0%

HandlerMapping

HandlerMapping

介绍

HandlerMapping处理器映射器,是SpringMvc的核心组件之一,用来根据请求的request信息查询对应的Handler,在web环境中,每个请求都需要一个对应的Handler来处理,所以当接收到一个请求,需要哪一个Handler来处理,HandlerMapping的作用就是找到处理的那个Handler

分析

1. HandlerMapping顶层接口

HandlerMapping类图.png

以上为HandlerMapping的类图,在HandlerMapping接口中有一个公共的抽象类AbstractHandlerMapping
所有的子孙都会继承它。该抽象类有两个子类AbstractHandlerMethodMapping表示基于方法的映射方法,这个方式就是我们日常使用的Controller的那种方式,AbstractUrlHandlerMapping表示根据url获取到对应的Handler

这里分析一下公共抽象父类AbstractHandlerMapping。这个抽象类采用了模板方法的设计模式,编写了HandlerMapping的核心逻辑getHandler()方法,获取具体的handler由子类继承并实现getHandlerInternal方法,在获取到具体的handler之后,添加该请求匹配的拦截器列表,再返回HandlerExecutionChain结构,里面包含了具体的handler和拦截器列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 模板方法接口, 不同子类不同实现
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}

// 添加匹配的拦截器
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 (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}

这里点一下初始化拦截器的方法。由于AbstractHandlerMapping间接继承于ApplicationContextAware接口,在bean初始化时会调用该接口进行applicationContext的赋值。而改方法中设置了一个模板方法接口,由具体子类实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// ApplicationObjectSupport
/**
* bean初始化时会调用这个接口(BeanPostProcessor)
*
* @Author: xiaocainiaoya
* @Date: 2021/08/04 16:56:03
* @param context
* @return:
**/
@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
} else if (this.applicationContext == null) {
// Initialize with passed-in context.
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
} else {
// Ignore reinitialization if same context passed in.
if (this.applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this.applicationContext + "], passed-in one is [" + context + "]");
}
}
}

protected void initApplicationContext(ApplicationContext context) throws BeansException {
// 扩展点, 由子类自行实现
initApplicationContext();
}

// AbstractHandlerMapping#initApplicationContext

/**
* 主要是初始化拦截器
*
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
// 空实现。
// 子类可重写此方法以注册额外的拦截器
extendInterceptors(this.interceptors);
// 从上下文中查询拦截器并添加到拦截器列表中
detectMappedInterceptors(this.adaptedInterceptors);
// 初始化拦截器
initInterceptors();
}

根据以下代码可以得出一个简单的结论,拦截器的作用范围实际上是在Handler执行的前后,而过滤器Filter作用的范围应该是在请求进入到servlet的前和执行完servlet逻辑之后,套在springmvc中,Filter的作用范围是在请求进入到DispatcherServlet之前和执行完DispatcherServlet之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 将自定义bean设置到适配拦截器中,bean需实现HandlerInterceptor或WebRequestInterceptor
*
* @see #setInterceptors
* @see #adaptInterceptor
*/
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}

2. AbstractHandlerMethodMapping

AbstractHandlerMethodMapping这个就是RequestMappingHandlerMapping的抽象顶层父类,这种映射方式就是我们日常开发的那种,将标记有@Controller的类中的每个标记有@RequestMapping的方法都抽象为一个对应的HandlerMethod

简单罗列一下使用到的类:

  1. HandlerMethod:在初始化RequestMappingHandlerMapping会将spring容器中标记有@Controller的类中的@RequestMapping的方法都封装为HandlerMethod实体,包含了Handler对应的方法以及ControllerBean,并提供一些访问参数、方法返回值、获取注解等方法。
  2. RequestMappingInfo:@Controller类上标记的@RequestMaping或者是在方法上标记的@RequestMapping最终都会加载到RequestMappingInfo实体中。
  3. MappingRegistration:主要记录RequestMappingInfoHandlerMethod关系
  4. MappingRegistry:一个注册表,它维护到处理程序方法的所有映射,公开执行查找的方法并提供并发访问。

AbstractHandlerMethodMapping分支的类图:

RequestMappingHandlerMapping类图.png

图中红框中的三个类,分别依次继承,我们日常开发所使用的就是RequestMappingHandlerMapping

2.1 初始化

​ 在springbean容器启动后,当初始化类RequestMappingHandlerMapping完成时,由于它间接实现了初始化的后置方法InitializingBean,所以会进入afterPropertiesSet方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// RequestMappingHandlerMapping
public void afterPropertiesSet() {
// 初始化RequestMappingInfo构建配置
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());

// 通过该方法调用到父类的afterPropertiesSet, 从而调用到父类的initHandlerMethods方法(这个方法是完成映射的解析工作)
super.afterPropertiesSet();
}

/**
* // AbstractHandlerMethodMapping
*
* 后置初始化方法, 当这个Bean初始化完成之后调用, 获取容器中所有BeanDefinition
* 中含有@RequestMapping或者是@Controller的Bean信息
*
* Detects handler methods at initialization.
* @see #initHandlerMethods
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}

/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
// 从容器中获取所有Bean的名称,默认只查找SpringMVC的IOC容器,不查找它的父容器Spring的IOC容器
// 获取容器中所有Object.class类型的bean, 逐个遍历进行处理
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 利用反射得到@ControllerBean中的Method并包装成HandlerMethod,然后放入注册表中
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}

protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 这里的 isHandler()方法由子类(RequestMappingHandlerMapping)实现,判断是否拥有 @Controller 注解或 @RequestMapping 注解
// 这个isHandler()是个抽象接口, 由子类实现来控制是否进行接下来的注册。
if (beanType != null && isHandler(beanType)) {
// 利用反射得到@ControllerBean中的Method并包装成HandlerMethod,然后放入注册表中
detectHandlerMethods(beanName);
}
}

在接下来就是将具体的@Controller标记的类中的方法进行解析,满足条件的method封装为HandlerMethod后,添加到注册表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 利用反射得到 Bean 中的 Method 并包装成 HandlerMethod,然后放入 Map 中
*
* Look for handler methods in the specified handler bean.
* @param handler either a bean name or an actual handler instance
* @see #getMappingForMethod
*/
protected void detectHandlerMethods(Object handler) {
// 根据字符串获取到对应的类型
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());

if (handlerType != null) {
// 若是代理对象获取目标类型
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 收集要封装的接口信息, 键为method的引用, 值为RequestMethodInfo的引用
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 根据 Method 和它的 @RequestMapping 注解,创建 RequestMappingInfo 对象。
// 这里的 T 就是 RequestMappingInfo,它封装了 @RequestMapping 信息
return getMappingForMethod(method, userType);
}catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册到mappingRegistry中
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
// 收集要封装的接口信息, 键为method的引用, 值为RequestMethodInfo的引用
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;

// 非代理类
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

for (Class<?> currentHandlerType : handlerTypes) {
// specificHandlerType若为空表示其为代理类, 则取currentHandlerType
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

// 函数式接口: 遍历currentHandlerType的所有methods, 并执行第二个函数引用
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// 获取@RequestMapping信息
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}

这里使用了两次的函数式接口,不同函数式接口可能看到这回有点吃力。第一个函数式接口的作用其实就是将某个method上标记的@RequestMapping信息和这个method所在类上标记的@RequestMapping信息拼接起来后包装为RequestMappingInfo后返回。第二个函数式接口的作用是将第一个函数式接口处理之后返回的RequestMappingInfo信息暂存到某个变量中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 最后将解析出来的所有RequestMappingInfo信息逐个遍历,通过调用以下方法,添加到注册表中。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}

public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
}
this.readWriteLock.writeLock().lock();
try {
// 实例化一个HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 校验是否已经存在这个handlerMethod
validateMethodMapping(handlerMethod, mapping);
// 添加到mappingLookup查找器中
this.mappingLookup.put(mapping, handlerMethod);

List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}

String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}

// 获取跨域的配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
// 注册跨域配置
this.corsLookup.put(handlerMethod, corsConfig);
}

this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
} finally {
this.readWriteLock.writeLock().unlock();
}
}
2.2 查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* // AbstractHandlerMethodMapping
*
* getHandlerInternal()方法是由AbstractHandlerMapping抽象类定义的模板方法,具体细节由
* 子类实现,用于获取对应的HandlerMethod
*/
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据当前请求获取“查找路径”
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
// 获取当前请求最佳匹配的处理方法(即Controller类的方法中)
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}

/**
* 具体是通过该方法获取HandlerMethod
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 从MappingRegistry.urlLookup属性中,获取lookupPath对应的mapping集合
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 获取匹配的mapping,添加到matches列表
addMatchingMappings(directPathMatches, matches, request);
}
// 如果没有匹配lookupPath的实例,则遍历所有的mapping,查找符合条件的mapping
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

//说明存在符合条件的mapping
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 多个的情况
if (matches.size() > 1) {
//获取匹配条件的排序器,由抽象方法getMappingComparator()方法获取,该方法由子类实现
// 这个排序规则也是个模板方法, 由子类实现, 由子类控制当出现多个匹配的mapping
// 通过比较器排序后, 选择第一个为最匹配的mapping
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
-------------本文结束感谢您的阅读-------------