Spring MVC 之 HandlerMapping 源码分析与应用(获取所有URI)
整理这篇文章由两个因素引发:
一个是参与的微服务项目涉及到权限管理应用到了 HandlerMapping ,使用自定义注解,自动或手动拿到各个微服务所有自定义的 Controller RequestMapping(URI)同步到权限管理模块。
二是开发的项目报了异常,打印的异常信息比较有意思,就跟踪源码查到信息也是来自于 HandlerMapping,这些信息可以自己获取后应用在拦截器或 Controller 层的 AOP 中。
备注:已使用 AOP 拿到类似到源码抛出的打印的异常信息,但没有源码抛出的异常信息完美优雅。
HandlerMapping 是 Spirng MVC 的执行流程中需要用到的一个组件,用于获取 Handler 配置的所有相关的对象,包括 Handler 对象对应的拦截器,把这些对象封装到 HandlerExecutionChain(处理执行器链) 对象当中返回。可参考 Spring MVC 的执行流程。
HandlerMapping 有多个实现,可看下面的类图,而 RequestMappingHandlerMapping 是用得比较多的,它基于 @Controller 注解类上的 @RequestMapping 注解,从类型和方法级别创建请求映射信息 RequestMappingInfo。
HandlerMapping
HandlerMapping 是一个接口,里面主要有一个获取处理器执行链的方法 *HandlerExecutionChain getHandler(HttpServletRequest request)*,此接口由定义 请求 和 处理器对象 之间映射的对象实现。
Handler(处理对象)将始终被包装成 HandlerExecutionChain(处理器执行链)实现,其中可以包含一些 HandlerInterceptor 实例。
HandlerMapping 主要实现
HandlerMapping 的主要实现类主要有以下几个:
WelcomePageHandlerMapping:默认的欢迎首页(index.html)映射器。
SimpleUrlHandlerMapping:将 URLs 映射到请求处理器的 bean,支持映射到 bean 实例 和 bean names。
语法:PATH=HANDLER_BEAN_NAME,示例如下。
1
2ticketController =
ticketController =RequestMappingHandlerMapping:基于 @Controller 注解类上的 @RequestMapping 注解,从类型和方法级别创建请求映射信息 RequestMappingInfo。开发中用到最多的。
BeanNameUrlHandlerMapping:从 URLs 映射到名称以斜杠(
"/"
)开头的 bean,这是 DispatcherServlet 和 RequestMappingHandlerMapping 一起使用的默认实现。例如,一个进入的请求 URL
/foo
映射到一个名为/foo
的处理器,或多个如/foo, /foo2
映射到单个处理器。支持直接匹配和模式匹配,如
/test
映射到/test
,/test
映射到/t*
。
HandlerMapping 源码分析
DispatcherServlet 里的 doService 方法中调用 doDispatch 方法。
doDispatch 方法中获取处理器执行链,实际是处理器映射器,被包装成了 HandlerExecutionChain。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 决定那个处理器来处理当前请求
mappedHandler = getHandler(processedRequest);
//...............
}
}
}应用启动完成之后,在接收到第一个请求(HttpServletRequest)时初始化
WebApplicationContext
,初始化同时调用onRefresh
方法执行初始化 Servlet 需要用到的策略对象,其中包括初始化处理器映射器(HandlerMapping),实际是将 Spring 容器(ApplicationContext 上下文中)中实现 HandlerMapping 接口的 Bean 装入一个 List 。DispatcherServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 初始化文件上传解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化处理器映射器
initHandlerMappings(context);
// 初始化处理器适配器
initHandlerAdapters(context);
// 初始化处理器异常解决器
initHandlerExceptionResolvers(context);
// 初始化请求到视图名的翻译器
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}initHandlerMappings(context) 初始化 处理器映射器,保存到 handlerMappings
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
private List<HandlerMapping> handlerMappings;
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 检查 ApplicationContext 中所有 HandlerMappings
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
// 封装成一个 List
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}然后遍历
List<HandlerMapping>
,获取与请求匹配的处理器。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private List<HandlerMapping> handlerMappings;
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;
}getHandler(request)
底层是根据 request 的 URI 查找处理器。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根据给定的 request 获取处理器
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);
//...........省略...........
return executionChain;
}RequestMappingInfoHandlerMapping.java
1
2
3
4
5
6
7
8
9
10
11
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
// 调用 AbstractHandlerMethodMapping 父类方法
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}AbstractHandlerMethodMapping.java
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90private final MappingRegistry mappingRegistry = new MappingRegistry();
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求 URI
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
//根据 URI 找到其映射的处理器
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
/**
* 查找当前请求的最佳匹配处理程序方法。 如果找到多个匹配项,则选择最佳匹配项。
* lookupPath
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 重点, 从初始化的 mappingRegistry 中获取匹配的路径
List<T> directPathMatches = this.mappingRegistry .getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 匹配处理
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
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);
}
}
/**
* MappingRegistry 是 AbstractHandlerMethodMapping 的内部类
* 维护与处理器方法的所有映射、公开方法以执行查找并提供并发访问的注册表
* MappingRegistry 的初始化见下面章节
*/
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//........省略.........
}前提是,HandlerMapping 的实现类在注册为 Bean 后会执行最终的初始化配置,即调
afterPropertiesSet
方法执行initHandlerMethods()
。initHandlerMethods 方法遍历 ApplicationContext 中的 Bean,检测是否为处理器方法,即判断这个 bean 是否有 @Controller 或 @RequestMapping 注解,如果有则认为是处理器控制器,然后封装处理器方法与映射信息,将其注册(存储)到 MappingRegistry 中的各个 Map 或 List 中,完成 URI 与处理器映射的初始化。
AbstractHandlerMethodMapping.java
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
public void afterPropertiesSet() {
//初始化
initHandlerMethods();
}
protected void initHandlerMethods() {
// 遍历所有的 beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 根据 beanName 判断处理
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);
}
}
// 判断是否为处理器
if (beanType != null && isHandler(beanType)) {
// 检查处理器方法
detectHandlerMethods(beanName);
}
}RequestMappingHandlerMapping.java
判断是否为处理器
1
2
3
4
5
6
protected boolean isHandler(Class<?> beanType) {
// 判断是否为处理器,即是否有 @Controller 或 @RequestMapping 注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}AbstractHandlerMethodMapping.java
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/**
* 检查处理器方法, handler 是 beanName
*/
protected void detectHandlerMethods(Object handler) {
// 拿到 bean 的 Class 对象, 即 Controller 类对象
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 获取方法, Key是方法对象, value是RequestMappingInfo对象,即 @RequestMapping注解的信息
// RequestMappingInfo 包含路径,请求类型,params,head,consume,produce等信息
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
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 对象,实际取的就是 method 对象
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册处理器方法
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
/**
* 注册处理器方法及其唯一映射。在启动时为每个检测到的处理器方法调用
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}MappingRegistry 是 AbstractHandlerMethodMapping 的内部类,
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/**
* MappingRegistry 是内部类
* 注册处理器方法及其唯一映射
* 实际就是抽取处理器方法信息和映射信息, 封装成不同的 List 和 Map, 完成初始化
* 以备后续有请求进来后, 根据 request 直接从这些 List 或 Map 中找到映射的处理器
*/
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//........省略......
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
//mapping
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
// url
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
// mappingName
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
// cors
this.corsLookup.put(handlerMethod, corsConfig);
}
// 映射的注册信息
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
//.........省略............
}
RequestMappingHandlerMapping
获取请求映射处理器
可以用于请求拦截器 或 Controller 层的 AOP ,统一打印请求映射处理信息。
1 |
|
输出信息:
1 | Request Controller:public com.clearofchina.core.model.ResponseModel com.clearofchina.hisp.controller.FileInfoController.detail(com.clearofchina.hisp.entity.vo.IdVO) |
获取所有映射的 URI
获取所有 @Controller 注解类上的 @RequestMapping 注解映射的 URI。
1 |
|
Set<String> uriSet
打印:
1 | [ |
List<Map<String, String>> list
打印:
1 | [ |
获取所有Controller的beanName
1 |
|
获取Spring容器中所有beanName
1 |
|
相关参考
Spring MVC 之 HandlerMapping 源码分析与应用(获取所有URI)
http://blog.gxitsky.com/2020/05/19/SpringMVC-36-requestHandlerMapping/