论坛首页 Java企业应用论坛

[SpringMVC]修改源码使之能够更加智能的自动装配request请求参数.

浏览 47700 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-06-11   最后修改:2012-06-13

记得以前用Struts2时候,有种说法好像叫做自动装配(可能名字记错了,见谅),就是将request中的<form>表单自动组装为java对象。然而后来使用SpringMVC之后,发现SpringMVC这一点做得有些差强人意。因为Struts2能够自动组装的是<input name="obj.property"> 这种request参数名,而SpringMVC自动组装的是<input name="property"> 这种参数名,虽然仅仅少了个obj. ,但是用起来差别很大。

 

比如在管理系统中,有时候一个form表单中会涉及多个类。我最近遇到的一个form表单中有两种类,一个是辅料 ,一种是原料 。两种原料中都有一些同名的参数mark 。在form表单中是这样的:

<form>
    ......<!--省去代码若干-->
    <input type='text' name='mark'/> <!-- 原料的mark -->
    ......<!--省去代码若干-->
    <input type='text' name='mark'/> <!-- 辅料的mark -->
    ......<!--省去代码若干-->
</form>
 

 这种情况下,我感觉使用SpringMVC很麻烦,或许是自己知识短浅,找不到解决办法。

 

但是我也想到了为什么SpringMVC不能像Struts2一样,可以再name标签上添加一个obj. 呢?如果可以添加obj. 的话,这种情况根本没有一点复杂的:

<form>
    ......<!--省去代码若干-->
    <input type='text' name='y.mark'/> <!-- 原料的mark,action中原料参数名为y -->
    ......<!--省去代码若干-->
    <input type='text' name='f.mark'/> <!-- 辅料的mark,action中辅料参数名为f -->
    ......<!--省去代码若干-->
</form>
 

 

可惜, SpringMVC没有这个功能,因此,我便动了修改它源码的念头。经过一番折腾,终于完成了,现将修改方法公布出来,供大家参考:

 

需要修改的Spring类全部都在org.springframework.web.jar包中,我使用的是3.0.6版本。

 

 

第一步,修改org.springframework.web.bind.annotation.support.HandlerMethodInvoker类的

private WebDataBinder resolveModelAttribute(...)方法

private WebDataBinder resolveModelAttribute(String attrName,
		MethodParameter methodParam, ExtendedModelMap implicitModel,
		NativeWebRequest webRequest, Object handler) throws Exception {
        // .......省去若干行
	// 原Spring中为name,应将name改为methodParam.getParameterName()
	WebDataBinder binder = createBinder(webRequest, bindObject, methodParam.getParameterName());
	initBinder(handler, name, binder, webRequest);
	return binder;
}

 

第二步,修改org.springframework.web.bind.ServletRequestParameterPropertyValues类,最后新增两个方法

/**
 * 新增的构造方法,此方法添加了一个ServletRequestDataBinder类型参数,用于获取方法arg名称
 * @param request
 * @param binder
 */
public ServletRequestParameterPropertyValues(ServletRequest request, ServletRequestDataBinder binder){
	// super(WebUtils.getParametersStartingWith(request, (prefix != null ? prefix + prefixSeparator : null)));
	super(getAvailableParams(request, binder));
}

/**
 * 新增方法,此方法内部将target.property提取为property
 * @param request
 * @param binder
 * @return
 */
public static Map<String, Object> getAvailableParams(ServletRequest request, ServletRequestDataBinder binder){
	// 此方法内部优先提取target.property值,之后再取property值
	Map<String, Object> params = new HashMap<String, Object>();
	Enumeration<String> paramNames = request.getParameterNames();
	while (paramNames != null && paramNames.hasMoreElements()) {
		String paramName = (String) paramNames.nextElement(); // parameter名称
		String[] values = request.getParameterValues(paramName);
		// 如果是以target.开始的参数则去除target.
		if(paramName.startsWith(binder.getObjectName()+".")){
			paramName = paramName.substring(paramName.indexOf('.')+1);
		}else{
			if(params.containsKey(paramName))
				continue;
		}
		if (values == null || values.length == 0) {
			// Do nothing, no values found at all.
		} else if (values.length > 1) {
			params.put(paramName, values);
		} else {
			params.put(paramName, values[0]);
		}
	}
	return params;
}

 

第三步,修改org.springframework.web.bind.ServletRequestDataBinder类的

public void bind(ServletRequest request)方法

public void bind(ServletRequest request) {
        // 此处原来为new ServletRequestParameterPropertyValues(request)
	MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request, this);
	MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
	if (multipartRequest != null) {
		bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
	}
	doBind(mpvs);
}

 

大功告成,此时,你便可以将form表单中的同名不同类参数修改为obj.property ,以标明同名的参数分别归哪个对象

 

现将修改之后的org.springframework.web-3.0.6.jar发布出来,如果你使用的是不同版本的Spring,可以采取上面的步骤自己重新打包使用

 

 

谢谢观看,完毕~~~~~

 

 

 

 

 

 

 

 

   发表时间:2012-06-11  
这样改好像侵入性很大,有没有更简单一点或者说更方便一点的方法,在网上也不知道用怎样的关键词去搜索。或者有没有一个配置就可以让spring mvc 变成struts2 那种加上po前缀的那种表单名???
0 请登录后投票
   发表时间:2012-06-11  
yxb1990 写道
这样改好像侵入性很大,有没有更简单一点或者说更方便一点的方法,在网上也不知道用怎样的关键词去搜索。或者有没有一个配置就可以让spring mvc 变成struts2 那种加上po前缀的那种表单名???

说实话这种策略SpringMVC不会默认添加入它的框架中的,因为Struts2的Action中的参数都是以私有变量自动装配的,而SpringMVC的Controller中的参数是以控制方法参数自动装配的。
Struts2的私有变量名可以通过Java反射获取,而SpringMVC的方法参数名却不能直接通过Java反射获取。我只能通过解析class文件获取参数名称。
然而根据Java规范,任何方法的参数名称可以被javac重命名(打开编译优化参数时)。也就是说我一楼的那种方法不是绝对保险的,必须使用常规的java编译策略。而Spring作为开源框架必然也不会约束用户必须使用常规Java编译策略。

再者,如果使用者对Spring和java的了解差不多的话,像一楼那样修改源码应该不会对Spring本身造成影响。
0 请登录后投票
   发表时间:2012-06-12  
我怎么记得spring原本就支持这种绑定方式,包括参数名也是通过内置的asm解析的。也许是我记混了?...
0 请登录后投票
   发表时间:2012-06-12  
mazzystar 写道
我怎么记得spring原本就支持这种绑定方式,包括参数名也是通过内置的asm解析的。也许是我记混了?...


Spring原生支持的是不带pojo.这种请求参数,因此在使用中或多或少没有Struts2支持的pojo.方便。
0 请登录后投票
   发表时间:2012-06-12   最后修改:2012-06-12
做了个FormBean注解解决你的问题

public User save(@Valid @FormBean("user") User user){

    if (hasErrors()) {

        throw new FormBindException(getMergedBindingResults());

    }

    // something

    return user;

}
0 请登录后投票
   发表时间:2012-06-12  
从今天凌晨到现在,过去21个小时了。

我数了数java板块最新的总回复,才22个帖子有新回复

呵呵。ITEYE真是越来越冷清了!!!
0 请登录后投票
   发表时间:2012-06-12  
s929498110 写道
从今天凌晨到现在,过去21个小时了。

我数了数java板块最新的总回复,才22个帖子有新回复

呵呵。ITEYE真是越来越冷清了!!!

人都到哪里去了,最近都没学习的动力了
0 请登录后投票
   发表时间:2012-06-12  
在我印象中是支持obj.prop的呀,好久没用了,明天查下
0 请登录后投票
   发表时间:2012-06-13  

 
这种方式SpringMVC本来就支持!而且SpringMVC还可以绑定List、Map、Set等集合,楼主你可以参考这篇文章

http://www.iteye.com/topic/973918

 

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics