Spring架构增强MultiActionController

Spring架构增强MultiActionController

在使用Spring提供的控制器时,AbstractController和SimpleFormController是应用得最多的。AbstractController是最基本的Controller,可以给予用户最大的灵活性。

SimpleFormController则用于典型的表单编辑和提交。在一个需要增,删,改,查的需求中,增加和修改扩展SimpleFormController完成,删除和查询则扩展AbstractController完成。

但是像上面那样完成某一业务对象的增,删,改,查,都属于一类相关的业务。把一类相关的操作分布到不同的类去完成,违返“高内聚”的设计原则。这样四个业务操作需要四个类来完成,造成太多的类文件,难以维护和配置。

所以Spring借鉴Struts的DispatchAction提供了类似功能的MultiActionController。可以实现不同的请求路径对应MultiActionController中的不同方法,这样就可以把相关的操作都在一个类的相关方法中完成。这样使得这个类具有“高内聚”,也利于系统的维护,还避免了重复代码。增加和修改操作的数据验证逻辑是很相似的,使用MultiActionController后就可以让增加和修改操作共用一段数据验证逻辑代码。

1.使用MultiActionController

MultiActionController会使不同的请求映射为不同方法,这里是一个实现用户信息增删改查的例子:

1.1SampleMultiMethodController实现

publicclassSampleMultiMethodControllerextendsMultiActionController...{

//用户信息列表view

privatestaticfinalStringuserInfoListView="ehld.sample.getuserinfolist";

//用户信息编辑view

privatestaticfinalStringuserFormView="ehld.sample.userForm";

//提交成功后显示的view

privatestaticfinalStringuserSuccessView="

redirect:ehld.sample.getuserinfolist.do";

//用户信息列表key值

privatestaticfinalStringuserInfoListKey="userInfoList";

//userid

privatefinalStringuserIdParam="id";

//定义业务对象

privateSampleActionsampleAction;

publicSampleActiongetSampleAction()...{

returnsampleAction;

}

publicvoidsetSampleAction(SampleActionsampleAction)...{

this.sampleAction=sampleAction;

}

/**//**

*功能:获得所有的用户信息<br>

*/

publicModelAndViewlistUser(HttpServletRequestrequest,

HttpServletResponseresponse)throwsException...{

ListuserInfoList=this.sampleAction.getUserInfoList();

ModelAndViewmav=newModelAndView(userInfoListView);

mav.addObject(this.userInfoListKey,userInfoList);

returnmav;

}

/**//**

*功能:编辑用户信息<br>

*/

publicModelAndViewedtiUser(HttpServletRequestrequest,

HttpServletResponseresponse)throwsException...{

Stringuid=RequestUtils.getStringParameter(request,userIdParam);

UserInfoDTOuserInfo=null;

if(!"".equals(uid))...{

userInfo=this.sampleAction.getUserInfo(uid);

}

if(userInfo==null)...{

userInfo=newUserInfoDTO();

}

ModelAndViewmav=newModelAndView(this.userFormView,this

.getCommandName(null),userInfo);

returnmav;

}

/**//**

*功能:保存修改或新增的用户信息<br>

*检查从页面bind的对象,如果userId或userName为空则返回原来的form页面;

否则进行保存用户信息操作,返回

*成功页面

*/

publicModelAndViewsaveUser(HttpServletRequestrequest,

HttpServletResponseresponse,UserInfoDTOcommand)throws

Exception...{

UserInfoDTOuser=(UserInfoDTO)command;

ServletRequestDataBinderbinder=newServletRequestDataBinder(command,

getCommandName(command));

BindExceptionerrors=binder.getErrors();

ModelAndViewmav=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=newModelAndView(this.userFormView,errors.getModel());

}else...{

this.sampleAction.saveUserInfo(user);//保存用户信息

mav=newModelAndView(this.userSuccessView);

}

returnmav;

}

/**//**

*功能:删除用户信息<br>

*/

publicModelAndViewdeleteUser(HttpServletRequestrequest,

HttpServletResponseresponse)throwsException...{

Stringuid=RequestUtils.getStringParameter(request,userIdParam);

UserInfoDTOuser=newUserInfoDTO();

user.setUserID(uid);

this.sampleAction.deleteUserInfo(user);

ModelAndViewmav=newModelAndView(this.userSuccessView);

returnmav;

}

}

1.2web-context配置

<!--把sampleMultiMethodController所有的请求映射到SimpleUrlHandlerMapping-->

<beanid="handlerMapping"

class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<propertyname="defaultHandler"ref="sampleMultiMethodController"/>

</bean>

<!--集增,删,改,查操作到一个类的controller-->

<beanid="sampleMultiMethodController"

class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController">

<propertyname="methodNameResolver">

<bean

class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">

<propertyname="mappings">

<props>

<propkey="/ehld.sample.getuserinfolist.do">listUser</prop>

<propkey="/ehld.sample.edituserinfo.do">edtiUser</prop>

<propkey="/ehld.sample.saveuserinfo.do">saveUser</prop>

</props>

</property>

</bean>

</property>

<propertyname="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也不例外。

2.1.multiActionController的配置相对复杂

MultiActionController需要注入一个MethodNameResolver对象,再通过MethodNameResolver的mappings属性来提供请求与方法之间的映射。这样的配置是复杂的和难以理解的。使用Spring框架的确很灵活,但是有时这种过分的灵活反而增加了系统的复杂度。

2.2.multiActionController配置涉及的bean过多

除了自身的bean定义外,还需要把所有的映射配置到一个UrlHandlerMapping中去。这样除了维护multiActionController的自身的bean定义外,还需要维护UrlHandlerMapping的定义。

笔者十分反对这种具有连带性的配置,一个bean的属性改变会造成对别一个bean属性的改变。这样增加了系统的复杂度,和维护成本。所以必须提供一种默认的实现,让bean之间的依赖,不要造成bean属性之间的依赖。MultiActionController在这方面表示得十分不好。

2.3.数据绑定支持不好

SimpleFormController专门用来支持编辑和表单提效的,它支持数据绑定,在这方面做得很好。可以把jsp页面的字段值绑定为某一对象(Command)。可以自定义command的名称。虽然MultiActionController也支持数据绑定,但是它并不支持自定义command的名称。它默认的comamnd名称为”command”。这也是不便于维护的,对象应该有一个代表自身含义的名字。如果所有页面的绑定对象都以“command”作为名字,那将难以理解。MultiActionController支持数据绑定的方法参见上面例子的saveUser方法。

3.理想的MultiActionController构想

一个理想的MultActionController应该配置简单明了,并且无需要在多个地方进行配置。

应该支持对绑定对象自定义名称。

<beanname="sampleMultiMethodController"

class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController">

<propertyname="commandName"value="userInfoDTO"/>

<propertyname="formView"value="ehld.sample.userForm"/>

<propertyname="successView"value="redirect:ehld.sample.getuserinfolist.do"/>

<propertyname="urlMethodmappings">

<props>

<!--显示用户信息列表-->

<propkey="/ehld.sample.getuserinfolist.do">listUser</prop>

<!--编辑用户信息-->

<propkey="/ehld.sample.edituserinfo.do">edtiUser</prop>

<!--保存用户信息-->

<propkey="/ehld.sample.saveuserinfo.do">saveUser</prop>

</props>

</property>

<propertyname="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的构想,有三点需要实现。现在来讨论实现它们。

4.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接口。

publicclassMultiMethodControllerextendsMultiActionControllerimplements

InitializingBean...{

privatePropertiesurlMethodmappings;

publicvoidafterPropertiesSet()throwsException...{

if(urlMethodmappings!=null&&!urlMethodmappings.isEmpty())...{

PropertiesMethodNameResolverpropertiesMethodNameResolver

=newPropertiesMethodNameResolver();

propertiesMethodNameResolver.setMappings(urlMethodmappings);

this.setMethodNameResolver(propertiesMethodNameResolver);

if(this.logger.isInfoEnabled())...{

this.logger.info("bindingsuccess......");

}

}else...{

logger.info("no'urlMethodmappings'setonMultiMethodController");

}

}

/**//**

*@returnReturnstheurlMethodmappings.

*/

publicPropertiesgetUrlMethodmappings()...{

returnurlMethodmappings;

}

/**//**

*@paramurlMethodmappings

*TheurlMethodmappingstoset.

*/

publicvoidsetUrlMethodmappings(PropertiesurlMethodmappings)...{

this.urlMethodmappings=urlMethodmappings;

}

}

在afterPropertiesSet中,PropertiesMethodNameResolverpropertiesMethodNameResolver

=newPropertiesMethodNameResolver();

创建一个默认的PropertiesMethodNameResolver的实例

propertiesMethodNameResolver.setMappings(urlMethodmappings);

把urlMethodmappings作为propertiesMethodNameResolver的mapping属性值

this.setMethodNameResolver(propertiesMethodNameResolver);

调用父类方法,把propertiesMethodNameResolver注入MethodNameResolver属性中。

注意,在这里我命名为MultiMethodController是为了与MultiActionController区别。

通过这样的代码,简化了原本复杂的配置。

4.2通过一个commandName属性,可以让用户自由决定绑定对象的名称

MultiActionController的getCommandName如下:

publicstaticfinalStringDEFAULT_COMMAND_name="command";

protectedStringgetCommandName(Objectcommand)...{

returnDEFAULT_COMMAND_NAME;

}

MultiActionController并没有一个setCommandName的方法,所以我们需要一个setCommandName的方法,然后重写getCommandName(Objectcommand)方法

privateStringcommandName=DEFAULT_COMMAND_NAME;

publicStringgetCommandName()...{

returncommandName;

}

publicvoidsetCommandName(StringcommandName)...{

this.commandName=commandName;

}

protectedStringgetCommandName(Objectobject)...{

returnthis.getCommandName();

}

如果没有设置commandName属性,默认值为“command”,通过setCommandName方法就可以自由的去决定comamnd对象的名称了。

这样我们基本上已经简化了MultiActionController的自身的配置,但是它仍然需要与一个UrlHandlerMapping联系,也就是增加或删除一个MutilActionController的bean。都需要修改某一个UrlHandlerMapping的bean的配置。这也就是我们上面说的理想MultiActionController的第3点。

4.3简化UrlHandlerMapping的关联配置

UrlHandlerMapping是请求路径与Controller之间的对应映射。UrlHandlerMapping有一个最简单的实现就是SimpleUrlHandlerMapping。

<beanid="simpleUrlMapping"

class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<propertyname="mappings">

<props>

<propkey="/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会依次查找handlerMappingsList中的HandlerMapping实例是否匹配当前请求路径,如果匹配当前请求路径,就获取请求路径对应的Controller实例;如果Controller实例是MultiActionController类型时,MultiActionController就会会根据当前请求路径去调用MultiActionController相应的方法。这就是一个MultiActionController的执行过程。

根据这样的原理,能够有一个类似SimpleUrlHandlerMapping的HandlerMapping能够在初始化的时候自动在当前WebApplicationContext中查找所有MultiActionController类型的bean。然后依次生成一个以MultiActionController的urlMethodmappingsMap的所有key值作为key值,以MultiActionController实例为value值的一个Map,并把这个Map所有元素都添加到SimpleUrlHandlerMapping的mappings属性中。这样就达到了我们自动化配置的效果。

我们把这个HandlerMapping称为MultiMethodControllerUrlHandlerMapping,下面我们讲怎么具体去实现它。

原文:http://www.51cto.com/specbook/223/36243.htm

相关推荐