SpringMvc参数处理过程

前言

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

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

从源码角度简单看一下

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

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

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的进行处理、然后会忽略别的处理器。

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方法中

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

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的一个处理方法

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

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的样子,这样我们就可以对参数进行一次处理。

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

@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))
            );
        }
    }
}
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 @ 2022-12-29 17:21  MY1024-  阅读(108)  评论(0编辑  收藏  举报

Wish.
Do.