在使用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
{