springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数

来源:互联网 发布:java files.write 编辑:程序博客网 时间:2024/05/22 04:48

springMVC 数据绑定 多个对象 如何准确绑定?

遇到的问题:
我有Person和Cat两个类,他们都有name这个field,如果我有一个Controller的方法接收Cat和Person两个参数,我应该如何分别他们的name?
话说在页面写person.name和cat.name是没什么意义的,于是我看了一下stackOverFlow。
有人推荐我写一个类,并给这个类里增加Person和Cat类型的变量,这样就可以在页面里写person.name和cat.name以作区分了。
虽然解决问题,但并不高雅。
于是我找到了另一个方法——给method参数加annotation,使用HandlerMethodArgumentResolver。

SpringMVC3.1引入了HandlerMethodArgumentResolver接口,spring调用该接口实现Controller的参数装配。HandlerMethodArgumentResolver实现类中会调用DataBinder,Converter等。

常用的该接口实现类有:

ServletModelAttributeMethodProcessor:实体类的组装用它实现。
RequestParamMethodArgumentResolver:基本数据类型如String用它实现。

在我学习过程中,发现对List类型的参数SpringMVC没有提供默认实现。我参照Spring的示例 通过实现 HandlerMethodArgumentResolver接口,实现对List参数的组装

那么在说HandlerMethodArgumentResolver接口实现之前,要先说一个类:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

/*** An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s* with the signature -- method argument and return types, defined in* {@code @RequestMapping}.** <p>Support for custom argument and return value types can be added via* {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.* Or alternatively to re-configure all argument and return value types use* {@link #setArgumentResolvers} and {@link #setReturnValueHandlers(List)}.** @author Rossen Stoyanchev* @since 3.1* @see HandlerMethodArgumentResolver* @see HandlerMethodReturnValueHandler*/public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,InitializingBean
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

看了说明大致明白,他可以用来定制method参数和返回值。
我这里需要修改的是参数,也就是需要用他的setCustomArgumentResolvers方法。

/*** Provide resolvers for custom argument types. Custom resolvers are ordered* after built-in ones. To override the built-in support for argument* resolution use {@link #setArgumentResolvers} instead.*/public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {this.customArgumentResolvers = argumentResolvers;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

参数List类型的泛型是HandlerMethodArgumentResolver,我需要有一个类去实现他。
实现类:

package ;import java.lang.annotation.Annotation;import java.util.Map;import javax.servlet.ServletRequest;import org.apache.commons.lang.StringUtils;import org.springframework.beans.BeanUtils;import org.springframework.core.MethodParameter;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.validation.BindException;import org.springframework.validation.Errors;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.ServletRequestDataBinder;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.support.WebDataBinderFactory;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.method.support.ModelAndViewContainer;import com.maigangle.erp.common.annotation.FormModel;/** * 解析obj.xx *  * *  * @version 1.0 * */public class FormObjectMethodArgumentsResolver implements HandlerMethodArgumentResolver {    public boolean supportsParameter(MethodParameter parameter) {        return parameter.hasParameterAnnotation(FormModel.class);    }    public Object resolveArgument(  MethodParameter parameter,                                    ModelAndViewContainer mavContainer,                                    NativeWebRequest webRequest,                                    WebDataBinderFactory binderFactory) throws Exception {        FormModel multi = parameter.getParameterAnnotation(FormModel.class);        String name = StringUtils.isEmpty(multi.value()) ? parameter.getParameterName() : multi.value();// 参数名:默认去@Multi的value值                                                                                                        // 如果是""则去获取参数值的命名变量        // String name = ModelFactory.getNameForParameter(parameter);        Object attribute = (mavContainer.containsAttribute(name)    ? mavContainer.getModel().get(name)                                                                    : createAttribute(name, parameter, binderFactory, webRequest));        if (!mavContainer.isBindingDisabled(name)) {            ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);            if (ann != null && !ann.binding()) {                mavContainer.setBindingDisabled(name);            }        }        WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);        if (binder.getTarget() != null) {            if (!mavContainer.isBindingDisabled(name)) {                bindRequestParameters(binder, webRequest, name);            }            validateIfApplicable(binder, parameter);            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {                throw new BindException(binder.getBindingResult());            }        }        // Add resolved attribute and BindingResult at the end of the model        Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();        mavContainer.removeAttributes(bindingResultModel);        mavContainer.addAllAttributes(bindingResultModel);        return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);    }    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request, String parameterName) {        ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);        ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;        // servletBinder.setFieldDefaultPrefix(servletBinder.getObjectName()+".");        servletBinder.setFieldDefaultPrefix(parameterName + ".");        servletBinder.bind(servletRequest);    }    protected Object createAttribute(String attributeName, MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request)            throws Exception {        return BeanUtils.instantiateClass(methodParam.getParameterType());    }    protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {        Annotation[] annotations = methodParam.getParameterAnnotations();        for (Annotation ann : annotations) {            Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);            if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {                Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));                Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});                binder.validate(validationHints);                break;            }        }    }    protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {        int i = methodParam.getParameterIndex();        Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();        boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));        return !hasBindingResult;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

supportsParameter里判断参数是否满足我的Resolver。
而在resolveArgument里我们可以处理这些参数。
另外,FormModel就是我创建的annotation。

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** *  *  *  * @version 1.0 **/@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FormModel {    String value();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

另外,我需要将他配置到spring context中。

    <!--自定义controller接受参数进行解析  -->    <mvc:annotation-driven>        <mvc:argument-resolvers>            <bean                class="com.common.util.FormObjectMethodArgumentsResolver" />        </mvc:argument-resolvers>    </mvc:annotation-driven>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

或者中配置的方式:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="synchronizeOnSession" value="true" /><property name="customArgumentResolvers"><list><bean class="com.common.util.FormObjectMethodArgumentsResolver" /></list></property></bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

剩下的就是去使用他,controller中:

@RequestMapping(value="/index")public ModelAndView index(@FormModel("p")Person p){ModelAndView tmpMAV = new ModelAndView("index");System.out.println(p);tmpMAV.addObject("p.name",p.getName());return tmpMAV;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

页面中:

<form action="index" method="post"><input type="text" name="p.name" /><input type="text" name="name" /><input type="text" name="p.age" /><input type="text" name="weight" /><input type="text" name="p.height" /><input type="submit" value="提交"/></form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

参考:http://blog.csdn.net/truong/article/details/30971317

问题点:

1.为什么开始要讲RequestMappingHandlerAdapter

DefaultAnnotationHandlerMapping和 AnnotationMethodHandlerAdapter是spring mvc 支持类注解和方法级别注解的两个处理类
DefaultAnnotationHandlerMapping用来解析Spring MVC里面的annotation对应的Controller,也就是通过这个类,给annotation设置映射关系,如@RequestMapping等
AnnotationMethodHandlerAdapter
对Annotation设置的方法进行处理的类,通过此类,解析annotation设置的类的处理,也就是有请求时,通过此类,可以调用annotation设置controller的方法,主要处理方法
一般配置为:

<!-- 处理在类级别上的@RequestMapping注解 --><bean    class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">    <property name="interceptors">    <list>        <!-- 多个拦截器,顺序执行 -->        <ref bean="SpringMVCInterceptor" />        <ref bean="OpenSessionInViewInterceptor"/>    </list>    </property></bean><!-- 处理方法级别上的@RequestMapping注解 --><bean id="annotationMethodHandlerAdapter"    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">    <property name="messageConverters">    <list>        <bean            class="org.springframework.http.converter.StringHttpMessageConverter">            <property name="supportedMediaTypes">            <list>                <value>text/html;charset=utf-8</value>                <value>text/plain;charset=utf-8</value>            </list>            </property>        </bean>        <bean            class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverterv2">            <property name="objectMapper">                <bean class="net.pm.misc.Hibernate4AwareObjectMapper" />            </property>        </bean>    </list>    </property></bean><!-- 表示使用cglib,而非JDK的动态代理,因为Controller没有实现接口,所以要配置这里 --><aop:aspectj-autoproxy proxy-target-class="true"  />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

xml配置中annotation-driven和DefaultAnnotationHandlerMapping/AnnotationMethodHandlerAdapter的关系

Spring 3.0.x中使用了annotation-driven后,缺省使用DefaultAnnotationHandlerMapping 来注册handler method和request的mapping关系。 AnnotationMethodHandlerAdapter来在实际调用handlermethod前对其参数进行处理。

在spring mvc 3.1中,对应变更为
DefaultAnnotationHandlerMapping -> RequestMappingHandlerMapping
AnnotationMethodHandlerAdapter -> RequestMappingHandlerAdapter
AnnotationMethodHandlerExceptionResolver -> ExceptionHandlerExceptionResolver

以上都在使用了annotation-driven后自动注册。
而且对应分别提供了AbstractHandlerMethodMapping , AbstractHandlerMethodAdapter和 AbstractHandlerMethodExceptionResolver以便于让用户更方便的实现自定义的实现类。

相当于注册了DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter两个bean,配置一些messageconverter。即解决了@Controller注解的使用前提配置。
这两个功能相似,不能一起使用
参考:http://starscream.iteye.com/blog/1098880

2.FormModel类上的注解都是什么意思
@Target,@Retention,@Documented详解
要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。

1、元注解(meta-annotation):

  元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。
 @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention:

  @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

详情参考:http://www.cnblogs.com/gmq-sh/p/4798194.html

阅读全文
0 0
原创粉丝点击