RequestMappingHandlerAdapter含有大量的web基础组件协助完成一整个请求的调度、处理
RequestMappingHandlerAdapter
适配@RequestMapping注解标注的Handler(HandlerMethod类型)
Spring3.1新增的一个适配器类,用于数据绑定、数据转换、数据校验、内容协商
因为有了它的存在,使得开发者几乎可以忘掉原生的Servlet API
Spring 5.0在把Servlet容器从必选项变成可选项后,即使切换了web容器(比如换成jetty),也能做到在使用层面上对开发者无感知
@since 3.1 实现了InitializingBean接口和BeanFactoryAware
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { // 唯一构造方法:默认注册一些消息转换器。开启@EnableWebMvc后此默认行为会被set方法覆盖 public RequestMappingHandlerAdapter() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList<>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); } // 此方法在容器启动的时候会执行:扫描解析容器内的@ControllerAdvice等等 // 看起来代码不多,但其实每个方法内部,都可谓是个庞然大物,请详细观察理解~~~~ @Override public void afterPropertiesSet() { // Do this first, it may add ResponseBody advice beans // 详见下面的解释分析 initControllerAdviceCache(); // 这三大部分,可是自 参数组装 相关的组件~~~~每一份都非常的重要 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); } } }
为了突出重点,本文采用针对重点方法逐一描述的方式进行循序渐进的讲解:
initControllerAdviceCache() 它是初始化@ControllerAdvice标注的Bean并解析其内部各部分(@ModelAttribute、@InitBinder、RequestBodyAdvice和ResponseBodyAdvice接口等)然后缓存起来的方法。 RequestMappingHandlerAdapter: // ======================相关成员变量们====================== // 装载RequestBodyAdvice和ResponseBodyAdvice的实现类们~ private List<Object> requestResponseBodyAdvice = new ArrayList<>(); // MethodIntrospector.selectMethods的过滤器。 // 这里意思是:含有@ModelAttribute,但是但是但是不含有@RequestMapping注解的方法~~~~~ public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method -> (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) && AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class)); // 标注了注解@InitBinder的方法~~~ public static final MethodFilter INIT_BINDER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, InitBinder.class); // 存储标注了@ModelAttribute注解的方法的缓存~~~~ private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>(); // 存储标注了@InitBinder注解的方法的缓存~~~~ private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>(); private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } // 拿到容器内所有的标注有@ControllerAdvice的组件们 // BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class) // .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null) // .map(name -> new ControllerAdviceBean(name, context)) // 使用ControllerAdviceBean包装起来,持有name的引用(还木实例化哟) // .collect(Collectors.toList()); // 因为@ControllerAdvice注解可以指定包名等属性,具体可参见HandlerTypePredicate的判断逻辑,是否生效 // 注意:@RestControllerAdvice是@ControllerAdvice和@ResponseBody的结合体,所以此处也会被找出来 // 最后Ordered排序 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); AnnotationAwareOrderComparator.sort(adviceBeans); // 临时存储RequestBodyAdvice和ResponseBodyAdvice的实现类 // 它哥俩是必须配合@ControllerAdvice一起使用的~ List<Object> requestResponseBodyAdviceBeans = new ArrayList<>(); for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } // 又见到了这个熟悉的方法selectMethods~~~~过滤器请参照成员变量 // 含有@ModelAttribute,但是但是但是不含有@RequestMapping注解的方法~~~~~ 找到之后放在全局变量缓存起来 // 简单的说就是找到@ControllerAdvice里面所有的@ModelAttribute方法们 Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(adviceBean, attrMethods); } // 找标注了注解@InitBinder的方法~~~(和有没有@RequestMapping木有关系了~~~) // 找到@ControllerAdvice里面所有的@InitBinder方法们 Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(adviceBean, binderMethods); } // 这两个接口是Spring4.1 4.2提供的,实现了这两个接口的 // 此处先放在requestResponseBodyAdviceBeans里面装着 最后放到全局缓存requestResponseBodyAdvice里面去 if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } } // 这个意思是,放在该list的头部。 // 因为requestResponseBodyAdvice有可能通过set方法进来已经有值了~~~所以此处放在头部 if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); } // 输出debug日志...略(debug日志哦~) if (logger.isDebugEnabled()) { ... } }
该步骤调理清晰,有4个作用:
找到容器内(包括父容器)所有的标注有@ControllerAdvice注解的Bean们缓存起来,然后一个个解析此种Bean
找到该Advice Bean内所有的标注有@ModelAttribute但没标注@RequestMapping的方法们,缓存到modelAttributeAdviceCache里对全局生效
找到该Advice Bean内所有的标注有@InitBinder的方法们,缓存到initBinderAdviceCache里对全局生效
找到该Advice Bean内所有实现了接口RequestBodyAdvice/ResponseBodyAdvice们,最终放入缓存requestResponseBodyAdvice的头部,他们会介入请求body和返回body
介绍完此initControllerAdviceCache方法后,继续afterPropertiesSet()后续方法:初始化参数解析器、@InitBinder参数解析器、返回值解析器等。
@Override public void afterPropertiesSet() { ... // 初始化参数解析器 if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 初始化@InitBinder的参数解析器 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); } } getDefaultArgumentResolvers() 这个步骤作用是初始化HandlerMethodArgumentResolver,提供对方法参数的支持。 RequestMappingHandlerAdapter: // Return the list of argument resolvers to use including built-in resolvers and custom resolvers provided via {@link #setCustomArgumentResolvers}. // 返回内建的参数处理器们,以及用户自定义的一些参数处理器(注意顺序) private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // 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 // 基于type类型的 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()); // Custom arguments // 用户自定义的 if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all // 兜底方案:这就是为何很多时候不写注解参数也能够被自动封装的原因 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; } 这里使用的是ArrayList保存的,所以处理器都是有序的。最终会放进HandlerMethodArgumentResolverComposite使用Composite模式统一管理和使用~ getDefaultInitBinderArgumentResolvers() 这篇文章里有介绍过@InitBinder注解标注的方法入参也是可以写很多类型的参数的,需要注意的是它没有上面支持的那么全面,它支持的内容如下: RequestMappingHandlerAdapter: private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // 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 ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); return resolvers; } 从此处可以看到它具体支持哪些参数喽,像什么@RequestParam/@PathVariable/ServletRequest...等等都是支持的,但像什么@ModelAttribute/@RequestBody/@RequestPart等等这些它就不支持了。 getDefaultReturnValueHandlers() 顾名思义,它是提供对HandlerMethod返回值的支持,最终使用的是HandlerMethodReturnValueHandlerComposite相同的模式管理和使用,此处是初始化逻辑,具体使用可点击这里。 RequestMappingHandlerAdapter: private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); // 返回值是ResponseBodyEmitter时候,得用reactiveAdapterRegistry看看是Reactive模式还是普通模式 // taskExecutor:异步时使用的线程池,使用当前类的 contentNegotiationManager:内容协商管理器 handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager)); handlers.add(new StreamingResponseBodyReturnValueHandler()); // 此处重要的是getMessageConverters()消息转换器,一般情况下Spring MVC默认会有8个,包括`MappingJackson2HttpMessageConverter` // 参见:WebMvcConfigurationSupport定的@Bean --> RequestMappingHandlerAdapter部分 // 若不@EnableWebMvc默认是只有4个消息转换器的哦~(不支持json) // 此处的requestResponseBodyAdvice会介入到请求和响应的body里(消息转换期间) handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); // Annotation-based return value types // 当标注有@ModelAttribute或者@ResponseBody的时候 这里来处理。显然也用到了消息转换器~ handlers.add(new ModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types // 当返回的是个字符串/Map时候,这时候就可能有多个目的了(Multi-purpose) // 比如字符串:可能重定向redirect、或者直接到某个view handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types // 自定义的返回值处理器 if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all // 兜底:ModelAndViewResolver是需要你自己实现然后set进来的(一般我们不会自定定义) // 所以绝大部分情况兜底使用的是ModelAttributeMethodProcessor表示,即使你的返回值里木有标注@ModelAttribute // 但你是非简单类型(比如对象类型)的话,返回值都会放进Model里 if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); } else { handlers.add(new ModelAttributeMethodProcessor(true)); } return handlers; } 这个步骤完成后,整个RequestMappingHandlerAdapter的初始化就全部完成了。当然它还有一些属性、方法等都没有讲述到,下面做出集中式的统一说明: 其它重要属性、方法 属性: RequestMappingHandlerAdapter: // ModelAndViewResolver木有内置实现,可自定义实现来参与到返回值到ModelAndView的过程(自定义返回值处理) // 一般不怎么使用,我个人也不太推荐使用 @Nullable private List<ModelAndViewResolver> modelAndViewResolvers; // 内容协商管理器 默认就是它喽(使用的协商策略是HeaderContentNegotiationStrategy) private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); // 消息转换器。使用@Bean定义的时候,记得set进来,否则默认只会有4个(不支持json) // 若@EnableWebMvc后默认是有8个的,一般都够用了 private List<HttpMessageConverter<?>> messageConverters; // 它在数据绑定初始化的时候会被使用到,调用其initBinder()方法 // 只不过,现在一般都使用@InitBinder注解来处理了,所以使用较少 // 说明:它作用域是全局的,对所有的HandlerMethod都生效~~~~~ @Nullable private WebBindingInitializer webBindingInitializer; // 默认使用的SimpleAsyncTaskExecutor:每次执行客户提交给它的任务时,它会启动新的线程 // 并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用(默认值是-1,表示不限流) // @EnableWebMvc时可通过复写接口的WebMvcConfigurer.getTaskExecutor()自定义提供一个线程池 private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync"); // invokeHandlerMethod()执行目标方法时若需要异步执行,超时时间可自定义(默认不超时) // 使用上面的taskExecutor以及下面的callableInterceptors/deferredResultInterceptors参与异步的执行 @Nullable private Long asyncRequestTimeout; private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0]; private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0]; // @Since 5.0 private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); // 对应ModelAndViewContainer.setIgnoreDefaultModelOnRedirect()属性 // redirect时,是否忽略defaultModel 默认值是false:不忽略 private boolean ignoreDefaultModelOnRedirect = false; // 返回内容缓存多久(默认不缓存) 参考类:WebContentGenerator private int cacheSecondsForSessionAttributeHandlers = 0; // 执行目标方法HandlerMethod时是否要在同一个Session内同步执行??? // 也就是同一个会话时,控制器方法全部同步执行(加互斥锁) // 使用场景:对同一用户同一Session的所有访问,必须串行化~~~~~~ private boolean synchronizeOnSession = false; private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); @Nullable private ConfigurableBeanFactory beanFactory; // ====================下面是各种缓存们==================== private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64); private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64); private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>(); private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64); private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>(); 方法: RequestMappingHandlerAdapter: ... // 省略所有属性的get/set方法 @Override protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) { return -1; } // 因为它只需要处理HandlerMethod这样的Handler,所以这里恒返回true 请参照父类的supportsInternal()钩子方法 @Override protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; }