Upgrade to Pro — share decks privately, control downloads, hide ads and more …

从 Struts 迁移到 Spring MVC,以及为什么?

从 Struts 迁移到 Spring MVC,以及为什么?

LI Daobing

March 11, 2010
Tweet

More Decks by LI Daobing

Other Decks in Programming

Transcript

  1. 我们如何定义一个标准? 我们如何定义一个标准? •定义一个接口, 比如 javax.sql.DataSource •定义接口的各个函数, 比如 Connection getConnection() throws

    ... •定义各个函数的文档, 比如执行前应满足什么?执行后应满足 什么? •各个供应商根据标准去实现自己的类:com.mysql.jdbc.Driver 比如 javax.sql 这个包中就定义 14 个接口和 3 个类 (Event Object)
  2. 如何用 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";} ... }
  3. 如何用 Interface 来设计Validable 如果用 interface 来设计 Validable, 结果会是如何呢? public interface

    Validable { Set<Error> valid(); } public abstract class AbstractValidable implements Validable { public Set<Error> valid() {} } public class User extends AbstractValidable { ... }
  4. 一个真实的 User 类 •比如一个 User 类, 他同时具有如下的属性 ◦代表一个用户 ◦可以持久化到数据库 ◦可以校验完整性

    ◦可以Java序列化 ◦可以JSON序列化 ◦... class User extends AbstractEntity, AbstractValidable implements IUser, Serializable, JSONSerializable {} Java 太烂了, 连多重继承都不支持, 我准备转投 C++ 了!!!!!
  5. 现在 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; ... }
  6. 更多基于 Annotation 的标准 •JPA: 持久化 API •EJB 3: 企业级 Java

    Bean •Java Validation API: 验证 API •Servlet 2.5/3.0: web 服务器, 部分使用 Annotation •Spring 2.5 / Spring MVC •...
  7. Annotation 的本质 •他就是一个标签 ◦可作用于: 类型, 类成员, 方法,参数,局部变量,包... ◦可以用程序来访问: Class.getAnnotation, ...

    • 管理类读入这些信息, 然后 1.提取元信息作为配置, 比如用 @Table 来定义对应的数据库 表, 2.使用代理类来封装原始类, 在构造函数, 析构函数, 方法调用 前后,异常发生时挂钩子 3....
  8. Web 编程接口 CGI: 每次请求一个进程, 参数从环境变量传入, POST 数据从标准 输入传入, 返回数据走标准输出 SCGI,FCGI:

    对 CGI 的简单包装, 一个进程可以负责多个请求, 参 数改从标准输入送入 ASP/PHP/JSP: 混合页面编程, 在页面布局中加入动态数据
  9. 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); }
  10. Web 编程接口 •Struts2 class ActionSupport { public String execute(); }

    •Spring MVC @Controller @RequestMapping @RequestParam BindingResult
  11. 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 文件
  12. 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; }
  13. struts-config.xml <struts-config> <form-beans > <form-bean name="suggestionForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="content" type="java.lang.String"/>

    <form-property name="type" type="java.lang.Integer"/> <form-property name="fromUrl" type="java.lang.String"/> </form-bean> </form-beans> <action-mappings> <action path="/savesuggestion" name="suggestionForm"> <forward name="success" path="com.kingsoft.savesuggestion" /> </action> </action-mappings> </struts-config>
  14. 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; }
  15. 用 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; } }
  16. 用 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: 信息集中
  17. 用 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: 代码测试简单
  18. Spring MVC: 自由定义参数和返回值 @Controller public class Foo { // 地址,

    方法, 参数, 请求头 @RequestMapping(URL, METHODS, PARAMS, HEADERS) public ReturnType bar( InputType1 v1 InputType2 v2, ... ) { } }
  19. 范例: 底层接口 @RequestMapping("/hello") public void hello(HttpServletRequest request, HttpServletResponse response) {

    String name = request.getParam("name"); response.setContentType("text/plain"); response.getWriter().println("hello, " + name); }
  20. 范例: 使用 JSP @RequestMapping("/hello") public String hello(HttpServletRequest request, Model model

    ) { String name = request.getParam("name"); model.addAttribution("name", name); return "hello"; }
  21. 范例: 使用自动参数绑定 @RequestMapping("/hello") public String hello( @RequestParam("name") String name, Model

    model ) { model.addAttribution("name", name); return "hello"; } 现在这个函数已经彻底跟 Servlet 没有关系了
  22. 范例: 缺省值 @RequestMapping({"/index", "/foo", "/bar"}) public void dummy() { }

    /index -> dummy() -> /WEB-INF/jsp/index.jsp /foo -> dummy() -> /WEB-INF/jsp/foo.jsp
  23. 范例: 基本 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; }
  24. 范例: 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; }
  25. 范例: 基本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(); }
  26. 范例: 公共路径 @Controller @RequestMapping("/user") public class UserController { @RequestMapping("/detail") public

    void detail() {...} @RequestMapping("/add") public void detail() {...} @RequestMapping("/update") public void detail() {...} }
  27. 范例: 完整的例子 @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; }
  28. Spring MVC: 自由定义参数和返回值 @Controller public class Foo { // 地址,

    方法, 参数, 请求头 @RequestMapping(URL, METHODS, PARAMS, HEADERS) public ReturnType bar( InputType1 v1 InputType2 v2, ... ) { } }
  29. 整体架构 Interceptor: API校验, IP过滤, 用户认证, 运行期开关, ... Controller: 封装业务逻辑 Exception

    Resolver: 集中处理错误情况 Viewer: 界面层 Interceptor Controller Exception Resolver Viewer HTTP Reuest
  30. 推荐阅读材料 •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/