zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    拟阵交
    HEOI2021退役记
    退役划水一
    上下界网络流学习笔记
    扩展卢卡斯学习笔记
    扩展中国剩余定理(EXCRT)学习笔记
    插头DP学习笔记
    如何优雅地生成仙人掌图
    Powerful Number 筛学习笔记
    边分治学习笔记
  • 原文地址:https://www.cnblogs.com/softidea/p/5907316.html
Copyright © 2011-2022 走看看