View
10.288
Download
4
Category
Preview:
DESCRIPTION
2012 Java Developer Day
Citation preview
葉政達Cheng Ta Yeh
12年7月19日星期四
Integrate Spring MVC with RequireJS & Backbone.js
& Spring Data JPA
12年7月19日星期四
Who Am I• 葉政達 Cheng-Ta Yeh
• Co-founder of Humus Technology (優沃科技)
• Founder of GOGOSamba (Android app & Calorie Management Service)
• Speaker of TWJUG 2012/5
• Speaker of Google Developer Day 2008
• Speaker of Java One Tokyo 2005
• 12 years Java related experiences
12年7月19日星期四
Once upon a time...
12年7月19日星期四
The Dream...
12年7月19日星期四
Start lean, get bigger later12年7月19日星期四
What Am I Going To Talk About?
Building a Scalable Single-Page app from front-end to backend.
Overview
My Lessons Learned12年7月19日星期四
Outlines
• Front-end development
• Integrate Backbone with Spring MVC
• Diving into Spring Data JPA
12年7月19日星期四
Front-end development12年7月19日星期四
Problems ...
• Few conventions and standards
12年7月19日星期四
• jQuery is cool, it has plenty of selectors and callbacks, but ...
http://mysistersjar.wordpress.com/2008/04/02/wanted-moving-boxes/
We have to tie data to the DOM heavily
We have to deal with callbacks’ callbacks’...,
to manipulate DOM, animations, events......
Problems ...
12年7月19日星期四
Problems ...• Between server-side and client-side, we
need efficient tools.
12年7月19日星期四
Backbone.js12年7月19日星期四
Who Use Backbone.jsBasecamp Mobile
12年7月19日星期四
Backbone Components
DataSource
DOMUI
Router View Model / Collection
Template
EventHistory
DOM events
Model events
Render
syncFragmentrouting
Use
TalksHash tag
Dispatching routersMorninghashchange
12年7月19日星期四
Backbone View
• Owns a DOM element
• Talks to its model or collection
• Observe model events (change,...)
• Handle user’s inputs (DOM events)
12年7月19日星期四
Backbone Model
• Data storage and business logic
• Changes on the attributes will fire `change` event
• Talks to server uses RESTful JSON
12年7月19日星期四
My Real Case
12年7月19日星期四
My Real Case
12年7月19日星期四
Backbone Sample Code
ItemView
ItemDetailView
Use
window.ItemModel = Backbone.Model.extend();
ItemModel
12年7月19日星期四
Backbone Sample Code
ItemViewwindow.ItemDetailView = Backbone.View.extend({
events: {
"click #item_input": "doEdit"
},
initialize: function() { this.model.bind('change', this.setTitle, this);},setTitle: function() { $(this.el).html(this.model.get('name'));},
//....
//....
});
window.ItemModel = Backbone.Model.extend();
ItemModel
window.itemModel.save( {name: this.$('#input').val()});
ItemDetailView
window.ItemView = Backbone.View.extend({initialize: function() { this.model.bind('change', this.setItemName, this);},setItemName: function() { $(this.el).html(this.model.get('name'));},
});
change name change event
12年7月19日星期四
That’s It?
12年7月19日星期四
How to build loose coupling scalable applications?
12年7月19日星期四
Apply “Module Pattern”, but ...
var testModule = (function () {
var _counter = 0; //private variable
function privateMethod(){ //..... }
return {
incrementCounter: function () { return __counter++; },
resetCounter: function () { console.log( "counter value prior to reset: " + _counter ); _counter = 0; } };
})();
12年7月19日星期四
Real life application
• Lots of Screens• Lots of Modules
• Complex dependency
12年7月19日星期四
RequireJS12年7月19日星期四
Standards for Modules
• CommonJS Modules 1.1.1
• Targeted at server-side environments
• AMD (Asynchronous Module Definition)
• A format for writing modular javascript in the browser
CommonJS
12年7月19日星期四
RequirJS AMD Loader
Other AMD loaders:• curl• lsjs• Dojo 1.7 and MooTools 2.0 each have their own
12年7月19日星期四
AMD APIs
• Facilitating module definition • define (module_id?, dependencies?, factory);
• Handling dependency async loading • require (dependencies?, callback);
12年7月19日星期四
Defining Backbone’s Classes into Modules
define(function () { return new Backbone.Model.extend({ // ... });});
define([../models/itemModel],function (itemModel) { return new Backbone.View.extend({ model: itemModel; render: function(){ this.model.fetch(); //... }, // ... });});
./models/itemModel.js
./views/itemView.js
12年7月19日星期四
Your App’s Entry Pointrequire.config({
// .....})
require(['jquery','app'], function ($,app) { app.initial();});
define([../views/itemView],function (itemView) {
return {initial: function(){
// Do initialization.....
// Render first App’s screen.
itemView.render();
},
};});
./main.js
./app.js
1. Set up requireJS configuration.2. Start to run your app.
1. As a top level container. 2. Initialize your app.4. Render your first screen.
./views/itemView.js
LoadLoad
Execute
12年7月19日星期四
<!DOCTYPE html><html> <head> <title>jQuery+RequireJS Sample Page</title> <script data-main="scripts/main" src="scripts/require-jquery.js"></script> </head> <body> <h1>Single Page Application Sample Page</h1>
<div id=”app_body”> Your DOM elements will be inserted dynamically. </div> </body></html>
Set up your HTML Page
12年7月19日星期四
Using requireJS Optimizer
• Optimizing all the CSS and JS files in your project
> r.js -o app.build.js
({ appDir: "../", baseUrl: "scripts", dir: "../../appdirectory-build", modules: [ { name: "main" } ]})
app.build.js
12年7月19日星期四
Tips & Lessons Learned
• Prevent Memory Leaks• All models, collections, views implement destructors.
• Create an abstraction layer for those destructors.
Backbone.View.prototype.close = function () { console.log("Closing a View...”);
if (this.beforeClose) { this.beforeClose(); } this.remove(); this.unbind(); };
12年7月19日星期四
Tips & Lessons Learned
• Package Pattern• File structure
• src/ui/widgets.js• src/ui/widgets/ooxx.js
define(['./widgets/selectors', './widgets/slider', './widgets/button'], function(selectors, slider, button) { return { ToolSelector: selectors.ToolSelector, OptionSelector: selectors.OptionSelector, Slider: slider.Slider, Button: button.Button };});
src/ui/widgets.js
< Just like Java’s package
12年7月19日星期四
Integrates Backbone.js with Spring MVC
http://s.ctyeh.me/LGW3ug
12年7月19日星期四
Backbone Model & RESTful JSON Inteface
* POST or PUT base on the id value of Model.
itemModel.save();
itemModel.fetch();
itemModel.save();
itemModel.clear();
C
R
U
D
• POST /items
• GET /items[/{id}]
• PUT /items/{id}
• DELETE /items/{id}
* Global ajax error handler is useful
12年7月19日星期四
Define RESTful Interface• Nouns: URI, addressed resources
• /sites
• /sites/{id}
• /sites/{id}/items
• /sites/{id}/items/{todoId}.
The REST Triangle
Find your resources BEFORE designing Backbone Models and Spring MVC Controller methods.
12年7月19日星期四
Spring MVC as REST Server
12年7月19日星期四
Receiving Values in the URL Path• The RESTful way
• @PathVariable in Controller methods
@RequestMapping(value="/sites/{siteId}/item/{itemId}",method= {RequestMethod.PUT})public @ResponseBody UIJentoItem updateModule(@PathVariable long siteId,@PathVariable Long itemId,@Valid @RequestBody UIJentoItem item) { UIJentoItem uiObject = new UIJentoItem(); //......
return uiObject;}
12年7月19日星期四
Sending HTTP Response Codes to Clients
• @ResponseStatus
• @ExecptionHandler.
public class AbstractController {
@ExceptionHandler(UserNotSigninException.class) @ResponseStatus(UNAUTHORIZED) public @ResponseBody UIMessage handleUserNotSigninException(UserNotSigninException e, HttpServletResponse response) { return UIMessage.fail("err.data.usernotsignin"); } @ExceptionHandler(DataNotFoundException.class) @ResponseStatus(NOT_FOUND) public @ResponseBody UIMessage handleDataNotFoundException(DataNotFoundException e, HttpServletResponse response) { return UIMessage.fail("err.data.notfound"); } @ExceptionHandler(Exception.class) @ResponseStatus(INTERNAL_SERVER_ERROR) public @ResponseBody UIMessage handleException(Exception e, HttpServletResponse response) { return UIMessage.fail("err.internal"); }
}
AbstractController
ControllerA
ControllerB
ControllerC
extends
// HTTP 401
// HTTP 404
// HTTP 505
12年7月19日星期四
Rarely Need Custom Parsers
• Automatically registers several converters.
12年7月19日星期四
Controller Methods’Parameters and Return Values
The bullets between
Backbone Model & Spring MVC
12年7月19日星期四
Parameters and Return Values
• Domain Entity
@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<Item> getItems(@PathVariable long id) {
//....
return itemList; }
@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST}) @ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id,@RequestBody Item item) {
//....
}
In
Out
Controller
Controller
12年7月19日星期四
Parameters and Return Values
• DTO (Data Transfer Object)
@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<DTOItem> getItems(@PathVariable long id) {
//....
return itemList; } @RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST})
@ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id,@RequestBody DTOItem item) {
//....
}In
Out
Controller
Controller
12年7月19日星期四
Parameters and Return Values
• JSONObject
import net.sf.json.JSONObject;
@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<JSONObject> getItems(@PathVariable long id) {
//....
return itemList; }
@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST}) @ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id, @RequestBody JSONObject itemJSON) {
//....
}In
Out
Controller
Controller
12年7月19日星期四
Parameters and Return Values
• Domain Entity
• Pros: No extra effort for creation and maintenance & readable
• Cons: Might waste extra bandwidth for transferring
• DTO
• Pros: readable
• Cons: Extra efforts for maintenance, transferring from domain object to DTO.
• JSONObject
• Pros: No extra effort for creation and maintenance
• Cons: Not readable, Unit testing is mandatory, transferring from domain entity to JSONObject!
12年7月19日星期四
Unit Test for Spring MVC Controller
• mockito - Mocking framework• http://code.google.com/p/mockito/
• Spring MVC Test Support• https://github.com/SpringSource/spring-test-mvc
12年7月19日星期四
Unit Test for Spring MVC Controller
public class ItemControllerTest {
private static MockMvc mockMvc; private UserSerivce userService; private ItemSerivce itemService;
@Before public void setUp() { userService = mock(UserService.class); itemService = mock(ItemService.class);
mockMvc = standaloneSetup(new ItemController()).build();
// userService.getCurrentUser() will be called in ItemController for URI "/sites/{id}/items" when(userService.getCurrentUser()).thenReturn(new User()); Item item = new Item();
item.setName("This is item name"); when(itemService.getItem(any(Long.class))).thenReturn(item);
}
@Test public void getItemsBySiteId() throws Exception { mockMvc.perform(get("/sites/1/items")) .andExpect(status().isOk()) .andExpect(content().type("application/json;charset=UTF-8")) .andExpect(jsonPath("$.[0].id").value(1)) .andExpect(jsonPath("$.[0].itemName").value("This is item name")) ;
Your URI
tell mocks when some calls happened return something
Create a mock service
Setup mockMVC for target Controller
ItemController
ItemService UserService
useuse
12年7月19日星期四
Diving into Spring Data JPA
12年7月19日星期四
Layer Architecture
Backbone.js requireJS
Spring MVC
Spring Data JPA
Domain Model
HTML
12年7月19日星期四
Between Spring MVC and Spring Data JPA
• Data conversion
• Design concept
12年7月19日星期四
Between Spring MVC and Spring Data JPA
• Data conversion• Using Factory Method in Domain entity or separate Factory classes.
DTO JSONObject
Domain Object
Spring MVC
Spring Data JPA
12年7月19日星期四
Between Spring MVC and Spring Data JPA
• Data conversion
• Using Factory Method in Domain entity or separate Factory classes.
DTO JSONObject
Domain Object
@Entitypublic class Item implements Serializable { private String name;
//.....
public JSONObject toJSON(){ JSONObject object = new JSONObject();
//Converting Item to JSONObject. object.put("name", this.name);
return object;}
public static Item fromJSON(JSONObject json){
//Converting JSONObject to Item.
return item; }}
12年7月19日星期四
Between Spring MVC and Spring Data JPA
• Design concept• Domain driven design in Domain Layer
• Entity
• Service
• Value Object
• Aggregate
• Repository Spring Data borrows this concept.
12年7月19日星期四
Spring Data Overview• Abstracts away basic data management concepts
• Support for RDB, NoSQL: Graph, Key-Value and Map-Reduce types.
• Current implementations• JPA/JDBC
• Hadoop
• GemFire
• REST
• Redis/Riak
• MongoDB
• Neo4j
• Blob (AWS S3, Rackspace, Azure,...)
• Plan for HBase and Cassandra
12年7月19日星期四
Let’s Begin With Plain JPA
12年7月19日星期四
@Entitypublic class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;
@ManyToOne private Site site;
// … methods omitted}
@Entitypublic class Site {
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;
// … methods omitted}
The Domain Entities
Site Item1*
12年7月19日星期四
@Repositorypublic class TodoDAO {"
@PersistenceContext private EntityManager em; @Override @Transactional public Item save(Item item) { if (item.getId() == null) { em.persist(item); return item; } else { return em.merge(item); } } @Override public List<Item> findBySiteId(Long siteId) { TypedQuery query = em.createQuery("select i from Item a where i.site.id = ?1", Item.class); query.setParameter(1, siteId); return query.getResultList(); }
}
The DAO
12年7月19日星期四
The Domain Service@Service@Transactional(readOnly = true)class ItemListServiceImpl implements ItemListService { @Autowired private ItemDAO itemDAO; @Override @Transactional public Item save(Item item) { return itemDAO.save(item); }
@Override public List<Item> findItems(Long siteId) { return itemDAO.findBySiteId(siteId); }
}
12年7月19日星期四
Refactoring to Spring Data JPA
12年7月19日星期四
public interface ItemRepository extends JpaRepository<Item, Long> { List<Item> findBySite(Site site);}
The Repository (DAO)
12年7月19日星期四
The Service@Service@Transactional(readOnly = true)class ItemListServiceImpl implements ItemListService { @Autowired private ItemRepository repository;
@Override @Transactional public Item save(Item item) { return repository.save(item); }
@Override public List<Item> findItems(Site site) { return repository.findBySite(site); } }
12年7月19日星期四
Strategy: CREATE• Split your query methods by prefix and
property names.
12年7月19日星期四
Strategy: USE_DECLARED_QUERY• @Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress);}
From Spring Data JPA 1.1.0, native SQL is allowed to be executed. Just set nativeQuery flag to true.
But, not support pagination and dynamic sort
12年7月19日星期四
Strategy: USE_DECLARED_QUERY• @Modify
@Modifying@Query("update User u set u.firstname = ?1 where u.lastname = ?2")int setFixedFirstnameFor(String firstname, String lastname);
@Modifying@Query("update User u set u.firstname = :firstName where u.lastname = :lastName")int setFixedFirstnameFor(@Param("firstName") String firstname, @Param("lastName") String lastname);
12年7月19日星期四
Configurations in Spring 3.1<!-- Activate Spring Data JPA repository support --> <jpa:repositories base-package="com.humus.domain.repo" />
<!-- Declare a JPA entityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props>
</property> <property name="packagesToScan"> <list> <value>com.humus.domain.entity</value> </list> </property>
</bean>
No persistence.xml any more~
12年7月19日星期四
Put It All Together
Backbone.js
Spring MVC
Spring Data JPA
requireJS
12年7月19日星期四
Recommended