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

  • 相关阅读:
    HAProxy、Keepalived 在 Ocatvia 的应用实现与分析
    Octavia 的 HTTPS 与自建、签发 CA 证书
    Octavia 创建 loadbalancer 的实现与分析
    OpenStack Rally 质量评估与自动化测试利器
    自建 CA 中心并签发 CA 证书
    Failed building wheel for netifaces
    通过 vSphere WS API 获取 vCenter Datastore Provisioned Space 置备空间
    OpenStack Placement Project
    我们建了一个 Golang 硬核技术交流群(内含视频福利)
    没有图形界面的软件有什么用?
  • 原文地址:https://www.cnblogs.com/softidea/p/5907316.html
Copyright © 2011-2022 走看看