tomcat + spring mvc原理(九):spring mvc如何将请求投送到Controller中的方法 1
前言
在原理七中列举了spring mvc的九大组件,分别是:
- HandlerMapping:处理器映射,寻找对应Interceptor和Controller
- HandlerAdapter:处理器适配,使用处理器Handler干活
- HandlerExceptionResolver:处理器错误解析,请求处理过程报错,设置ModelAndView
- ViewResolver:视图解析器,寻找对应视图
- RequestToViewNameTranslator:从请求获取view名,view名供视图解析器寻找对应视图
- LocaleResolver:从请求获取Locale,Locale供视图解析器使用
- ThemeResolver:主题解析器
- MultipartResolver:处理上传请求
- FlashMapManager:管理重定向相关的FlashMap
这篇文章主要是介绍HandlerMapping组件的原理。HandlerMapping组件的调用是在DispatcherServlet的doDispatch方法中,其中有:
mappedHandler = getHandler(processedRequest);
mappedHandler = getHandler(processedRequest),返回的mappedHandler是HandlerExecutionChain类型,内含Interceptor和Handler。和Controller一样,Interceptor也被归类为一种Handler。(各组件的配合使用完成请求的过程在原理八中介绍)
其中 getHandler方法的代码如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
DispatcherServlet类中维护了一个HandlerMapping的列表:
private List<HandlerMapping> handlerMappings;
通过传入的请求体,遍历这个HandlerMapping来找到处理这个请求的Handler(Controller或者Interceptor),这篇文章的主要内容就是分析HandlerMapping的原理,探索它是怎样实现“路由”到我们定义的Interceptor或者Controller的。
HandlerMapping组件的整体架构
![tomcat + spring mvc原理(九):HandlerMapping原理-handlerMapping.png](https://i-blog.csdnimg.cn/blog_migrate/644a515f1950cdb98e3e9db696c703d5.png)
HandlerMapping是一个接口,定义了HandlerMapping的标准,子类只需要实现接口定义的方法。这样做的好处是,HandlerMapping作为一个比较灵活的组件,支持用户实现该标准,自定义自己的处理器映射。一般使用接口做隔离的目的,就是能够提供比较好的扩展性,类定义遵循接口标准,可以方便地拔插不同的类实现。
实现HandlerMapping接口的类是两个系列:一个系列是AbstractUrlHandlerMapping,这个通过名字就可以看出是通过url匹配Handler;另一个系列是AbstractHandlerMethodMapping,它匹配的方式比较多,而且直接匹配到方法,我们目前常用的@RequestMapping注解、@GetMapping注解和@PostMapping注解就是这种类型。由于我没有了解过AbstractUrlHandlerMapping系列的使用场景,目前看上去使用比较多的是AbstractHandlerMethodMapping,所以我只会着重介绍后者的实现原理。
接口标准和AbstractHandlerMapping
事实上,HandlerMapping接口只有一个方法:
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
就是在上文中DispatcherServlet类的getHandler方法中调用的mapping.getHandler(request)。
上面的类继承图谱只显示了AbstractHandlerMapping继承树的一部分,下面的图展示了完整的部分。可以看到,AbstractHandlerMapping还实现了ApplicationContextAware、ServletContextAware、BeanNameAware接口。
在spring mvc中Aware系列的类代表对可以获取Aware前面修饰的环境,比如ApplicationContextAware可以获取ApplicationContext,ServletContextAware可以获取ServletContext,BeanNameAware可以获取BeanName。只需要implements这个接口,重载这个接口唯一的方法:
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext var1) throws BeansException;
}
整个spring mvc服务在初始化是会自动调用实现类重载的这个setApplicationContext方法,将ApplicationContext变量传递给我们使用。这个技巧在业务代码编写中也可以使用。
不过,AbstractHandlerMapping系列子类的初始化并不是调用setApplicationContext方法,ApplicationObjectSupport和WebApplicationObjectSupport对这个方法进行了重新封装。ApplicationObjectSupport自身持有ApplicationContext对象,在重载的setApplicationContext中调用了自行定义的initApplicationContext(ApplicationContext context)方法,然后内部调用无参的initApplicationContext()方法。无参的方法在ApplicationObjectSupport中是空实现,最终的是由AbstractHandlerMapping重载了initApplicationContext()方法。
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
其实initApplicationContext()的有效代码做的事就是配置和初始化Interceptor。extendInterceptors方法在AbstractHandlerMapping是模板方法,为子类提供添加、修改Interceptor列表的入口(虽然子类并没有实现)。
在讲detectMappedInterceptors方法和initInterceptors方法的内部代码之前,先需要了解三种类型的Interceptor:
private final List<Object> interceptors = new ArrayList<>(); //1
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); //2
AbstractHandlerMapping类中持有了其中两类。
- interceptors是代表所有spring mvc中配置的拦截器,能够从环境上下文中获取所有Interceptor的配置。
- mappedInterceptors代表有url映射的Interceptor,在getHandler方法返回的HandlerExceptionChain中,只有匹配命中的MappedInterceptor才会被返回,可以从adaptedInterceptors获取。
- adaptedInterceptors包含了mappedInterceptor,也包含不需要匹配的Interceptor。
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
这里方法传参的起名虽然是mappedInterceptors,但是在上文initApplicationContext方法中可以看到,传入的实际是this.adaptedInterceptors。detectMappedInterceptors方法的作用就是将程序上下文中所有的MappedInterceptor放入到adaptedInterceptors中。adaptedInterceptors中还包括了其他HandlerInterceptor和WebRequestInterceptor类型的拦截器。
总而言之,AbstractHandlerMapping初始化时主要做的工作就是初始化Interceptor。
初始化之后,对于HandlerMapping系列类来说,更重要的是如何实现getHandler方法。AbstractHandlerMapping重载了getHandler方法,并为后续子类提供了模板方法getHandlerInternal。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取映射的Handler
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//添加筛选的Interceptor
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//打印日志
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//跨站请求的处理逻辑
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
这个方法主要可以拆分为四步。第一步是获取映射的Handler(Controller对应的方法),getHandlerInternal只是一个模板方法,后续子类需要关注这个方法的实现。如果没有获取到Handler,可以取默认的Handler来处理,甚至直接利用Handler的name从环境上下文中加载。第二步是添加筛选出的Interceptor,由getHandlerExecutionChain方法构造出HandlerExecutionChain。上文提到,adaptedInterceptors中包括了mappedInterceptors和不需要匹配url的Interceptor。所以getHandlerExecutionChain中会筛选中命中url的mappedInterceptor和其他不需要匹配url的Interceptor,都加入到HandlerExecutionChain中返回。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
第三步打印跟踪日志或者调试日志。第四步是和http的跨站请求相关的。如果该请求为跨站请求,会在http头中有Origin属性,存储源站点信息。这样url匹配逻辑应该需要考虑源站url,所以要额外处理。
为了合理设置篇幅,使层次更加清晰,后续AbstractHandlerMethodMapping子类系列的内容放在二期中详细分析。
tomcat + spring mvc原理(一):tomcat原理综述和静态架构
tomcat + spring mvc原理(二):tomcat容器初始化加载和启动
tomcat + spring mvc原理(三):tomcat网络请求的监控与处理1
tomcat + spring mvc原理(四):tomcat网络请求的监控与处理2
tomcat + spring mvc原理(五):tomcat Filter组件实现原理
tomcat + spring mvc原理(六):tomcat WAR包的部署与加载
tomcat + spring mvc原理(七):spring mvc的Servlet和九大标准组件的静态结构与初始化
tomcat + spring mvc原理(八):spring mvc对请求的处理流程