设计使用MultiActionController心得今天总结一下在一个Spring的项目中使用MultiActionController的应用小
设计使用MultiActionController心得
今天总结一下在一个Spring的项目中使用MultiActionController的应用小结。
在项目开发过程中,面对一个需要对一个域对象进行查询明细,查询列表和查询该域对象的关联对象的案例,这里不包括对域对象进行更新的工作,这样如果采用实现Controller接口的话,每个实现类执行handleRequest返回ModelAndView来达到目的,很自然的将会产生3个实现类,这样项目的类文件数量增多,而且在以后维护工作中,虽然在一个用例中,仍然不得不从配置文件下手,一个一个根据映射找出对应的相应的Controller,这添加了维护的难度,所以这里我们很自然的采用了MultiActionController做实现。代码基本类似如下:
public class BookMultiController extends MultiActionController{//(1)public ModelAndView getBook(HttpServletRequest request,HttpServletResponse response) {ModelAndView mv = new ModelAndView("book/book_detail");Book book = bookManager.getBook(request.getParameter("bookId"));mv.addObject("book", book);return mv;}//(2)public ModelAndView getBookList(HttpServletRequest request,HttpServletResponse response) {ModelAndView mv = new ModelAndView("book/book_list");Book book = new Book();BeanUtils.populate(book, request.getParameterMap());List list = bookManager.getBooks(book);mv.addObject("bookList", list);return mv;}//(3)public ModelAndView getAuthorByBook(HttpServletRequest request,HttpServletResponse response) {ModelAndView mv = new ModelAndView("author/author_detail");Author author = authorManager.getAuthorByBookId(request.getParameter("bookId"));mv.addObject("author", author);return mv;}}
在Url配置映射方面,我们使用了org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver,所以上述代码
的访问是:
book.do?method=getBook&bookId=123;
book.do?method=getBookList&bookName=JavaDesign&price=100;
book.do?method=getAuthorByBook&bookId=123;
这样实现下来似乎很不错了。但是在开发过程中,这样情况还会出现很多,当我们碰到一个综合查询的时候,往往会有一颗树,点击树的每一个节点,通过传递一些参数,我们需要看到想要的结果显示在正确的页面。如果采用实现Controller的方法,那么每一个节点的点击将具体的对应到一个相应,通过处理返回ModelAndView。如果考虑这颗树的节点很多,那么是否应该随着节点的不断增加,controller也不断的增加,配置文件的不断增加呢?如果不这样,是否还是考虑使用MultiActionController呢?在这里我们的选择还是使用MultiActionController来实现,不过带来的后果就是有多少个节点,就意味着MultiActionController有多少个对应的方法执行来返回ModelAndView。在这里已经假设节点有很多的情况,那么就意味着这个Controller随着执行方法的增加,代码也会会很冗长。如果有30个节点的话,上面的方法将会有30个,代码一多,这时仔细看看,其实他们都很薄,也都很相似。这里薄和相似是他们
都没有像SimpleFormController那样需要绑定对象,对对象进行验证,保存和更新的动作,他们都是从用户请求那里拿到参数,然后返回给用户希望得到的东西,这就足够了。所以我们在这个基础上加了一些东西,让这些事情做起来更容易一些,在MultiActionController注入了一个IRequestHandler,由它来完成上述的需求,这样30个方法的查询,实现起来至多也就是2个。拿一开始的用例来举例,代码:
public ModelAndView query(HttpServletRequest request,HttpServletResponse response) {requestHandler.dynamicInvocate(request);Solution solution = requestHandler.getSolution();return new ModelAndView(solution.getPage(),solution.getModelName(),solution.getModel());}public ModelAndView queryBook(HttpServletRequest request,HttpServletResponse response) {Book book = new Book();BeanUtils.populate(book, request.getParameterMap());Map<String,Object> map = new HashMap<String,Object>();map.put("book", book);requestHandler.dynamicInvocate(request, map);Solution solution = requestHandler.getSolution();return new ModelAndView(solution.getPage(),solution.getModelName(),solution.getModel());}
对应的访问变成:
query.do?method=query&purpose=getBook&bookId=123;
query.do?method=queryBook&purpose=getBookList&bookName=JavaDesign&price=100;
query.do?method=query&purpose=getAuthorByBook&bookId=123;
这里IRequestHandler有两个版本的dynamicInvocate,第一个最为简单,就是从请求控制域获取参数然后执行调用,并最终返回一个Solution对象,这里面封装了模型和视图。而第二个版本,则可以实现更多的参数传递的调用。如一开始的代码清单(2)中,是没有办法直接从请求中获取Book对象的,这样构建一个Map传递给调用,也能成功的得到模型和视图。完成了这些,回过头来再次考虑刚才那个拥有30个节点的树,如果足够幸运,所有的查询都是从request,或者session中获取的参数,那么只有一个MultiActionController类文件,并且只有一个方法。值得一提的是这个requestHandler我们是使用方法注入的,也就是他始终是以原型的形式出现,不会造成一些负面的影响,在我们系统测试运行期间,他运作的很好,效果还算满意。
大家对这样的设计有何看法,欢迎大家讨论。<item name="getBook"><operation action="bookManager" method="getBook"><parameter match="request">bookId</parameter></operation><solution page="book/book_detail" modelName="book" /></item><item name="getBookList"><operation action="bookManager" method="getBooks"><parameter>bookName</parameter><parameter>price</parameter><parameter match="custom" type="com.test.model.Book">book</parameter></operation><solution page="book/book_list" modelName="bookList" /></item><item name="getAuthorByBook"><operation action="authorManager" method="getAuthorByBookId"><parameter match="request" type="java.lang.String" >bookId</parameter></operation><solution page="author/author_detail" modelName="author" /></item>
在类图中,XmlParser在容器启动的时候,首先将xml解析成一组<String,Item>静态的映射,然后等待请求的到来。在这一组映射中的每一个Item,都由
Operation和Solution合成,Solution封装了model,view。而Operation封装了执行业务逻辑方法的manager,参数,方法。RequestHelper则根据xml中定
义的东东从request,session或者自定义的map中获取方法执行的参数,并WebApplicationContextUtils获取项目上下文,然后通过getBean获取manager,
那么这样一切准备就绪,requestHandler就可以开始工作了。这样相对一开始给出的那段(1),(2),(3)的代码清单,就变成了这样三段xml片断,以后的增
量不再写实际代码,而是写一段xml代码片断替代,维护工作就是从这一个xml开始,这样我们觉得还是值得的。