这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战
介绍
HandlerMapping处理器映射器,是SpringMvc的核心组件之一,用来根据请求的request信息查询对应的Handler,在web环境中,每个请求都需要一个对应的Handler来处理,所以当接收到一个请求,需要哪一个Handler来处理,HandlerMapping的作用就是找到处理的那个Handler。
分析
1. HandlerMapping顶层接口
以上为HandlerMapping的类图,在HandlerMapping接口中有一个公共的抽象类AbstractHandlerMapping
所有的子孙都会继承它。该抽象类有两个子类AbstractHandlerMethodMapping表示基于方法的映射方法,这个方式就是我们日常使用的Controller的那种方式,AbstractUrlHandlerMapping表示根据url获取到对应的Handler。
这里分析一下公共抽象父类AbstractHandlerMapping。这个抽象类采用了模板方法的设计模式,编写了HandlerMapping的核心逻辑getHandler()方法,获取具体的handler由子类继承并实现getHandlerInternal方法,在获取到具体的handler之后,添加该请求匹配的拦截器列表,再返回HandlerExecutionChain结构,里面包含了具体的handler和拦截器列表。
模板方法接口, 不同子类不同实现。
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
这里点一下初始化拦截器的方法。由于AbstractHandlerMapping间接继承于ApplicationContextAware接口,在bean初始化时会调用该接口进行applicationContext的赋值。而改方法中设置了一个模板方法接口,由具体子类实现。
进入到ApplicationObjectSupport方法中,bean初始化时会调用这个接口(BeanPostProcessor),主要作用是在bean初始化时赋值applicationContext上下文信息,这是个扩展点, 由子类自行实现。
protected void initApplicationContext(ApplicationContext context){
initApplicationContext();
}
具体的实现子类AbstractHandlerMapping:主要作用是初始化拦截器。
- 空实现。子类可重写此方法以注册额外的拦截器
- 从上下文中查询拦截器并添加到拦截器列表中
- 初始化拦截器
protected void initApplicationContext() throws BeansException {
// 1.
extendInterceptors(this.interceptors);
// 2.
detectMappedInterceptors(this.adaptedInterceptors);
// 3.
initInterceptors();
}
根据以下代码可以得出一个简单的结论,拦截器的作用范围实际上是在Handler执行的前后,而过滤器Filter作用的范围应该是在请求进入到servlet的前和执行完servlet逻辑之后,套在springmvc中,Filter的作用范围是在请求进入到DispatcherServlet之前和执行完DispatcherServlet之后。
将自定义bean设置到适配拦截器中,bean需实现HandlerInterceptor或WebRequestInterceptor
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
2. AbstractHandlerMethodMapping
AbstractHandlerMethodMapping这个就是RequestMappingHandlerMapping的抽象顶层父类,这种映射方式就是我们日常开发的那种,将标记有@Controller的类中的每个标记有@RequestMapping的方法都抽象为一个对应的HandlerMethod。
简单罗列一下使用到的类:
- HandlerMethod:在初始化
RequestMappingHandlerMapping会将spring容器中标记有@Controller的类中的@RequestMapping的方法都封装为HandlerMethod实体,包含了Handler对应的方法以及ControllerBean,并提供一些访问参数、方法返回值、获取注解等方法。 - RequestMappingInfo:在
@Controller类上标记的@RequestMaping或者是在方法上标记的@RequestMapping最终都会加载到RequestMappingInfo实体中。 - MappingRegistration:主要记录
RequestMappingInfo与HandlerMethod关系 - MappingRegistry:一个注册表,它维护到处理程序方法的所有映射,公开执行查找的方法并提供并发访问。
AbstractHandlerMethodMapping分支的类图:
图中红框中的三个类,分别依次继承,我们日常开发所使用的就是RequestMappingHandlerMapping。
2.1 初始化
在springbean容器启动后,当初始化类RequestMappingHandlerMapping完成时,由于它间接实现了初始化的后置方法InitializingBean,所以会进入afterPropertiesSet方法,
这里主要先初始化RequestMappingInfo构建配置,再通过该方法调用到父类的afterPropertiesSet, 从而调用到父类的initHandlerMethods方法(这个方法是完成映射的解析工作)
后置初始化方法, 当这个Bean初始化完成之后调用, 获取容器中所有BeanDefinition中含有@RequestMapping或者是@Controller的Bean信息
public void afterPropertiesSet() {
initHandlerMethods();
}
从容器中获取所有Bean的名称,默认只查找SpringMVC的IOC容器,不查找它的父容器,获取容器中所有Object.class类型的bean, 逐个遍历进行处理。然后 利用反射得到@ControllerBean中的Method并包装成HandlerMethod,最后放入注册表中。
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
这里的isHandler方法由子类(RequestMappingHandlerMapping)实现,判断是否拥有@Controller注解或@RequestMapping注解这个isHandler是个抽象接口, 由子类实现来控制是否进行接下来的注册。
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
利用反射得到Bean中的Method并包装成HandlerMethod对象,然后放入Map中
- 根据字符串获取到对应的类型
- 若是代理对象获取目标类型
- 根据
Method和它的@RequestMapping注解,创建RequestMappingInfo对象。这里的T就是RequestMappingInfo,它封装了@RequestMapping信息。
protected void detectHandlerMethods(Object handler) {
// 1.
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 2.
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 3.
return getMappingForMethod(method, userType);
}
});
methods.forEach((method, mapping) -> {
// ..
});
}
}
收集要封装的接口信息, 键为method的引用, 值为RequestMethodInfo的引用
specificHandlerType若为空表示其为代理类, 则取currentHandlerType- 函数式接口: 遍历
currentHandlerType的所有methods, 并执行第二个函数引用
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
for (Class<?> currentHandlerType : handlerTypes) {
// 1.
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
// 2.
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// ...
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
这里使用了两次的函数式接口,不同函数式接口可能看到这回有点吃力。第一个函数式接口的作用其实就是将某个method上标记的@RequestMapping信息和这个method所在类上标记的@RequestMapping信息拼接起来后包装为RequestMappingInfo后返回。第二个函数式接口的作用是将第一个函数式接口处理之后返回的RequestMappingInfo信息暂存到某个变量中。
最后将解析出来的所有RequestMappingInfo信息逐个遍历,通过调用以下方法,添加到注册表中。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
注册的核心逻辑:实例化一个HandlerMethod对象,然后校验是否已经存在这个handlerMethod,最后添加到mappingLookup查找器中.
2.2 查找
getHandlerInternal方法是由AbstractHandlerMapping抽象类定义的模板方法,具体细节由子类实现,用于获取对应的HandlerMethod先根据当前请求获取“查找路径”,再获取当前请求最佳匹配的处理方法(即Controller类的方法中)
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
}
具体是通过该方法获取HandlerMethod,从MappingRegistry.urlLookup属性中,获取lookupPath对应的mapping集合,获取匹配的mapping后,添加到matches列表。如果没有匹配lookupPath的实例,则遍历所有的mapping,查找符合条件的mapping。
获取匹配条件的排序器,由抽象方法getMappingComparator方法获取,该方法由子类实现这个排序规则也是个模板方法, 由子类实现, 由子类控制当出现多个匹配的mapping通过比较器排序后, 选择第一个为最匹配的mapping
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));