天天看點

org.springframework.web.bind.ServletRequestDataBinde方法:利用其它的HandlerMethodArgumentResolver方法:利用@InitBinder注解方法N:自己實作HandlerMethodArgumentResolver簡單總結

org.springframework.validation

Class DataBinder

  • java.lang.Object
    • org.springframework.validation.DataBinder
  • All Implemented Interfaces:
    PropertyEditorRegistry,  TypeConverter
    Direct Known Subclasses:
    WebDataBinder
    public class DataBinder
    extends Object
    implements PropertyEditorRegistry, TypeConverter      

    Binder that allows for setting property values onto a target object, including support for validation and binding result analysis. The binding process can be customized through specifying allowed fields, required fields, custom editors, etc.

    Note that there are potential security implications in failing to set an array of allowed fields. In the case of HTTP form POST data for example, malicious clients can attempt to subvert an application by supplying values for fields or properties that do not exist on the form. In some cases this could lead to illegal data being set on command objects or their nested objects. For this reason, it is highly recommended to specify the 

    allowedFields

     property on the DataBinder.

    The binding results can be examined via the 

    BindingResult

     interface, extending the 

    Errors

     interface: see the 

    getBindingResult()

     method. Missing fields and property access exceptions will be converted to

    FieldErrors

    , collected in the Errors instance, using the following error codes:
    • Missing field error: "required"
    • Type mismatch error: "typeMismatch"
    • Method invocation error: "methodInvocation"
    By default, binding errors get resolved through the 

    BindingErrorProcessor

     strategy, processing for missing fields and property access exceptions: see the

    setBindingErrorProcessor(org.springframework.validation.BindingErrorProcessor)

     method. You can override the default strategy if needed, for example to generate different error codes.

    Custom validation errors can be added afterwards. You will typically want to resolve such error codes into proper user-visible error messages; this can be achieved through resolving each error via a

    MessageSource

    , which is able to resolve an 

    ObjectError

    /

    FieldError

     through its 

    MessageSource.getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)

     method. The list of message codes can be customized through the 

    MessageCodesResolver

     strategy: see the 

    setMessageCodesResolver(org.springframework.validation.MessageCodesResolver)

     method.

    DefaultMessageCodesResolver

    's javadoc states details on the default resolution rules.

    This generic data binder can be used in any kind of environment. It is typically used by Spring web MVC controllers, via the web-specific subclasses 

    ServletRequestDataBinder

     and

    PortletRequestDataBinder

    .
    Author:
    Rod Johnson, Juergen Hoeller, Rob Harrop, Stephane Nicoll

at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler.setValue(BeanWrapperImpl.java:344)

at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:452)

at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:278)

at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95)

at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:810)

at org.springframework.validation.DataBinder.doBind(DataBinder.java:706)

at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:189)

at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:106)

at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:150)

at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:110)

at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)

at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)

at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)

at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)

at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:799)

at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:728)

at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)

at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)

at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)

at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969)

at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:860)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)

at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

at com.global.filter.AddExtraToParamsFilter.doFilter(AddExtraToParamsFilter.java:27)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)

at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)

at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)

at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)

at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)

at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)

at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)

at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)

at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)

at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)

at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)

at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)

at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)

- locked <0x14a4> (a org.apache.tomcat.util.net.NioChannel)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

at java.lang.Thread.run(Thread.java:745)

增加的就是第110行

1 binder.setFieldDefaultPrefix(parameter.getParameterName() + "!");      

parameter.getParameterName()傳回的是你@Controller裡2RequestMapping方法參數的名字

"!"是我區分成員域與對象名的分解符...這個可以自己設定.你想設定成.也可以!也可以#也OK

隻要自己能區分就行了.

測試

URL:

1 http://localhost:8080/quick-start/test18?context!stateCode=91&a!name=hehe&context!exception.message=error&a!stateCode=84      

背景列印參數:

1 [email protected][stateCode=91,data=<null>,exception=com.labofjet.exception.BaseException: error]
2 [email protected][id=<null>,name=hehe,age=<null>,value=<null>,b=0,stateCode=84]      

Controller的方法簽名:

1     @RequestMapping("/test18")
2     public Object index18(ContextDTO context, ADTO a) throws IOException {
3         System.out.println(context);
4         System.out.println(a);
5         return null;
6     }      

原理

先簡明說下原理..具體的理論我想後面等我整理下思路,介紹RequestMappingHandlerAdapter的時候再講(反正沒人看...)

SpringMVC裡@Controller裡的各種參數由各種argumentsResolver來解析.

解析自定義的這種DTO的argumentsResolver是ServletModelAttributeMethodProcessor這個類.

收到參數以後他怎麼解析呢?

很簡單呀.在我測試例子中,比如我要初始化一個context對象,SpringMVC就先把context包裝成BeanWrapperImpl對象..然後把Request裡的各種key-value包裝成MutablePropertyValues..然後set進去就可以了.利用的是反射的知識,你有一個key,那我就看BeanWrapperImpl warp的那個對象有沒有對應的屬性.有就設定進去.

既然原理是這樣,那怎麼達到我們的目的呢?

這裡又有至少2種方法:

第一種就是像我那樣: binder.setFieldDefaultPrefix(parameter.getParameterName() + "!"); 這樣在解析MutablePropertyValues的時候會去掉你set進去的Feild的Prefix.

第二種方法: ServletRequestDataBinder在綁定的參數的時候需要先把request轉化成MutablePropertyValues,做法是:

org.springframework.web.bind.ServletRequestDataBinde方法:利用其它的HandlerMethodArgumentResolver方法:利用@InitBinder注解方法N:自己實作HandlerMethodArgumentResolver簡單總結
1     public void bind(ServletRequest request) {
2         MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
3         MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
4         if (multipartRequest != null) {
5             bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
6         }
7         addBindValues(mpvs, request);
8         doBind(mpvs);
9     }      
org.springframework.web.bind.ServletRequestDataBinde方法:利用其它的HandlerMethodArgumentResolver方法:利用@InitBinder注解方法N:自己實作HandlerMethodArgumentResolver簡單總結

也就是說:

1 MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);      

而MutablePropertyValues 其實還有一種構造方法:

1     public ServletRequestParameterPropertyValues(ServletRequest request, String prefix) {
2         this(request, prefix, DEFAULT_PREFIX_SEPARATOR);
3     }      

這種方法允許你填入一個prefix...那原理和前面是一樣的..

但是這種方法需要改很多類...是以沒有方法一簡單....

為什麼不使用繼承

可能會有朋友想問.改進的ArgumentResolver和原本Spring自帶的基本一樣,隻是多了一步,為什麼不繼承自原本的ServletModelAttributeMethodProcessor? 畢竟修改源碼方案不太好.

原因有很多,主要有2個原因:

原因1:

ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor

resolveArgument在ModelAttributeMethodProcessor裡的定義是:

public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
...........
}      

是final啊!final!!!!!!!!!!!

是以我修改不了.

原因2:

一般父類定義了一個處理流程的話不能修改的話,會在子類給我們留一個擴充接口...

沒錯,那就是:

1     @Override
2     protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
3         ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
4         ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
5         servletBinder.bind(servletRequest);
6     }      

這個是在ServletModelAttributeMethodProcessor裡覆寫了父類的方法,我們可以繼承ServletModelAttributeMethodProcessor再覆寫這個bindRequestParameters方法..

但是......

這裡隻有2個參數啊!!!!我們需要的@Controller的參數名稱的資訊不在這裡....我們需要這個變量:

1 public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
2             NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
3 .............
4 }      

參數的資訊都在這裡裡面..可是bindRequestParameters方法裡沒有傳入這個參數...是以坑爹的是我們在bindRequestParameters裡并不能知道@Controller裡參數的名字到底是什麼...

是以我們沒有辦法設定一個通用的綁定方法...

方法:利用其它的HandlerMethodArgumentResolver

具體方法

不改源碼最簡單的方法可能是不自己寫ArgumentResolver,而是利用SpringMVC原有的Resolver了吧..

我們可以利用RequestResponseBodyMethodProcessor這個ArgumentResolver..

具體請參考我的另外一篇文章:

http://www.cnblogs.com/abcwt112/p/5169250.html

原理就是利用HttpMessageConverter和其它的json轉化工具将request裡的參數轉化成java bean.這也是很簡單的.

基本隻要在參數前加一個@RequestBody...

方法:利用@InitBinder注解

具體:

請大家看這篇文章:

http://jinnianshilongnian.iteye.com/blog/1888474

缺點:

1.原理和方法:改源碼是差不多的....都是通過修改binder設定額外屬性來達到目的的,但是沒傳入MethodParameter parameter,是以還是不知道你的@Controller裡的參數名字..隻能手動指定字首

2.貌似要綁定的時候每個Controller裡都要寫@InitBinder,稍微有點麻煩..當然好處是更靈活...

方法N:自己實作HandlerMethodArgumentResolver

這個方法就太多了......

請參考:

http://jinnianshilongnian.iteye.com/blog/1717180

簡單總結

方法有太多太多了..不同方法可能适合不同場景,但是我覺得最簡單的還是@InitBinder和@RequestBody這2種方案.

http://www.cnblogs.com/abcwt112/p/5302469.html