- 浏览: 184248 次
- 性别:
- 来自: 深圳
最新评论
-
mengfei86:
你们讨论的时候我刚上大学,。。。。、、现在都过去好多年了,。 ...
J2EE项目异常处理 -
di1984HIT:
文章不错,学习了
Ibatis读写CLOB数据 -
wulixiaodao:
main{
metodA();
}
详解spring事务属性 -
wulixiaodao:
Main{
Connection con=null;
...
详解spring事务属性 -
tao_gun:
感谢,有点懂了
详解spring事务属性
Spring架构设计-增强MultiActionController
在使用Spring提供的控制器时,AbstractController和SimpleFormController是应用得最多的。AbstractController是最基本的Controller,可以给予用户最大的灵活性。SimpleFormController则用于典型的表单编辑和提交。在一个需要增,删,改,查的需求中,增加和修改扩展SimpleFormController完成,删除和查询则扩展AbstractController完成。
但是像上面那样完成某一业务对象的增,删,改,查,都属于一类相关的业务。把一类相关的操作分布到不同的类去完成,违返“高内聚”的设计原则。这样四个业务操作需要四个类来完成,造成太多的类文件,难以维护和配置。
所以Spring借鉴Struts的DispatchAction提供了类似功能的MultiActionController。可以实现不同的请求路径对应MultiActionController中的不同方法,这样就可以把相关的操作都在一个类的相关方法中完成。这样使得这个类具有“高内聚”,也利于系统的维护,还避免了重复代码。增加和修改操作的数据验证逻辑是很相似的,使用MultiActionController后就可以让增加和修改操作共用一段数据验证逻辑代码。
1. 使用MultiActionController
MultiActionController会使不同的请求映射为不同方法,这里是一个实现用户信息增删改查的例子:
1.1 SampleMultiMethodController实现
1.2 web-context配置
2. MultiActionController的缺点
MultiActionController把相关的业务方法集中在一个类中进行处理,减少控制类的数量。方便于系统的维护,可以重用相关的逻辑代码,提高代码的重用,同时也减少bean的配置。有太多的bean配置可以说是Spring 的一个暇疵。Spring提供IOC,让我们灵活的控制bean的依赖。同时我们需要去维护太多的bean配置,Spring项目中很大程度上都在烂用xml 配置文件,这很不利于团队开发和系统的后期维护。MultiActionController也不例外。
1. multiActionController的配置相对复杂。MultiActionController需要注入一个MethodNameResolver对象,再通过MethodNameResolver的mappings属性来提供请求与方法之间的映射。这样的配置是复杂的和难以理解的。使用Spring框架的确很灵活,但是有时这种过分的灵活反而增加了系统的复杂度。
2. multiActionController配置涉及的bean过多。除了自身的bean定义外,还需要把所有的映射配置到一个UrlHandlerMapping中去。这样除了维护multiActionController的自身的bean定义外,还需要维护UrlHandlerMapping的定义。
笔者十分反对这种具有连带性的配置,一个bean的属性改变会造成对别一个bean属性的改变。这样增加了系统的复杂度,和维护成本。所以必须提供一种默认的实现,让bean之间的依赖,不要造成bean属性之间的依赖。MultiActionController在这方面表示得十分不好。
3. 数据绑定支持不好。SimpleFormController专门用来支持编辑和表单提效的,它支持数据绑定,在这方面做得很好。可以把jsp页面的字段值绑定为某一对象(Command)。可以自定义command的名称。虽然MultiActionController也支持数据绑定,但是它并不支持自定义command的名称。它默认的comamnd名称为”command”。这也是不便于维护的,对象应该有一个代表自身含义的名字。如果所有页面的绑定对象都以”command”作为名字,那将难以理解。MultiActionController支持数据绑定的方法参见上面例子的saveUser方法。
3. 理想的MultiActionController构想
一个理想的MultActionController应该配置简单明了,并且无需要在多个地方进行配置。 应该支持对绑定对象自定义名称。
上面是一个更让人能够理解的配置。
1.把请求与具体方法之间的映射作为MultiActionController自身的一个属性“urlMethodmappings”。
2.通过一个commandName属性,可以让用户自由决定绑定对象的名称。
3.简化UrlHandlerMapping的关联配置。对MutilActionController的bean配置进行改动时,无再需要去关心 SimpleUrlHandlerMapping的bean配置
4. 增强的MultiActionController实现
上面提到理想MultiActionController的构想,有三点需要实现。现在来讨论实现它们。
1. 把请求与具体方法之间的映射作为MultActionController自身的一个属性。也就是说MultiActionController提供一个“urlMethodMapping”的属性来保存请求路径与对应方法之间的映射关系。
我们知道MultiActionController有一个methodNameResolver的属性,而请求路径与方法之间的对应映射关系是由一个MethodNameResolver 的bean来保存的。我们一般可以配置一个PropertiesMethodNameResolver来作默认实现。把请求路径与方法之间的映射关系保存在PropertiesMethodNameResolver中的“mapping”属性中。
我们可以在MultiActionController中定义一个PropertiesMethodNameResolver类型的成员变量“propertiesMethodNameResoler”。和定义一个Properties类型的成员变量“urlMethodmappings”
在MultiActionController的bean进行配置的时候把urlMethodmappings的值作为propertiesMethodNameResoler的mapping的值。然后再调用MultiActionController的setMethodNameResolver()方法,把propertiesMethodNameResoler设置为MultiActionController的methodNameResolver的属性值。要做到这一些还应该实现InitializingBean接口
2. 通过一个commandName属性,可以让用户自由决定绑定对象的名称
MultiActionController的
如果没有设置commandName属性,默认值为“command”,通过setCommandName方法就可以自由的去决定comamnd对象的名称了。
这样我们基本上已经简化了MultiActionController的自身的配置,但是它仍然需要与一个UrlHandlerMapping联系,也就是增加或删除一个MutilActionController的bean。都需要修改某一个UrlHandlerMapping的bean的配置。这也就是我们上面说的理想MultiActionController的第3点。
3. 简化UrlHandlerMapping的关联配置
UrlHandlerMapping是请求路径与Controller之间的对应映射。UrlHandlerMapping有一个最简单的实现就是SimpleUrlHandlerMapping.
“/welcom.do”这个请求路径对应bean 名称为oneController的Controller实例。所以应将MultiActionController中的所有请求路径都保存在一个UrlHandlerMapping的mappings属性中,作为key值,把MutilActionController 的bean实例作为value作。
DispatcherServlet在初始化时会在Context中查找所有类型为HandlerMapping的bean,将所有的HandlerMapping实例保存在handlerMappings属性List中。当一个请求进入时,DispatcherServlet会依次查找handlerMappings List中的HandlerMapping实例是否匹配当前请求路径,如果匹配当前请求路径,就获取请求路径对应的Controller实例;如果Controller实例是MultiActionController类型时,MultiActionController就会会根据当前请求路径去调用MultiActionController相应的方法。这就是一个MultiActionController的执行过程。
根据这样的原理,能够有一个类似SimpleUrlHandlerMapping的HandlerMapping能够在初始化的时候自动在当前WebApplicationContext中查找所有MultiActionController类型的bean。然后依次生成一个以MultiActionController的urlMethodmappings Map的所有key值作为key值,以MultiActionController实例为value值的一个Map,并把这个Map所有元素都添加到SimpleUrlHandlerMapping的mappings属性中。这样就达到了我们自动化配置的效果。
我们把这个HandlerMapping 称为MultiMethodControllerUrlHandlerMapping,下面我们讲怎么具体去实现它。
5.实现MultiMethodControllerUrlHandlerMapping
我们在上面讨论过了怎么实现MultiMethodControllerUrlHandlerMapping,要实现为具体的代码,我们可以通过扩展org.springframework.web.servlet.handler.AbstractUrlHandlerMapping。AbstractUrlHandlerMapping扩展了org.springframework.web.context.support.WebApplicationObjectSupport。WebApplicationObjectSupport可以获得当前WebApplicationContext。
1. 重写initApplicationContext方法,在context中查找所有MultiActionController类型的bean,把MultiActionController的urlMethodmappings属性的key值为key值,MultiActionController实例为键值的键值对添加到一个urlMap中。
然后在initApplicationContext方法中调用registerUrlMap方法
3. 使用MultiMethodControllerUrlHandlerMapping
使用MultiMethodControllerUrlHandlerMapping,只需要在ApplicationContext中,定义成一个bean就可以了。
注意:在一个context如果定义多个HandlerMapping,需要为每一个HandlerMapping指定order属性。
你只需要在在context 中定义MultiMethodControllerUrlHandlerMapping,在使用MultiActionController时,只需要配置urlMethodmappings属性就可以了。当删除或增加一个MultiActionController的bean时,无需要连带配置任何HandlerMapping. 简化了bean的配置。使得MultiActionControler的bean配置只关心自身的属性配置,而无需要去关心看起来与自身无关的HandlerMapping的配置。使得整个配置更合乎人们正常的思维逻辑,减少配置的复杂性。
6.设计讨论
在这里我们将对以Spring为基础进行项目架构设计进行一些讨论.
1. MultiActionController还是AbstractController与SimpleFormController组合
在使用Spring MVC时,SimpleFormController用于表单编辑和提交;而复杂的功能则通过扩展AbstractcController完成。这就是所谓的AbstractController与SimpleFormController组合。以AbstractController与SimpleFormController的结合来完成表示层逻辑。
Spring MVC虽然也提供了MultiActionController,但是它似乎天生就有点蹩脚。对数据绑定支持不是很好,在用于表单编辑和提交时不像SimpleFormController那么强大。其实通过对MultiActionController的扩展和增强,完成可以实现与SimpleFormController同样的功能,比如数据校验等,并且还比SimpleFormController具有更多的灵活性。
在OO技术中,有一个重要的原则:低耦合,高内聚;我们应该按职责来设计对象。按对象应该具有的职责来给对象设计相应的方法。如果把一个对象本来该具有的职责分散到不同类中去完成,那么这个些类是违反“低耦合,高内聚”原则的。一个类不是高内聚的,就不便于维护和扩展,造成大量重复代码的产生。同样把一组相关的功能分散到多个Controller去实现,是违反“低耦合,高内聚”原则的,可以就会产生大量的重复代码。比如参数获取,数据校验等。如果使用MultiActionController,把相关的功能由一个Controller的不同方法实现,集中在一个Controller类中处理,就使得这个Controller类是具有“高内聚”性的。所以,在项目应用中,相关的功能应该由一个MultiActionController的不同方法去实现。这样就便于代码的维护,提高代码的重用,减少bean配置,降低项目的复杂度。
2. 灵活性与简易化
Spring作为一个轻量级的j2ee基础框架,使用是非常灵活的。特别是可以通过xml文件来灵活的配置对象之间的依赖。但是,以Spring作为框架的项目,bean的配置太多,反而增加了项目的复杂度。在开发过程中,应该把主要精力花在关注业务逻辑的实现上面,而不应该花在配置上面。灵活度越大也就导致了复杂度越高。当然,Spring是一个通用框架,应该具有这样的灵活性,才便于扩展,以满足各种应用需要。
在具体的项目中,就应该使架构使用起来简单,易用。特别是以Spring作为基础的架构中,应该通过设计降低配置的复杂度,尽可能的减少bean的配置和使配置简单化。
一个bean属性发生变化,不应该产生连带关系,使得其它bean也需要修改配置。这是不利于团队开发的。在团队开发中,开发人员应该只关心业务对象的bean配置。
像HandlerMapping这些属于框架基础bean配置一旦定义后就应该具有稳定性。不要因为业务对象bean的改变而需要开发人员随之进行修改。
3. 增强的MultiActionController与MultiMethodControllerUrlHandlerMapping
通过扩展MultiActionController,使得它得到增强,能够实现SimpleFormController的功能,同时使得配置更加直观和简易。
只需要定义一个MultiMethodControllerUrlHandlerMapping,使得开发人员只需要关注相关MultiActionController的配置,而无需去再关注和修改HandlerMapping的配置。
通过MultiMethodControllerUrlHandlerMapping 与增强的MultiActionController结合,更易于运用OO技术设计高内聚的Controller类,减化bean的配置。让开发人员把精力花在系统的业务逻辑的实现上,而不会去过度关心bean的配置。
7.完整的代码实现
这里把增强的MultiActionController称为MultiMethodController
1. MultiMethodController.java
注:原创文章,曾发表在it168
我以前也这么想,但是现在觉得有点得不偿失
更pragmatic的做法还是ParameterMethodNameResolver
在使用Spring提供的控制器时,AbstractController和SimpleFormController是应用得最多的。AbstractController是最基本的Controller,可以给予用户最大的灵活性。SimpleFormController则用于典型的表单编辑和提交。在一个需要增,删,改,查的需求中,增加和修改扩展SimpleFormController完成,删除和查询则扩展AbstractController完成。
但是像上面那样完成某一业务对象的增,删,改,查,都属于一类相关的业务。把一类相关的操作分布到不同的类去完成,违返“高内聚”的设计原则。这样四个业务操作需要四个类来完成,造成太多的类文件,难以维护和配置。
所以Spring借鉴Struts的DispatchAction提供了类似功能的MultiActionController。可以实现不同的请求路径对应MultiActionController中的不同方法,这样就可以把相关的操作都在一个类的相关方法中完成。这样使得这个类具有“高内聚”,也利于系统的维护,还避免了重复代码。增加和修改操作的数据验证逻辑是很相似的,使用MultiActionController后就可以让增加和修改操作共用一段数据验证逻辑代码。
1. 使用MultiActionController
MultiActionController会使不同的请求映射为不同方法,这里是一个实现用户信息增删改查的例子:
1.1 SampleMultiMethodController实现
public class SampleMultiMethodController extends MultiActionController{ // 用户信息列表view private static final String userInfoListView = "ehld.sample.getuserinfolist"; //用户信息编辑view private static final String userFormView = "ehld.sample.userForm"; //提交成功后显示的view private static final String userSuccessView ="redirect:ehld.sample.getuserinfolist.do"; // 用户信息列表key值 private static final String userInfoListKey = "userInfoList"; // userid private final String userIdParam = "id"; // 定义业务对象 private SampleAction sampleAction; public SampleAction getSampleAction() { return sampleAction; } public void setSampleAction(SampleAction sampleAction) { this.sampleAction = sampleAction; } /** * 功能:获得所有的用户信息<br> */ public ModelAndView listUser(HttpServletRequest request, HttpServletResponse response) throws Exception { List userInfoList = this.sampleAction.getUserInfoList(); ModelAndView mav = new ModelAndView(userInfoListView); mav.addObject(this.userInfoListKey,userInfoList); return mav; } /** * 功能:编辑用户信息<br> */ public ModelAndView edtiUser(HttpServletRequest request, HttpServletResponse response) throws Exception { String uid = RequestUtils.getStringParameter(request, userIdParam); UserInfoDTO userInfo = null; if (!"".equals(uid)) { userInfo = this.sampleAction.getUserInfo(uid); } if (userInfo == null) { userInfo = new UserInfoDTO(); } ModelAndView mav = new ModelAndView(this.userFormView, this .getCommandName(null), userInfo); return mav; } /** * 功能:保存修改或新增的用户信息<br> *检查从页面bind的对象,如果userId或userName为空则返回原来的form页面;否则进行保存用户信息操作,返回 *成功页面 */ public ModelAndView saveUser(HttpServletRequest request, HttpServletResponse response, UserInfoDTO command) throws Exception { UserInfoDTO user = (UserInfoDTO) command; ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName(command)); BindException errors = binder.getErrors(); ModelAndView mav = null; if (user.getUserID() == null || "".equals(user.getUserID())) { errors.rejectValue("userID", "userIdNull", "用户id不能为空"); } if (user.getUserName() == null || "".equals(user.getUserName())) { errors.reject("userNameNull", "用户名不能为空"); } if (errors.hasErrors()) { mav = new ModelAndView(this.userFormView, errors.getModel()); } else { this.sampleAction.saveUserInfo(user);// 保存用户信息 mav = new ModelAndView(this.userSuccessView); } return mav; } /** * 功能:删除用户信息<br> */ public ModelAndView deleteUser(HttpServletRequest request, HttpServletResponse response) throws Exception { String uid = RequestUtils.getStringParameter(request, userIdParam); UserInfoDTO user = new UserInfoDTO(); user.setUserID(uid); this.sampleAction.deleteUserInfo(user); ModelAndView mav = new ModelAndView(this.userSuccessView); return mav; } }
1.2 web-context配置
<!-- 把sampleMultiMethodController所有的请求映射到SimpleUrlHandlerMapping --> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="defaultHandler" ref=" sampleMultiMethodController "/> </bean> <!-- 集增,删,改,查操作到一个类的controller --> <bean id="sampleMultiMethodController" class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"> <property name="methodNameResolver"> <bean class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver"> <property name="mappings"> <props> <prop key="/ehld.sample.getuserinfolist.do">listUser</prop> <prop key="/ehld.sample.edituserinfo.do">edtiUser</prop> <prop key="/ehld.sample.saveuserinfo.do">saveUser</prop> </props> </property> </bean> </property> <property name="sampleAction" ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property> </bean>
2. MultiActionController的缺点
MultiActionController把相关的业务方法集中在一个类中进行处理,减少控制类的数量。方便于系统的维护,可以重用相关的逻辑代码,提高代码的重用,同时也减少bean的配置。有太多的bean配置可以说是Spring 的一个暇疵。Spring提供IOC,让我们灵活的控制bean的依赖。同时我们需要去维护太多的bean配置,Spring项目中很大程度上都在烂用xml 配置文件,这很不利于团队开发和系统的后期维护。MultiActionController也不例外。
1. multiActionController的配置相对复杂。MultiActionController需要注入一个MethodNameResolver对象,再通过MethodNameResolver的mappings属性来提供请求与方法之间的映射。这样的配置是复杂的和难以理解的。使用Spring框架的确很灵活,但是有时这种过分的灵活反而增加了系统的复杂度。
2. multiActionController配置涉及的bean过多。除了自身的bean定义外,还需要把所有的映射配置到一个UrlHandlerMapping中去。这样除了维护multiActionController的自身的bean定义外,还需要维护UrlHandlerMapping的定义。
笔者十分反对这种具有连带性的配置,一个bean的属性改变会造成对别一个bean属性的改变。这样增加了系统的复杂度,和维护成本。所以必须提供一种默认的实现,让bean之间的依赖,不要造成bean属性之间的依赖。MultiActionController在这方面表示得十分不好。
3. 数据绑定支持不好。SimpleFormController专门用来支持编辑和表单提效的,它支持数据绑定,在这方面做得很好。可以把jsp页面的字段值绑定为某一对象(Command)。可以自定义command的名称。虽然MultiActionController也支持数据绑定,但是它并不支持自定义command的名称。它默认的comamnd名称为”command”。这也是不便于维护的,对象应该有一个代表自身含义的名字。如果所有页面的绑定对象都以”command”作为名字,那将难以理解。MultiActionController支持数据绑定的方法参见上面例子的saveUser方法。
3. 理想的MultiActionController构想
一个理想的MultActionController应该配置简单明了,并且无需要在多个地方进行配置。 应该支持对绑定对象自定义名称。
<bean name="sampleMultiMethodController" class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"> <property name="commandName" value="userInfoDTO"/> <property name="formView" value="ehld.sample.userForm"/> <property name="successView" value="redirect:ehld.sample.getuserinfolist.do"/> <property name="urlMethodmappings"> <props> <!--显示用户信息列表 --> <prop key="/ehld.sample.getuserinfolist.do">listUser</prop> <!-- 编辑用户信息 --> <prop key="/ehld.sample.edituserinfo.do">edtiUser</prop> <!-- 保存用户信息--> <prop key="/ehld.sample.saveuserinfo.do">saveUser</prop> </props> </property> <property name="sampleAction" ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property> </bean>
上面是一个更让人能够理解的配置。
1.把请求与具体方法之间的映射作为MultiActionController自身的一个属性“urlMethodmappings”。
2.通过一个commandName属性,可以让用户自由决定绑定对象的名称。
3.简化UrlHandlerMapping的关联配置。对MutilActionController的bean配置进行改动时,无再需要去关心 SimpleUrlHandlerMapping的bean配置
4. 增强的MultiActionController实现
上面提到理想MultiActionController的构想,有三点需要实现。现在来讨论实现它们。
1. 把请求与具体方法之间的映射作为MultActionController自身的一个属性。也就是说MultiActionController提供一个“urlMethodMapping”的属性来保存请求路径与对应方法之间的映射关系。
我们知道MultiActionController有一个methodNameResolver的属性,而请求路径与方法之间的对应映射关系是由一个MethodNameResolver 的bean来保存的。我们一般可以配置一个PropertiesMethodNameResolver来作默认实现。把请求路径与方法之间的映射关系保存在PropertiesMethodNameResolver中的“mapping”属性中。
我们可以在MultiActionController中定义一个PropertiesMethodNameResolver类型的成员变量“propertiesMethodNameResoler”。和定义一个Properties类型的成员变量“urlMethodmappings”
在MultiActionController的bean进行配置的时候把urlMethodmappings的值作为propertiesMethodNameResoler的mapping的值。然后再调用MultiActionController的setMethodNameResolver()方法,把propertiesMethodNameResoler设置为MultiActionController的methodNameResolver的属性值。要做到这一些还应该实现InitializingBean接口
public class MultiMethodController extends MultiActionController implements InitializingBean { private Properties urlMethodmappings; public void afterPropertiesSet() throws Exception { if (urlMethodmappings != null && !urlMethodmappings.isEmpty()) { PropertiesMethodNameResolver propertiesMethodNameResolver = new PropertiesMethodNameResolver(); propertiesMethodNameResolver.setMappings(urlMethodmappings); this.setMethodNameResolver(propertiesMethodNameResolver); if (this.logger.isInfoEnabled()) { this.logger.info("binding success...... "); } } else { logger.info("no 'urlMethodmappings' set on MultiMethodController"); } } /** * @return Returns the urlMethodmappings. */ public Properties getUrlMethodmappings() { return urlMethodmappings; } /** * @param urlMethodmappings * The urlMethodmappings to set. */ public void setUrlMethodmappings(Properties urlMethodmappings) { this.urlMethodmappings = urlMethodmappings; } }
在afterPropertiesSet中, PropertiesMethodNameResolver propertiesMethodNameResolver = new PropertiesMethodNameResolver(); 创建一个默认的PropertiesMethodNameResolver的实例 propertiesMethodNameResolver.setMappings(urlMethodmappings); 把urlMethodmappings作为propertiesMethodNameResolver的mapping属性值 this.setMethodNameResolver(propertiesMethodNameResolver); 调用父类方法,把propertiesMethodNameResolver注入MethodNameResolver属性中
2. 通过一个commandName属性,可以让用户自由决定绑定对象的名称
MultiActionController的
getCommandName如下 public static final String DEFAULT_COMMAND_NAME = "command"; protected String getCommandName(Object command) { return DEFAULT_COMMAND_NAME; }MultiActionController并没有一个setCommandName的方法,所以我们需要一个setCommandName的方法,然后重写getCommandName(Object command)方法
private String commandName =DEFAULT_COMMAND_NAME; public String getCommandName() { return commandName; } public void setCommandName(String commandName) { this.commandName = commandName; } protected String getCommandName(Object object) { return this.getCommandName(); }
如果没有设置commandName属性,默认值为“command”,通过setCommandName方法就可以自由的去决定comamnd对象的名称了。
这样我们基本上已经简化了MultiActionController的自身的配置,但是它仍然需要与一个UrlHandlerMapping联系,也就是增加或删除一个MutilActionController的bean。都需要修改某一个UrlHandlerMapping的bean的配置。这也就是我们上面说的理想MultiActionController的第3点。
3. 简化UrlHandlerMapping的关联配置
UrlHandlerMapping是请求路径与Controller之间的对应映射。UrlHandlerMapping有一个最简单的实现就是SimpleUrlHandlerMapping.
<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/welcom.do">oneController</prop> </props> </property> </bean>
“/welcom.do”这个请求路径对应bean 名称为oneController的Controller实例。所以应将MultiActionController中的所有请求路径都保存在一个UrlHandlerMapping的mappings属性中,作为key值,把MutilActionController 的bean实例作为value作。
DispatcherServlet在初始化时会在Context中查找所有类型为HandlerMapping的bean,将所有的HandlerMapping实例保存在handlerMappings属性List中。当一个请求进入时,DispatcherServlet会依次查找handlerMappings List中的HandlerMapping实例是否匹配当前请求路径,如果匹配当前请求路径,就获取请求路径对应的Controller实例;如果Controller实例是MultiActionController类型时,MultiActionController就会会根据当前请求路径去调用MultiActionController相应的方法。这就是一个MultiActionController的执行过程。
根据这样的原理,能够有一个类似SimpleUrlHandlerMapping的HandlerMapping能够在初始化的时候自动在当前WebApplicationContext中查找所有MultiActionController类型的bean。然后依次生成一个以MultiActionController的urlMethodmappings Map的所有key值作为key值,以MultiActionController实例为value值的一个Map,并把这个Map所有元素都添加到SimpleUrlHandlerMapping的mappings属性中。这样就达到了我们自动化配置的效果。
我们把这个HandlerMapping 称为MultiMethodControllerUrlHandlerMapping,下面我们讲怎么具体去实现它。
5.实现MultiMethodControllerUrlHandlerMapping
我们在上面讨论过了怎么实现MultiMethodControllerUrlHandlerMapping,要实现为具体的代码,我们可以通过扩展org.springframework.web.servlet.handler.AbstractUrlHandlerMapping。AbstractUrlHandlerMapping扩展了org.springframework.web.context.support.WebApplicationObjectSupport。WebApplicationObjectSupport可以获得当前WebApplicationContext。
1. 重写initApplicationContext方法,在context中查找所有MultiActionController类型的bean,把MultiActionController的urlMethodmappings属性的key值为key值,MultiActionController实例为键值的键值对添加到一个urlMap中。
public class MultiMethodControllerUrlHandlerMapping extends AbstractUrlHandlerMapping{ private Map urlMap = new HashMap(); public void initApplicationContext() throws BeansException { initialUrlMap(); } protected void initialUrlMap()throws BeansException{ //找查所有MultiMethodController类型和子类型的bean到一个map中,bean Name为key值 ,bean实例为value值 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( getWebApplicationContext(), MultiMethodController.class, true, false); List controllers = null; if(!matchingBeans.isEmpty()){ controllers = new ArrayList(matchingBeans.values()); for(int i = 0; controllers != null && i < controllers.size();i++){ MultiMethodController controller = (MultiMethodController)controllers.get(i); Properties urlPros = controller.getUrlMethodmappings(); Iterator itr = urlPros.keySet().iterator(); for(;itr.hasNext();){ String url = (String)itr.next(); urlMap.put(url,controller); } } } }2. 遍历urlMap,调用AbstractUrlHandlerMapping的registerHandler(String urlPath, Object handler)方法,依次将url与对应的handler注册到AbstractUrlHandlerMapping的handlerMap中。
protected void registerUrlMap()throws BeansException{ if (this.urlMap.isEmpty()) { logger.info("Neither 'urlMap' nor 'mappings' set on MultiMethodControllerUrlHandlerMapping"); } else { Iterator itr = this.urlMap.keySet().iterator(); while (itr.hasNext()) { String url = (String) itr.next(); Object handler = this.urlMap.get(url); // prepend with slash if it's not present if (!url.startsWith("/")) { url = "/" + url; } //父类方法 registerHandler(url, handler); } } }
然后在initApplicationContext方法中调用registerUrlMap方法
public void initApplicationContext() throws BeansException { initialUrlMap(); registerUrlMap(); }
3. 使用MultiMethodControllerUrlHandlerMapping
使用MultiMethodControllerUrlHandlerMapping,只需要在ApplicationContext中,定义成一个bean就可以了。
id="multiMethodControllerUrlHandlerMapping" class="com.prs.application.ehld.web.handler.MultiMethodControllerUrlHandlerMapping"> <property name="order"> <value>3</value> </property> </bean>
注意:在一个context如果定义多个HandlerMapping,需要为每一个HandlerMapping指定order属性。
你只需要在在context 中定义MultiMethodControllerUrlHandlerMapping,在使用MultiActionController时,只需要配置urlMethodmappings属性就可以了。当删除或增加一个MultiActionController的bean时,无需要连带配置任何HandlerMapping. 简化了bean的配置。使得MultiActionControler的bean配置只关心自身的属性配置,而无需要去关心看起来与自身无关的HandlerMapping的配置。使得整个配置更合乎人们正常的思维逻辑,减少配置的复杂性。
6.设计讨论
在这里我们将对以Spring为基础进行项目架构设计进行一些讨论.
1. MultiActionController还是AbstractController与SimpleFormController组合
在使用Spring MVC时,SimpleFormController用于表单编辑和提交;而复杂的功能则通过扩展AbstractcController完成。这就是所谓的AbstractController与SimpleFormController组合。以AbstractController与SimpleFormController的结合来完成表示层逻辑。
Spring MVC虽然也提供了MultiActionController,但是它似乎天生就有点蹩脚。对数据绑定支持不是很好,在用于表单编辑和提交时不像SimpleFormController那么强大。其实通过对MultiActionController的扩展和增强,完成可以实现与SimpleFormController同样的功能,比如数据校验等,并且还比SimpleFormController具有更多的灵活性。
在OO技术中,有一个重要的原则:低耦合,高内聚;我们应该按职责来设计对象。按对象应该具有的职责来给对象设计相应的方法。如果把一个对象本来该具有的职责分散到不同类中去完成,那么这个些类是违反“低耦合,高内聚”原则的。一个类不是高内聚的,就不便于维护和扩展,造成大量重复代码的产生。同样把一组相关的功能分散到多个Controller去实现,是违反“低耦合,高内聚”原则的,可以就会产生大量的重复代码。比如参数获取,数据校验等。如果使用MultiActionController,把相关的功能由一个Controller的不同方法实现,集中在一个Controller类中处理,就使得这个Controller类是具有“高内聚”性的。所以,在项目应用中,相关的功能应该由一个MultiActionController的不同方法去实现。这样就便于代码的维护,提高代码的重用,减少bean配置,降低项目的复杂度。
2. 灵活性与简易化
Spring作为一个轻量级的j2ee基础框架,使用是非常灵活的。特别是可以通过xml文件来灵活的配置对象之间的依赖。但是,以Spring作为框架的项目,bean的配置太多,反而增加了项目的复杂度。在开发过程中,应该把主要精力花在关注业务逻辑的实现上面,而不应该花在配置上面。灵活度越大也就导致了复杂度越高。当然,Spring是一个通用框架,应该具有这样的灵活性,才便于扩展,以满足各种应用需要。
在具体的项目中,就应该使架构使用起来简单,易用。特别是以Spring作为基础的架构中,应该通过设计降低配置的复杂度,尽可能的减少bean的配置和使配置简单化。
一个bean属性发生变化,不应该产生连带关系,使得其它bean也需要修改配置。这是不利于团队开发的。在团队开发中,开发人员应该只关心业务对象的bean配置。
像HandlerMapping这些属于框架基础bean配置一旦定义后就应该具有稳定性。不要因为业务对象bean的改变而需要开发人员随之进行修改。
3. 增强的MultiActionController与MultiMethodControllerUrlHandlerMapping
通过扩展MultiActionController,使得它得到增强,能够实现SimpleFormController的功能,同时使得配置更加直观和简易。
只需要定义一个MultiMethodControllerUrlHandlerMapping,使得开发人员只需要关注相关MultiActionController的配置,而无需去再关注和修改HandlerMapping的配置。
通过MultiMethodControllerUrlHandlerMapping 与增强的MultiActionController结合,更易于运用OO技术设计高内聚的Controller类,减化bean的配置。让开发人员把精力花在系统的业务逻辑的实现上,而不会去过度关心bean的配置。
7.完整的代码实现
这里把增强的MultiActionController称为MultiMethodController
1. MultiMethodController.java
public class MultiMethodController extends MultiActionController implements InitializingBean { private Properties urlMethodmappings; private String commandName =DEFAULT_COMMAND_NAME; private String formView ; private String successView; /** * @return Returns the formView. */ public String getFormView() { return formView; } /** * @param formView The formView to set. */ public void setFormView(String formView) { this.formView = formView; } /** * @return Returns the successView. */ public String getSuccessView() { return successView; } /** * @param successView The successView to set. */ public void setSuccessView(String successView) { this.successView = successView; } /* (non-Javadoc) * @see org.springframework.web.servlet.mvc.multiaction.MultiActionController#getCommandName(java.lang.Object) */ protected String getCommandName(Object object) { return this.getCommandName(); } /* * (non-Javadoc) * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { if (urlMethodmappings != null && !urlMethodmappings.isEmpty()) { PropertiesMethodNameResolver propertiesMethodNameResolver = new PropertiesMethodNameResolver(); propertiesMethodNameResolver.setMappings(urlMethodmappings); this.setMethodNameResolver(propertiesMethodNameResolver); if (this.logger.isInfoEnabled()) { this.logger.info("binding success...... "); } } else { logger.info("no 'urlMethodmappings' set on MultiMethodController"); } } /** * @return Returns the urlMethodmappings. */ public Properties getUrlMethodmappings() { return urlMethodmappings; } /** * @param urlMethodmappings * The urlMethodmappings to set. */ public void setUrlMethodmappings(Properties urlMethodmappings) { this.urlMethodmappings = urlMethodmappings; } /** * @return Returns the commandName. */ public String getCommandName() { return commandName; } /** * @param commandName The commandName to set. */ public void setCommandName(String commandName) { this.commandName = commandName; } } 2. MultiMethodControllerUrlHandlerMapping.java public class MultiMethodControllerUrlHandlerMapping extends AbstractUrlHandlerMapping{ private Map urlMap = new HashMap(); /** *映射URL 到 Controller 的bean 名称 *这是一个配置HandMapping的典型的方式. *<p>支持直接URL匹配和"ant风格"模式的匹配 *详细的语法,参见AntPathMatcher类 * * @param mappings URL作为键,而bean 名称作为键值的Properties * @see org.springframework.util.AntPathMatcher */ public void setMappings(Properties mappings){ this.urlMap.putAll(mappings); } /** * @return Returns the urlMap. */ public Map getUrlMap() { return urlMap; } /** * @param urlMap The urlMap to set. */ public void setUrlMap(Map urlMap) { this.urlMap = urlMap; } public void initApplicationContext() throws BeansException { initialUrlMap(); registerUrlMap(); } protected void registerUrlMap()throws BeansException{ if (this.urlMap.isEmpty()) { logger.info("Neither 'urlMap' nor 'mappings' set on MultiMethodControllerUrlHandlerMapping"); } else { Iterator itr = this.urlMap.keySet().iterator(); while (itr.hasNext()) { String url = (String) itr.next(); Object handler = this.urlMap.get(url); // prepend with slash if it's not present if (!url.startsWith("/")) { url = "/" + url; } registerHandler(url, handler); } } } protected void initialUrlMap()throws BeansException{ //找查所有MultiMethodController类型和子类型的bean到一个map中,bean Name为key值 ,bean实例为value值 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( getWebApplicationContext(), MultiMethodController.class, true, false); List controllers = null; if(!matchingBeans.isEmpty()){ controllers = new ArrayList(matchingBeans.values()); Collections.sort(controllers, new OrderComparator()); for(int i = 0; controllers != null && i < controllers.size();i++){ MultiMethodController controller = (MultiMethodController)controllers.get(i); Properties urlPros = controller.getUrlMethodmappings(); Iterator itr = urlPros.keySet().iterator(); for(;itr.hasNext();){ String url = (String)itr.next(); urlMap.put(url,controller); } } } } }
注:原创文章,曾发表在it168
评论
7 楼
klyuan
2007-04-27
PropertiesMethodNameResolver
并不好用啊,我不喜欢那样的方式
并不好用啊,我不喜欢那样的方式
6 楼
jamesby
2007-04-21
我用
我觉得这个好,但是可能配置稍微多了一些.
<bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
我觉得这个好,但是可能配置稍微多了一些.
5 楼
daquan198163
2007-04-21
klyuan 写道
不是没有考虑过使用
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
.
但是你使用了它之后,就一定得改变你的编程模式!!!
例如在jsp中的引用就写成这样了
http://localhost:8080/springapp/book.do?method=add
跟struts的dispatcherAction一样。
我非常反感这样的方式的。
而我那种方式,则使得一个url地址与一个方法有一个明确的对应,更直观,也更乎合人们的编程习惯。而且也更容易理解和维护
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
.
但是你使用了它之后,就一定得改变你的编程模式!!!
例如在jsp中的引用就写成这样了
http://localhost:8080/springapp/book.do?method=add
跟struts的dispatcherAction一样。
我非常反感这样的方式的。
而我那种方式,则使得一个url地址与一个方法有一个明确的对应,更直观,也更乎合人们的编程习惯。而且也更容易理解和维护
我以前也这么想,但是现在觉得有点得不偿失
更pragmatic的做法还是ParameterMethodNameResolver
4 楼
klyuan
2007-04-20
可以这样说吧,
ParameterMethodNameResolver相当于弱类型,而我这种方式就相当于强类型。呵呵
ParameterMethodNameResolver相当于弱类型,而我这种方式就相当于强类型。呵呵
3 楼
klyuan
2007-04-20
不是没有考虑过使用
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
.
但是你使用了它之后,就一定得改变你的编程模式!!!
例如在jsp中的引用就写成这样了
http://localhost:8080/springapp/book.do?method=add
跟struts的dispatcherAction一样。
我非常反感这样的方式的。
而我那种方式,则使得一个url地址与一个方法有一个明确的对应,更直观,也更乎合人们的编程习惯。而且也更容易理解和维护
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
.
但是你使用了它之后,就一定得改变你的编程模式!!!
例如在jsp中的引用就写成这样了
http://localhost:8080/springapp/book.do?method=add
跟struts的dispatcherAction一样。
我非常反感这样的方式的。
而我那种方式,则使得一个url地址与一个方法有一个明确的对应,更直观,也更乎合人们的编程习惯。而且也更容易理解和维护
2 楼
lsy
2007-04-20
除了改进绑定以外,我想维护UrlHandlerMapping的配置是没有必要,这都是因为楼主使用了
为什么不考虑使用
这里使用ParameterMethodNameResolver,定义方法名称为method,默认方法为list
这里如果请求url是http://localhost:8080/springapp/book.do,将执行默认方法list,返回ModelAndView。
否则可以是http://localhost:8080/springapp/book.do?method=add;
或者http://localhost:8080/springapp/book.do?method=delete。
并没有太多的urlMapping,唯一的mapping就是先前SimpleUrlHandlerMapping所定义/book.do
org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver
为什么不考虑使用
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/book.do">bookAction</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"> <value>org.springframework.web.servlet.view.InternalResourceView</value> </property> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> <bean id="paraMethodResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <property name="paramName"><value>method</value></property> <property name="defaultMethodName"><value>list</value></property> </bean> <bean id="bookAction" class="onlyfun.caterpillar.GuestBookAction"> <property name="methodNameResolver"> <ref bean="paraMethodResolver"/> </property> <property name="testPage"> <value>test</value> </property> </bean> </beans>
这里使用ParameterMethodNameResolver,定义方法名称为method,默认方法为list
public class GuestBookAction extends MultiActionController { private String testPage; public ModelAndView list(HttpServletRequest req, HttpServletResponse res) { return new ModelAndView(this.getTestPage(),"executed", "list"); } public ModelAndView add(HttpServletRequest req, HttpServletResponse res) { return new ModelAndView(this.getTestPage(),"executed", "add"); } public ModelAndView delete(HttpServletRequest req, HttpServletResponse res) { return new ModelAndView(this.getTestPage(),"executed", "delete"); } public String getTestPage() { return testPage; } public void setTestPage(String testPage) { this.testPage = testPage; } }
这里如果请求url是http://localhost:8080/springapp/book.do,将执行默认方法list,返回ModelAndView。
否则可以是http://localhost:8080/springapp/book.do?method=add;
或者http://localhost:8080/springapp/book.do?method=delete。
并没有太多的urlMapping,唯一的mapping就是先前SimpleUrlHandlerMapping所定义/book.do
1 楼
jamesby
2007-04-20
搂主,使用code标签,否则太混乱。
发表评论
-
一个特殊的异常处理
2008-12-13 23:59 1400一个特殊的异常处理 文:袁光东 一、业务需求说明 前段时间接 ... -
程序员为什么不写单元测试
2007-07-04 11:31 29213程序员为什么不写单 ... -
Spring JavaConfig开发指南(下)
2007-06-03 10:56 6556... -
Spring JavaConfig开发指南(上)
2007-06-03 10:25 7774Spring JavaConfig开发指南 作者:袁光东 1. ... -
ThreadLocal与synchronized
2007-05-22 17:49 27376ThreadLocal与synchronized Java良好 ... -
倒底该怎么写DAO的单元测试?
2007-05-17 16:17 14046public void testAddUserInfo() ... -
详解spring事务属性
2007-05-10 22:55 20431Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我 ... -
Ibatis读写CLOB数据
2007-04-25 16:43 22857Ibatis是一个高效,方便,易于学习的数据访问组件,在性能上 ... -
细说框架风云 JSF能否拯救WEB江湖
2007-04-24 18:08 2058细说框架风云 JSF能否拯救WEB江湖 Java ... -
模板方法模式实现探讨
2007-04-23 18:30 4416模板方法(Template Method) ... -
让Spring架构减化事务配置
2007-04-19 12:20 4689让Spring架构减化事务配置 注:原创文章,本文曾发表于it ... -
J2EE项目异常处理
2007-04-18 12:19 16407J2EE项目异常处理 ...
相关推荐
springMVC3学习(五)--MultiActionController(源码) 文章地址:http://blog.csdn.net/itmyhome1990/article/details/25988091
Spring MVC MultiActionController---多动作控制器 博客介绍: http://blog.csdn.net/sunshine_love/article/details/8842261 更换web.xml中web-config.xml即可尝试不同方法解析器,如有问题,请博客回复。欢迎交流...
PropertyMultiActionController可能是MultiActionController的一个变种,它可能通过属性(property)来决定调用哪个处理方法,增强了灵活性。不过,随着Spring MVC的发展,这种方式已经被注解驱动的Controller所取代...
在3.0.6.RELEASE中,Spring Data Access层增强了对JPA2的支持,提供了更好的事务管理策略,并且支持动态SQL查询,使数据库操作更为简便。 五、消息支持 Spring 3.0.6.RELEASE提供了全面的消息传递支持,包括JMS...
#### 一、Spring架构简介及特点 Spring是一个轻量级的开源Java应用框架,最初由Rod Johnson创建,并由Juergen Hoeller等人继续开发和完善。Spring框架的设计目的是简化企业级应用的开发,提供了一整套的解决方案,...
Spring的设计理念强调了逻辑层与Web层的分离,这意味着业务逻辑可以独立于Web控制器进行校验和执行,从而提高代码的可复用性和测试性。例如,一个验证应用可以直接与业务对象交互,而不依赖特定的控制器,降低了对...
本文档是对Spring Web MVC模式的外文翻译,深入解析了Spring如何在Web开发中提供灵活且可扩展的架构。 1. **Spring框架的特性与优势** - **全面性**:Spring不仅是一个Web框架,它还提供了诸如bean配置、面向切面...
- Spring MVC提供多种类型的控制器,如AbstractController、AbstractCommandController、MultiActionController、SimpleFormController等,帮助开发者处理不同复杂度的控制逻辑。 - 与Struts相比,Spring MVC允许...
在Spring MVC中,`MultiActionController`是早期版本中的一个控制器类,它允许在一个控制器类中处理多个请求映射。然而,从Spring 3.0开始,`MultiActionController`已被废弃,取而代之的是更灵活、更面向注解的`@...
创建一个MultiActionController需要继承`org.springframework.web.servlet.mvc.MultiActionController`基类。然后,你可以定义多个处理方法,每个方法对应一个特定的请求。例如: ```java public class Test...
Spring MVC的设计理念是提供一种灵活且可扩展的架构,让开发者能够更好地分离业务逻辑和表示层,从而提高代码的可重用性和可测试性。 1. **组件介绍** - **DispatcherServlet**: Spring MVC的核心组件,它作为一个...
与Struts相比,Spring Web MVC的控制器接口更简单,允许开发者根据需求选择不同类型的控制器,如AbstractController、AbstractCommandController、MultiActionController等,无需强制使用特定的FormController。...
Spring MVC基于MVC(Model-View-Controller)设计模式,它将应用程序的业务逻辑(Model)、用户界面(View)和控制流程(Controller)分离,使得各部分职责明确,易于维护和扩展。Controller接收用户的请求,调用...
13.3.3. MultiActionController 13.3.4. 命令控制器 13.4. 处理器映射(handler mapping) 13.4.1. BeanNameUrlHandlerMapping 13.4.2. SimpleUrlHandlerMapping 13.4.3. 拦截器(HandlerInterceptor) 13.5. 视图与...
- **org.springframework.context-3.0.5.RELEASE.jar**:构建在beans包基础之上,增强了对资源文件和国际化方面的支持。 - **org.springframework.core-3.0.5.RELEASE.jar**:Spring的核心包,包含了框架的基础组件...
此外,Spring MVC支持多种控制器类型,如AbstractController、AbstractCommandController、MultiActionController等,这使得开发者可以根据具体需求选择合适的控制器基类,增强了代码的可复用性和可维护性。...
Spring Framework 开发参考手册 Rod Johnson Juergen Hoeller Alef Arendsen Colin Sampaleanu Rob Harrop Thomas Risberg Darren Davison Dmitriy Kopylenko Mark Pollack Thierry Templier Erwin ...
14.4 架构设计和环境搭配 14.4.1 内容管理系统采用的架构 14.4.2 在Eclipse下建立项目myEdition 14.4.3 编写本项目的Ant文件 14.4.4 配置本项目的web.xml文件 14.5 编写内容管理系统的JSP页面 14.5.1 用户注册画面...
Spring MVC 入门 从一个最简单的 Spring Web 应用程式,来看看 Spring MVC 框架的架构与 API 组成元素。 第一个 Spring MVC 程式 WebApplicationContext Handler Mapping Handler ...