SpringMvc参数处理过程

前言#

目前遇到了一个需求,想对入参的参数等spring处理完之后进行再次处理,需求产生学习的动力,开始研究源码,spring到底是怎么去处理的

可以看看简化的一个流程图#

从源码角度简单看一下#

首先我们看到springWeb的核心类: org.springframework.web.servlet.DispatcherServlet
我们直接看核心方法 doDispatch

Copy Highlighter-hljs
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... 看到核心的方法,获取到对应的HandlerAdapter、然后进行handler HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ... mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ... }

看到调用了handle方法

通过请求的debug,根据链路发现其中调用的一个核心方法org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

Copy Highlighter-hljs
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 这里会获取方法中的每个入参 MethodParameter[] parameters = getMethodParameters(); 循环进行处理 for (int i = 0; i < parameters.length; i++) { ... 通过对应的resolver进行处理、然后返回对应的处理后的参数 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); ... } }

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

我们看到这个方法、怎么获取resolver的呢

通过下述代码会发现、会从argumentResolvers中取出resolver, 获取到第一个supportsParameter为true的进行处理、然后会忽略别的处理器。

Copy Highlighter-hljs
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }

那argumentResolvers是什么呢?#

回到org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet方法中

会发现在这初始化了一堆参数处理器

Copy Highlighter-hljs
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); if (KotlinDetector.isKotlinPresent()) { resolvers.add(new ContinuationHandlerMethodArgumentResolver()); } // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new PrincipalMethodArgumentResolver()); resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }

有兴趣的可以看看每个参数参数处理器的处理

我们抽其中一个RequestResponseBodyMethodProcessor的处理器来看一下

看到其中的supportsParameter方法、发现他会处理有@RequestBody的参数.

这个方法就是我们常用的@RequestBody的一个处理方法

Copy Highlighter-hljs
public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); }

问题: 那么如果我想对处理完的参数进行处理怎么办?#

在我们分析代码的时候发现spring没有提供前后的切点给我们去处理参数, 那么我们可以怎么做呢?

  1. 可以去看resolver中的resolveArgument方法找切点
  2. 在ArgumentResolvers中找突破口

第一点#

我们还是看RequestResponseBodyMethodProcessor的resolveArgument方法

忽略重重调用找到
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters

Copy Highlighter-hljs
HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); ... body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);

发现提供了2个切点, 我们可以根据这两个切点对参数进行处理

第二点#

一个个参数处理器进行处理很麻烦, 但是我们想做一个通用的、对所有参数都进行处理怎么办呢

通过寻找在RequestMappingHandlerAdapter中发现,argumentResolvers是有一个set方法的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#setArgumentResolvers

那我们在外部包一层, 间接就形成了一个AOP的样子,这样我们就可以对参数进行一次处理。

代码如下,大家可以参考一下

Copy Highlighter-hljs
@Component public class ArgumentResolverBeanProcessor implements InitializingBean { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @Autowired private List<ArgumentResolverAdvice> argumentResolverAdvices; @Override public void afterPropertiesSet() { // 拿到原来的参数处理器 final List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers(); if (!CollectionUtils.isEmpty(argumentResolvers)) { final List<HandlerMethodArgumentResolver> originArgumentResolvers = new ArrayList<>(argumentResolvers); // 重新设置参数处理器为我们的参数处理器、把他原来自带的参数处理器全部传入一个Wrapper、再在内部进行一个分发 // 这样就可以对处理后的一个参数进行一个拦截、然后继续的去操作这个参数 requestMappingHandlerAdapter.setArgumentResolvers( Collections.singletonList(new ArgumentResolverWrapper(originArgumentResolvers, argumentResolverAdvices)) ); } } }
Copy Highlighter-hljs
public class ArgumentResolverWrapper implements HandlerMethodArgumentResolver { private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256); private final List<HandlerMethodArgumentResolver> argumentResolvers; private final ArgumentResolverAdviceChain adviceChain; public ArgumentResolverWrapper(List<HandlerMethodArgumentResolver> argumentResolvers, List<ArgumentResolverAdvice> advices) { this.argumentResolvers = argumentResolvers; this.adviceChain = new ArgumentResolverAdviceChain(advices); } @Override public boolean supportsParameter(@NonNull MethodParameter parameter) { return true; } @Override public Object resolveArgument( @NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory ) throws Exception { final HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException( "Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first." ); } // before final Object arg = resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); // after return arg; } @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; } }

自此我们对参数就可以进行再处理,达到我们需求的目的

Wish.
Do.
#

posted @   MY1024-  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)

Wish.
Do.

点击右上角即可分享
微信分享提示
CONTENTS