前言:

  这两天开发遇到一个需求,那就是在后台接收到请求后,能不能自定义添加参数呢?

  我们知道request中是能获取到前端获取的参数的,但是在后端不能再往parameter中添加参数了,虽然可以使用request.setAttribute()方法往request中存放数据,但是这些数据springmvc在解析request中的数据时是不会解析的;

  而且,如果我们想对前端传来的数据做处理这个方法也是无法实现的,我查看了一下网上的资料,并没有发现springmvc有预留这样的接口可以扩展,所以,我就考虑自己来处理一下它吧;

1,先找到springmvc是如何获取参数的

  首先,要找到springmvc是在哪获取参数的,于是我们通过debug模式查找一次请求中,参与处理的类有哪些,终于,我们找到了这样一个类:org.springframework.web.util.WebUtil,这个类中有这样一个方法:

 
public static Map<String, Object> getParametersStartingWith(ServletRequest request, @Nullable String prefix) {
        Assert.notNull(request, "Request must not be null");
        Enumeration<String> paramNames = request.getParameterNames();
        Map<String, Object> params = new TreeMap<>();
        if (prefix == null) {
            prefix = "";
        }
        while (paramNames != null && paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            if ("".equals(prefix) || paramName.startsWith(prefix)) {
                String unprefixed = paramName.substring(prefix.length());
                String[] values = request.getParameterValues(paramName);
                if (values == null || values.length == 0) {
                    // Do nothing, no values found at all.
                }
                else if (values.length > 1) {
                    params.put(unprefixed, values);
                }
                else {
                    params.put(unprefixed, values[0]);
                }
            }
        }
        return params;
    }

springmvc就是使用这个方法获取到request中所有的参数,那么有读者就问了,直接重写一下这个方法不就得了,问题就解决啦;然而我得说,如果我们还想继续用springmvc,那么就不要这么想,因为调用这个方法是这样调用得:

1 public ServletRequestParameterPropertyValues(
2             ServletRequest request, @Nullable String prefix, @Nullable String prefixSeparator) {
3 
4         super(WebUtils.getParametersStartingWith(
5                 request, (prefix != null ? prefix + prefixSeparator : null)));
6     }

调用WebUtils类得位置

这是一个静态方法,因此也不需要声明实例,所以我们没办法自己重写WebUtils类以后再交由这个类来调用,因此我们要找到可以注入得地方下手来做这件事,所以我就开始找,过程走了不少得弯路,终于,我找到了啦!

2,所有扩展得类(ps:我是使用得@requestMapping来处理的http请求,如果是其他方式,请自行去继承相应的adapter就可以的)

(1)创建一个RequestMappingHandlerAdapter类的子类,并且交给spring去管理,这样才能把这个类注册到dispatchServlet中作为适配器使用

 1 @Component
 2 public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
 3 
 4     protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
 5         return new MyServletInvocableHandlerMethod(handlerMethod);
 6     }
 7 
 8     protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
 9             throws Exception {
10 
11         return new MyWebDataBinderFactory(binderMethods, getWebBindingInitializer());
12     }
13 }

MyRequestMappingHandlerAdapter

(2)创建一个ServletRequestDataBinderFactory类的子类

 1 public class MyWebDataBinderFactory extends ServletRequestDataBinderFactory {
 2     /**
 3      * Create a new instance.
 4      *
 5      * @param binderMethods one or more {@code @InitBinder} methods
 6      * @param initializer   provides global data binder initialization
 7      */
 8     public MyWebDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer initializer) {
 9         super(binderMethods, initializer);
10     }
11 
12     @Override
13     protected ServletRequestDataBinder createBinderInstance(
14             @Nullable Object target, String objectName, NativeWebRequest request) throws Exception  {
15 
16         return new MyExtendedServletRequestDataBinder(target, objectName);
17     }
18 }

MyServletRequestDataBinderFactory

(3)创建一个ExtendedServletRequestDataBinder类的子类

 1 public class MyExtendedServletRequestDataBinder extends ExtendedServletRequestDataBinder {
 2 
 3 
 4     public MyExtendedServletRequestDataBinder(Object target, String objectName) {
 5         super(target, objectName);
 6     }
 7 
 8     public void bind(ServletRequest request) {
 9         MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
10         ((Consumer)request.getAttribute(WebConfig.DEFAULT_PARAM_NAME)).accept(mpvs);
11         MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
12         if (multipartRequest != null) {
13             bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
14         }
15         addBindValues(mpvs, request);
16         doBind(mpvs);
17     }
18 }

MyExtendedServletRequestDataBinder

(4)创建一个注册拦截器的配置类WebMvcConfigurer的子类用来注册拦截器

 1 @Configuration
 2 public class WebConfig implements WebMvcConfigurer {
 3 
 4 
 5     public final static String DEFAULT_PARAM_NAME = "paramDealFunction";
 6 
 7     @Autowired
 8     private PageInterceptor pageInterceptor;
 9 
10 
11     public void addInterceptors(InterceptorRegistry registry) {
12         //注册自定义拦截器,添加拦截路径和排除拦截路径
13         registry.addInterceptor(pageInterceptor).addPathPatterns("/**/page");
14     }
15 
16     @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
17     public DispatcherServlet getDis(){
18         return new MyDispatcherServlet();
19     }
20 }

WebConfig

(5)创建一个拦截器HandlerInterceptor的子类

 1 @Component
 2 public class PageInterceptor implements HandlerInterceptor {
 3 
 4 
 5     @Override
 6     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 7         Consumer<MutablePropertyValues> function = (MutablePropertyValues mpvs) -> mpvs.addPropertyValue("pageStart",mpvs.get("pageNumber"));
 8         request.setAttribute(WebConfig.DEFAULT_PARAM_NAME, function);
 9         return true;
10     }
11 
12 }

View Code

总结一下所有的工作的目的:

(1)设计这个MyRequestMappingHandlerAdapter是为了重写createDataBinderFactory,也就是重新注入DataBinderFactory;

(2)设计这个MyWebDataBinderFactory是为了重写createBinderInstance方法,也就是重新注入一个ServletRequestDataBinder;

(3)设计这个MyExtendedServletRequestDataBinder是为了重写bind方法,为了实现可以自定义处理参数;

其实这三个方法重写已经可以实现了对参数的自定义处理,但是为了和springmvc拦截器整合,所以有新加了pageInterceptor和WebConfig两个类;

好了,创建好这几个类以后,你就可以在PageInterceptor中自行根据业务需求去处理参数啦!如果不关心是怎么找到这个方法的读者可以去改造上面的代码啦

下面上干货,记录我是如何找到这个方法去处理参数的,也是整理一下我自己的思路;

3,整个分析的过程记录

(1)毕竟springmvc还是特别好用的,我还是希望在不影响它任何功能的情况下实现这个处理参数的目的,我发现了实际处理参数的类,改造这个方法是最好的:

然后我找到了调用这个方法的方法,发现是直接调用的,别人的代码不能改,毕竟支持扩展,避免修改

于是找谁调用的这个方法,一定要直到找到可以注入的地方为止,发现下main这个方法就是调的本类的方法,没有用,继续找;

我发现下面这个方法是通过new出来的ServletRequestParameterPropertyValues对象进行获取数据的,哎呀,如果这个地方是可以自定义注入的话,那问题就解决啦,注入一个自定义的子类,然后在这个子类中添加自定义参数的方法,就没问题啦,

版权声明:本文为zhengcj原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhengcj/p/9877558.html