- 浏览: 185481 次
- 性别:
- 来自: 深圳
-
最新评论
-
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 1415一个特殊的异常处理 文:袁光东 一、业务需求说明 前段时间接 ... -
程序员为什么不写单元测试
2007-07-04 11:31 29309程序员为什么不写单 ... -
Spring JavaConfig开发指南(下)
2007-06-03 10:56 6584... -
Spring JavaConfig开发指南(上)
2007-06-03 10:25 7832Spring JavaConfig开发指南 作者:袁光东 1. ... -
ThreadLocal与synchronized
2007-05-22 17:49 27528ThreadLocal与synchronized Java良好 ... -
倒底该怎么写DAO的单元测试?
2007-05-17 16:17 14106public void testAddUserInfo() ... -
详解spring事务属性
2007-05-10 22:55 20520Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我 ... -
Ibatis读写CLOB数据
2007-04-25 16:43 23020Ibatis是一个高效,方便,易于学习的数据访问组件,在性能上 ... -
细说框架风云 JSF能否拯救WEB江湖
2007-04-24 18:08 2109细说框架风云 JSF能否拯救WEB江湖 Java ... -
模板方法模式实现探讨
2007-04-23 18:30 4452模板方法(Template Method) ... -
让Spring架构减化事务配置
2007-04-19 12:20 4712让Spring架构减化事务配置 注:原创文章,本文曾发表于it ... -
J2EE项目异常处理
2007-04-18 12:19 16466J2EE项目异常处理 ...
相关推荐
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 ...
### Spring入门知识点详解 #### Spring框架概述 - **Spring**是一个开源框架,旨在简化企业级应用的开发。作为一款轻量级的Java平台框架,Spring提供了广泛的解决方案,从基础的依赖注入(DI)到复杂的事务管理和...
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 用户注册画面...