springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
2017-06-07 14:43
936 查看
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
看了说明大致明白,他可以用来定制method参数和返回值。
我这里需要修改的是参数,也就是需要用他的setCustomArgumentResolvers方法。
参数List类型的泛型是HandlerMethodArgumentResolver,我需要有一个类去实现他。
实现类:
supportsParameter里判断参数是否满足我的Resolver。
而在resolveArgument里我们可以处理这些参数。
另外,FormModel就是我创建的annotation。
另外,我需要将他配置到spring context中。
或者中配置的方式:
剩下的就是去使用他,controller中:
页面中:
参考:http://blog.csdn.net/truong/article/details/30971317
DefaultAnnotationHandlerMapping和 AnnotationMethodHandlerAdapter是spring mvc 支持类注解和方法级别注解的两个处理类
DefaultAnnotationHandlerMapping用来解析Spring MVC里面的annotation对应的Controller,也就是通过这个类,给annotation设置映射关系,如@RequestMapping等
AnnotationMethodHandlerAdapter
对Annotation设置的方法进行处理的类,通过此类,解析annotation设置的类的处理,也就是有请求时,通过此类,可以调用annotation设置controller的方法,主要处理方法
一般配置为:
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
遇到的问题:
我有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
看了说明大致明白,他可以用来定制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; }
参数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; } }
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) @Documented public @interface FormModel { String value(); }
另外,我需要将他配置到spring context中。
<!--自定义controller接受参数进行解析 --> <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="com.common.util.FormObjectMethodArgumentsResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
或者中配置的方式:
<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>
剩下的就是去使用他,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; }
页面中:
<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>
参考:http://blog.csdn.net/truong/article/details/30971317
问题点:
1.为什么开始要讲RequestMappingHandlerAdapterDefaultAnnotationHandlerMapping和 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" />
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
相关文章推荐
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- SpringMVC 使用HandlerMethodArgumentResolver自定义解析器实现请求数据绑定方法入参
- 通过实现HandlerMethodArgumentResolver接口,给springMvc的Controller的方法注入自定义参数
- springMVC的自定义参数解析器HandlerMethodArgumentResolver的使用
- SpringMVC之分析HandlerMethodArgumentResolver请求对应处理器方法参数的解析过程(一)
- 使用HandlerMethodArgumentResolver接口自定义Spring MVC的参数接受规则
- Spring MVC HandlerMethodArgumentResolver 自定义参数解析器
- springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)
- springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)
- SpringMVC自定义注解并自定义解析器HandlerMethodArgumentResolver
- SpringMVC中的参数组装:HandlerMethodArgumentResolver
- HandlerMethodArgumentResolver 自定义封装参数bean
- SpringMVC源码总结(十)自定义HandlerMethodArgumentResolver