org.springframework.web.bind.ServletRequestDataBinde


org.springframework.validation

Class DataBinder

 

 

 

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 com.labofjet.web.ContextDTO@9344568[stateCode=91,data=<null>,exception=com.labofjet.exception.BaseException: error]
2 com.labofjet.dto.ADTO@814d736[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,做法是:

復制代碼
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     }
復制代碼

也就是說:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM