1.前言
在上一篇文章中,我們留下了好幾個(gè)點(diǎn)沒(méi)有分析,其中有一個(gè)就是HandlerMethodArgumentResolver類,這個(gè)類呢,就是用于處理方法的參數(shù)解析。用于比如@RequestBody,@RequestParam以及我們自定義等的參數(shù)處理。
2.例子
我們這里有一個(gè)例子,根據(jù)請(qǐng)求頭是否有jiang來(lái)鑒權(quán),沒(méi)有就拋出異常。這里例子比較簡(jiǎn)單,如果真正業(yè)務(wù)場(chǎng)景的話,可能就會(huì)做更多事情。
1.注解
就是一個(gè)簡(jiǎn)單的@CurrentUser
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
}
2.解析器定義
參數(shù)解析器有兩個(gè)方法:
1.supportsParameter:用于判斷是否該解析器支持解析
2.resolveArgument:當(dāng)解析器支持的話,就會(huì)調(diào)用該方法解析參數(shù)對(duì)象
這里的解析就是判斷請(qǐng)求頭有沒(méi)有帶有name是jiang的參數(shù),有就返回
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String name = request.getHeader("name");
if (!"jiang".equals(name)) {
throw new RuntimeException("鑒權(quán)錯(cuò)誤");
}
return name;
}
}
3.配置解析器
下面的代碼也比較簡(jiǎn)單,主要是注入了一個(gè)WebMvcConfigurer的Bean,這個(gè)Bean重寫了addArgumentResolvers,將我們的CurrentUserArgumentResolver參數(shù)解析器加入了。
@Slf4j
@Configuration
public class BootWebConfiguration {
@Bean
WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CurrentUserArgumentResolver());
}
};
}
}
3.參數(shù)解析器加載流程
上面我們看到了只要注入一個(gè)WebMvcConfigurer,然后里面實(shí)現(xiàn)方法即可讓這個(gè)參數(shù)解析器生效。那這個(gè)解析器又是如何被使用生效的呢,下面開始分析
1.SpringBoot對(duì)SpringMVC的加載
眾所周知springboot的對(duì)各種組件,類似redis,kafka等都是用starter的模式,通過(guò)spring.factories文件來(lái)加載。
SpringMVC中也不例外。
在org.springframework.boot:spring-boot-autoconfigure包中的spring.factories。
# .......
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
# ......
上面有很多AutoConfiguration,其中包括DispatcherServlet的,以及ServletWebServerFactory用于加載tomcat,還是jetty容器等等。
其中WebMvcAutoConfiguration就是我們啟動(dòng)的主角了。
2.WebMvcAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
// *****
}
先看定義,這有其中有一個(gè)要求是@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),也就是如果我們自定義了WebMvcConfigurationSupport類型的Bean,那這個(gè)WebMvcAutoConfiguration就不會(huì)生效了。
所以這里也涉及到了一個(gè)常見的錯(cuò)誤,很多人要加請(qǐng)求攔截器,或者啥的都繼承WebMvcConfigurationSupport,然后重寫里面的方法,這樣寫是不太規(guī)范的,最好是像我們上面的一樣,注入WebMvcConfigurer來(lái)實(shí)現(xiàn)。
再繼續(xù)看里面的代碼,會(huì)發(fā)現(xiàn)有個(gè)內(nèi)部的Bean
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
// 注入RequestMappingHandlerAdapter
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
// 注入RequestMappingHandlerMapping
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}
這里會(huì)注入RequestMappingHandlerAdapter,以及RequestMappingHandlerMapping。根據(jù)上兩篇文章分析,這兩個(gè)Bean,就是用于我們@RequestMapping的這種形式的處理hander,以及handlerAdapter。
我們既然是分析對(duì)參數(shù)的解析,那自然是要看RequestMappingHandlerAdapter這個(gè)在請(qǐng)求流程中至關(guān)重要Bean的注入了。
我們看到它調(diào)用了super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);
我們先看一下EnableWebMvcConfiguration它的類繼承關(guān)系

這里可以看到DelegatingWebMvcConfiguration這個(gè)類其實(shí)是個(gè)中間層的代理類。它有個(gè)函數(shù)setConfigurers,這里會(huì)將所有的WebMvcConfigurer都注入進(jìn)來(lái),然后加入到WebMvcConfigurerComposite。
然后其他的方法,就是挨個(gè)調(diào)用WebMvcConfigurerComposite的WebMvcConfigurer來(lái)處理。
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
// WebMvcConfigurer的組合類
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
// ******
接著往上看它的父類WebMvcConfigurationSupport,它的東西就是核心了。
3.WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
// 創(chuàng)建RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
// ...
return mapping;
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
return adapter;
}
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
}
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
}
}
上面我們可以看到,它實(shí)現(xiàn)了創(chuàng)建RequestMappingHandlerMapping, RequestMappingHandlerAdapter,同時(shí)還留了addArgumentResolvers,addReturnValueHandlers這種接口給子類去實(shí)現(xiàn)。
那我們先研究RequestMappingHandlerAdapter,因?yàn)檫@里可以看到有一句,adapter.setCustomArgumentResolvers(getArgumentResolvers()); 用于設(shè)置參數(shù)解析器。
同時(shí)這里也是有adapter.setCustomReturnValueHandlers(getReturnValueHandlers());用于設(shè)置返回值處理器。
這里我們先看參數(shù)解析器的邏輯,其實(shí)和設(shè)置返回值處理器的邏輯是一致的。
發(fā)現(xiàn)會(huì)調(diào)用getArgumentResolvers
protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {
if (this.argumentResolvers == null) {
this.argumentResolvers = new ArrayList<>();
addArgumentResolvers(this.argumentResolvers);
}
return this.argumentResolvers;
}
第一次的話這個(gè)argumentResolvers肯定是空的,所以會(huì)調(diào)用addArgumentResolvers,注意,這里是傳了一個(gè)List進(jìn)去,意味著如果要增加參數(shù)解析器只需要向這里面添加就好了,而這個(gè)剛好是由DelegatingWebMvcConfiguration實(shí)現(xiàn)了。
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}
在看configurers的addArgumentResolvers
lass WebMvcConfigurerComposite implements WebMvcConfigurer {
// Web配置類列表
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.delegates.addAll(configurers);
}
}
// 遍歷WebMvcConfigurer,調(diào)用addArgumentResolvers
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addArgumentResolvers(argumentResolvers);
}
}
// 遍歷WebMvcConfigurer,調(diào)用addReturnValueHandlers
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addReturnValueHandlers(returnValueHandlers);
}
}
這個(gè)XXXComposite模式是不是有點(diǎn)似曾相似,沒(méi)錯(cuò),前面HandlerMethodArgumentResolverComposite啥的,都是和這個(gè)一個(gè)套路,將所有的WebMvcConfigurer配置類都組合到一起,做增強(qiáng)處理,同時(shí)自己也繼承了WebMvcConfigurer。
而這個(gè)configurers在DelegatingWebMvcConfiguration.setConfigurers就將Spring中的所有WebMvcConfigurer都注入了。
所以,當(dāng)調(diào)用WebMvcConfigurationSupport.getArgumentResolvers的時(shí)候,就會(huì)調(diào)用到DelegatingWebMvcConfiguration的addArgumentResolvers,然后調(diào)用到我們自己的代碼:
@Bean
WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor());
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CurrentUserArgumentResolver());
}
};
}
最終將這個(gè)參數(shù)解析器放入到WebMvcConfigurationSupport的argumentResolvers中。
那么到這里,我們已經(jīng)將我們自己定義的參數(shù)解析器放入到了處理器適配器RequestMappingHandlerAdapter的customArgumentResolvers中了。
4.RequestMappingHandlerAdapter中的參數(shù)解析器
上面的第三步,我們已經(jīng)將CurrentUserArgumentResolver這個(gè)自定義的參數(shù)解析器放到了RequestMappingHandlerAdapter的customArgumentResolvers中了。接下來(lái),這個(gè)RequestMappingHandlerAdapter就要初始化了。
可以看到RequestMappingHandlerAdapter是實(shí)現(xiàn)InitializingBean,所以會(huì)進(jìn)入afterPropertiesSet方法
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
// 參數(shù)解析器
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 返回值處理器
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
這里有g(shù)etDefaultArgumentResolvers(),然后獲取出來(lái)了以后,創(chuàng)建一個(gè)HandlerMethodArgumentResolverComposite,賦值給argumentResolvers
可以看下這個(gè)getDefaultArgumentResolvers的代碼:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// @RequestParam的解析
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
// @PathVariable的解析
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
// @RequestBody的解析
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
// .....
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;
}
這里可以看到其實(shí)默認(rèn)的參數(shù)解析器是有很多的,比如處理@RequestParam的,@PathVariable的,以及@RequestBody的,我們的自定義會(huì)在getCustomArgumentResolvers()取出,然后加入進(jìn)去。返回值參數(shù)處理器,也是類似的。
所以這里也有小的知識(shí)點(diǎn):就是我們自己定義的參數(shù)解析器順序是比較靠后的,在參數(shù)解析的時(shí)候,都是優(yōu)先遍歷放在前面的參數(shù)解析器。
最終我們將所有的參數(shù)解析器都放在了RequestMappingHandlerAdapter的argumentResolvers中了。
至此,我們的初始化就以及完成了,后面就是請(qǐng)求進(jìn)來(lái)后,找解析器的工作了。
5.使用參數(shù)解析器
當(dāng)前面初始化好了,也可以接受請(qǐng)求的時(shí)候,這個(gè)時(shí)候請(qǐng)求進(jìn)來(lái)了。
根據(jù)之前的流程分析,這個(gè)請(qǐng)求會(huì)從DispatcherSerlvet有的doDispatch一直調(diào)用,然后到RequestMappingHandlerAdapter的invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 設(shè)置參數(shù)解析器到`ServletInvocableHandlerMethod `
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 設(shè)置返回值處理器到`ServletInvocableHandlerMethod `
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 調(diào)用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
}
上面的代碼可以看到這個(gè)參數(shù)解析器被設(shè)置到了ServletInvocableHandlerMethod中,這個(gè)類我們之前也分析了。
它就是一個(gè)可以對(duì)這次請(qǐng)求進(jìn)行參數(shù)解析,調(diào)用處理方法,最后對(duì)返回值解析的類。
然后我們一路跟進(jìn)到invokeForRequest,再到getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
return args;
}
在這它用resolvers變量,這個(gè)變量就是之前的RequestMappingHandlerAdapter的argumentResolvers,然后他就調(diào)用每個(gè)參數(shù)解析器的supportsParameter,判斷是否支持,支持了就調(diào)用resolveArgument來(lái)獲得這個(gè)解析后的參數(shù)。
所以到這里,我們就分析完成了HandlerMethodArgumentResolver是如何加載,以及如何被使用的。
6.總結(jié)
1.SpringBoot加載WebMvcAutoConfiguration
2.調(diào)用EnableWebMvcConfiguration,然后調(diào)用父類的DelegatingWebMvcConfiguration,將自定義配置WebMvcConfigurer存放到WebMvcConfigurerComposite
3.調(diào)用requestMappingHandlerAdapter,然后調(diào)用到頂級(jí)父類WebMvcConfigurationSupport的requestMappingHandlerAdapter來(lái)創(chuàng)建這個(gè)RequestMappingHandlerAdapter
4.調(diào)用RequestMappingHandlerAdapter的getArgumentResolvers,然后會(huì)調(diào)用DelegatingWebMvcConfiguration里面的WebMvcConfigurerComposite,最終回調(diào)自定義的WebMvcConfigurer,將HandlerMethodArgumentResolver參數(shù)解析器加入到RequestMappingHandlerAdapter中
5.請(qǐng)求進(jìn)來(lái)后,從DispatcherSerlvet一直調(diào)用到RequestMappingHandlerAdapter的invokeHandlerMethod,
然后創(chuàng)建了一個(gè)ServletInvocableHandlerMethod,并且將WebMvcConfigurerComposite放到它的里面
6.到ServletInvocableHandlerMethod的getMethodArgumentValues里,將處理方法的所以參數(shù)都取出來(lái),循環(huán)遍歷判斷參數(shù)解析器是否支持,支持的話就解析
7.后續(xù)
前面可以看到了這個(gè)HandlerMethodArgumentResolver是有非常多的種類的,后續(xù)我們會(huì)對(duì)這些種類進(jìn)行一些分析,特別是RequestResponseBodyMethodProcessor,這個(gè)是比較復(fù)雜的,它不僅把參數(shù)解析器實(shí)現(xiàn)了,還把返回值處理器也給實(shí)現(xiàn)了,內(nèi)部用HttpMessageConverter,實(shí)現(xiàn)了多種數(shù)據(jù)轉(zhuǎn)換器,其中內(nèi)置包含了String,gson,Jackson等等的。