Slide 1

Slide 1 text

从 Struts 迁移到 Spring MVC 以及为什么? LI Daobing 2010-03-11 Zhuhai, China

Slide 2

Slide 2 text

Overview ●Java 标准定义语言的变迁: 从 Interface 到 Annontation ●Spring MVC 带来了什么?

Slide 3

Slide 3 text

我们如何定义一个标准? 我们如何定义一个标准? ●定义一个接口, 比如 javax.sql.DataSource ●定义接口的各个函数, 比如 Connection getConnection() throws ... ●定义各个函数的文档, 比如执行前应满足什么?执行后应满足 什么? ●各个供应商根据标准去实现自己的类:com.mysql.jdbc.Driver 比如 javax.sql 这个包中就定义 14 个接口和 3 个类 (Event Object)

Slide 4

Slide 4 text

从接口定义的标准 ●Servlet: web 服务器 ●JDBC: 数据库连接 ●Java Mail API:邮件客户端 ●JMX: 管理协议 ●JMS: 消息协议 ●EJB 2.x: 企业级 Java Bean

Slide 5

Slide 5 text

JPA: 主流类型不再是 interface annotation: 65 enum: 9 exception: 8 interface: 4 class: 1 JPA: Java 持久化 API

Slide 6

Slide 6 text

如何用 Interface 来设计 Entity 如果用 interface 来设计 Entity, 结果会是如何呢? public interface Entity { String getTableName(); // 表名 void persist(DataSource dataSource); ... } public abstract class AbstractEntity implements Entity { public void persist(DataSoruce dataSource) {} } public class User extends AbstractEntity { @Override public String getTableName() {return "USERS";} ... }

Slide 7

Slide 7 text

如何用 Interface 来设计Validable 如果用 interface 来设计 Validable, 结果会是如何呢? public interface Validable { Set valid(); } public abstract class AbstractValidable implements Validable { public Set valid() {} } public class User extends AbstractValidable { ... }

Slide 8

Slide 8 text

一个真实的 User 类 ●比如一个 User 类, 他同时具有如下的属性 ○代表一个用户 ○可以持久化到数据库 ○可以校验完整性 ○可以Java序列化 ○可以JSON序列化 ○... class User extends AbstractEntity, AbstractValidable implements IUser, Serializable, JSONSerializable {} Java 太烂了, 连多重继承都不支持, 我准备转投 C++ 了!!!!!

Slide 9

Slide 9 text

现在 JPA 1.0 的方式 @Entity @Table(name="USERS") public class User { @Id @GeneratedValue private Long id; @Column(length=32, unique=true) @NotNull @Size(max=32) private String name; @OneToOne @Valid private UserConfig config; ... }

Slide 10

Slide 10 text

更多基于 Annotation 的标准 ●JPA: 持久化 API ●EJB 3: 企业级 Java Bean ●Java Validation API: 验证 API ●Servlet 2.5/3.0: web 服务器, 部分使用 Annotation ●Spring 2.5 / Spring MVC ●...

Slide 11

Slide 11 text

Annotation 的本质 ●他就是一个标签 ○可作用于: 类型, 类成员, 方法,参数,局部变量,包... ○可以用程序来访问: Class.getAnnotation, ... ● 管理类读入这些信息, 然后 1.提取元信息作为配置, 比如用 @Table 来定义对应的数据库 表, 2.使用代理类来封装原始类, 在构造函数, 析构函数, 方法调用 前后,异常发生时挂钩子 3....

Slide 12

Slide 12 text

Interface 实现方式的问题在哪儿? ●无法分离关注点 ●无法继承多个抽象基类 ●名字空间冲突 (比如各个 Interface 的方法名可能冲突) ●测试不友好, (EJB 2.x 是最好的例子) ●无法用 XML 覆盖配置

Slide 13

Slide 13 text

Annotation 的其他优点 ●关注点分离 ●依赖注入的支持 ●超强的灵活性 ●可覆盖的配置 ●你可以发明新的用途

Slide 14

Slide 14 text

Part 2: From Struts -> Spring MVC

Slide 15

Slide 15 text

Web 编程接口 CGI: 每次请求一个进程, 参数从环境变量传入, POST 数据从标准 输入传入, 返回数据走标准输出 SCGI,FCGI: 对 CGI 的简单包装, 一个进程可以负责多个请求, 参 数改从标准输入送入 ASP/PHP/JSP: 混合页面编程, 在页面布局中加入动态数据

Slide 16

Slide 16 text

Web 编程接口 ●Servlet: public abstract class HttpServlet ... { void doGet(HttpServletRequest, HttpServletResponse); } ●Struts 1.2 public class Action { ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response); }

Slide 17

Slide 17 text

Web 编程接口 ●Struts2 class ActionSupport { public String execute(); } ●Spring MVC @Controller @RequestMapping @RequestParam BindingResult

Slide 18

Slide 18 text

Struts 1 的典型流程 1.建立一个新类,继承 Action,实现 Execute 方法 2.修改 struts-config.xml, 加入 URL->Action 的映射 3.如果需要DynaActionForm,在 struts-config.xml 加入 form-bean 内容 4.修改 tiles-def.xml, 加入view层的配置 5.写对应的 JSP 文件

Slide 19

Slide 19 text

SaveSuggestionAction.java public class SaveSuggestionAction extends BaseAction { private SuggestionService suggestionService; public ActionForward execute(ActionMapping mapping, ActionForm _form, HttpServletRequest request, HttpServletResponse response) { DynaActionForm form = (DynaActionForm) _form; String content = form.getString("content"); Integer type = (Integer)form.get("type"); String fromUrl = form.getString("fromUrl"); Suggestion inst = new Suggestion(); inst.setContent(content); inst.setType(type); boolean result = getSuggestionService().addSuggestion(inst); request.setAttribute("result", result); request.setAttribute("fromUrl", fromUrl); return mapping.findForward(SUCCESS); } public void setSuggestionService(SuggestionService suggestionService) { this.suggestionService = suggestionService; } public SuggestionService getSuggestionService() { return suggestionService; }

Slide 20

Slide 20 text

struts-config.xml

Slide 21

Slide 21 text

tiles-defs.xml

Slide 22

Slide 22 text

SaveSuggestionAction.java public class SaveSuggestionAction extends BaseAction { private SuggestionService suggestionService; public ActionForward execute(ActionMapping mapping, ActionForm _form, HttpServletRequest request, HttpServletResponse response) { DynaActionForm form = (DynaActionForm) _form; String content = form.getString("content"); Integer type = (Integer)form.get("type"); String fromUrl = form.getString("fromUrl"); Suggestion inst = new Suggestion(); inst.setContent(content); inst.setType(type); boolean result = getSuggestionService().addSuggestion(inst); request.setAttribute("result", result); request.setAttribute("fromUrl", fromUrl); return mapping.findForward(SUCCESS); } public void setSuggestionService(SuggestionService suggestionService) { this.suggestionService = suggestionService; } public SuggestionService getSuggestionService() { return suggestionService; }

Slide 23

Slide 23 text

用 Spring MVC 来实现 @Controller public class FooController { @RequestMapping("/savesuggestion") public String saveSuggestion( @RequestParam("content") String content, @RequestParam(value="type", defaultValue=1) int type, @RequestParam(value="fromUrl", required=false) String fromUrl, Model model ) { Suggestion inst = new Suggestion(); inst.setContent(content); inst.setType(type); Suggestion suggestion = getSuggestionService().addSuggestion(inst); model.addAttribute(suggestion); return null; } }

Slide 24

Slide 24 text

用 Spring MVC 来实现 @Controller public class FooController { @RequestMapping("/savesuggestion") public String saveSuggestion( @RequestParam("content") String content, @RequestParam(value="type", defaultValue=1) int type, @RequestParam(value="fromUrl", required=false) String fromUrl, Model model ) { Suggestion inst = new Suggestion(); inst.setContent(content); inst.setType(type); Suggestion suggestion = getSuggestionService().addSuggestion(inst); model.addAttribute(suggestion); return null; } } 优点1: 信息集中

Slide 25

Slide 25 text

用 Spring MVC 来实现 @Controller public class FooController { @RequestMapping("/savesuggestion") public String saveSuggestion( @RequestParam("content") String content, @RequestParam(value="type", defaultValue=1) int type, @RequestParam(value="fromUrl", required=false) String fromUrl, Model model ) { Suggestion inst = new Suggestion(); inst.setContent(content); inst.setType(type); Suggestion suggestion = getSuggestionService().addSuggestion(inst); model.addAttribute(suggestion); return null; } } 优点2: 代码测试简单

Slide 26

Slide 26 text

Spring MVC: 自由定义参数和返回值 @Controller public class Foo { // 地址, 方法, 参数, 请求头 @RequestMapping(URL, METHODS, PARAMS, HEADERS) public ReturnType bar( InputType1 v1 InputType2 v2, ... ) { } }

Slide 27

Slide 27 text

支持的参数类型 ●ServletRequest/HttpServletRequest ●ServletResponse/HttpServletResponse ●HttpSession ●WebRequest ●Locale ●InputStream/OutputStream/Reader/Writer ●@PathVariable / @RequestParam / @RequestHeader / @RequestBody / @CookieValue ●Map/Model ●Java Bean ●BindingResult ●SessionStatus ...

Slide 28

Slide 28 text

支持的返回值类型 ●void: 函数体中处理完毕或者使用缺省的view ●String: view name ●Model / View / ModelAndView / Map ●@ResponseBody ●...

Slide 29

Slide 29 text

范例: 底层接口 @RequestMapping("/hello") public void hello(HttpServletRequest request, HttpServletResponse response) { String name = request.getParam("name"); response.setContentType("text/plain"); response.getWriter().println("hello, " + name); }

Slide 30

Slide 30 text

范例: 使用 JSP @RequestMapping("/hello") public String hello(HttpServletRequest request, Model model ) { String name = request.getParam("name"); model.addAttribution("name", name); return "hello"; }

Slide 31

Slide 31 text

范例: 使用自动参数绑定 @RequestMapping("/hello") public String hello( @RequestParam("name") String name, Model model ) { model.addAttribution("name", name); return "hello"; } 现在这个函数已经彻底跟 Servlet 没有关系了

Slide 32

Slide 32 text

范例: 缺省值 @RequestMapping({"/index", "/foo", "/bar"}) public void dummy() { } /index -> dummy() -> /WEB-INF/jsp/index.jsp /foo -> dummy() -> /WEB-INF/jsp/foo.jsp

Slide 33

Slide 33 text

范例: 重定向 @RequestMapping("/") public void index() { return "redirect:/index.do"; } / -> index() -> 302 -> /index.do

Slide 34

Slide 34 text

范例: 基本 CRUD: read @RequestMapping("/user/detail") public String detail( @RequestParam("id") long id, Model model ) { User user = genericDAO.find(User.class, id); if(user == null) { return "404"; } model.addAttribute(user); return null; }

Slide 35

Slide 35 text

范例: REST 风格路径 @RequestMapping("/user/{id}") public String detail( @PathVariable long id, Model model ) { User user = genericDAO.find(User.class, id); if(user == null) { return "404"; } model.addAttribute(user); return null; }

Slide 36

Slide 36 text

范例: 基本CRUD: create @RequestMapping(value="/add", method=GET) public void addForm(Model model) { model.addAttribute(new User()); } @RequestMapping(value="/add", method=POST) public String add( User user, BindingResult result, Model model ) { if(result.hasErrors()) { model.addAttribute(user); return null; } genericDAO.persist(user); return "redirect:/detail?id=" + user.getId(); }

Slide 37

Slide 37 text

范例: 公共路径 @Controller @RequestMapping("/user") public class UserController { @RequestMapping("/detail") public void detail() {...} @RequestMapping("/add") public void detail() {...} @RequestMapping("/update") public void detail() {...} }

Slide 38

Slide 38 text

范例: 通用 CRUD? @Controller @RequestMapping("/user") public class UserController extends BaseCRUDController{ public UserController() { super(User.class); } }

Slide 39

Slide 39 text

范例: 完整的例子 @RequestMapping(...) public String list( ListForm form, // Java Bean, 过滤, 排序 BindingResult result, // 绑定结果 @RequestParam(value="format", required=false) String format, // 返回格式, 缺省为 JSP 页面, 但可以是 json, xml, yaml, ini Model model ) { // 检验参数 Set<~> validationResults = validator.validate(form); if(result.hasErrors || !validationResults.isEmpty()) { logger.warn("..."); return "400"; } List<~> result = fooService.list(form); if(result.isEmpty()) return "404"; model.add("fooList", result); return format==null ? "foo/list" : format; }

Slide 40

Slide 40 text

Spring MVC: 自由定义参数和返回值 @Controller public class Foo { // 地址, 方法, 参数, 请求头 @RequestMapping(URL, METHODS, PARAMS, HEADERS) public ReturnType bar( InputType1 v1 InputType2 v2, ... ) { } }

Slide 41

Slide 41 text

支持的参数类型 ●ServletRequest/HttpServletRequest ●ServletResponse/HttpServletResponse ●HttpSession ●WebRequest ●Locale ●InputStream/OutputStream/Reader/Writer ●@PathVariable / @RequestParam / @RequestHeader / @RequestBody / @CookieValue ●Map/Model ●Java Bean ●BindingResult ●SessionStatus ...

Slide 42

Slide 42 text

支持的返回值类型 ●void: 函数体中处理完毕或者使用缺省的view ●String: view name ●Model / View / ModelAndView / Map ●@ResponseBody ●...

Slide 43

Slide 43 text

整体架构 Interceptor: API校验, IP过滤, 用户认证, 运行期开关, ... Controller: 封装业务逻辑 Exception Resolver: 集中处理错误情况 Viewer: 界面层 Interceptor Controller Exception Resolver Viewer HTTP Reuest

Slide 44

Slide 44 text

推荐阅读材料 ●Developing a Spring Framework MVC application step-by-step http://static.springsource.org/docs/Spring-MVC-step-by-step/ ●Spring Reference: Part V. The Web http://static.springsource.org/spring/docs/3.0.x/spring-framework- reference/html/spring-web.html ●Youtube/Slideshare http://is.gd/a7JAC http://is.gd/a7JO0 ●Spring WebFlow http://www.springsource.org/webflow ●Spring-WS http://static.springsource.org/spring-ws/sites/1.5/

Slide 45

Slide 45 text

Q Q&A?