整理这篇文章由两个因素引发:
- 一个是参与的微服务项目涉及到权限管理应用到了 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,示例如下。
/welcome.html=ticketController /show.html=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。
protected 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
@Override 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
@Nullable 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>
,获取与请求匹配的处理器。@Nullable private List<HandlerMapping> handlerMappings; @Nullable 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 查找处理器。@Override @Nullable 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
@Override 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
private final MappingRegistry mappingRegistry = new MappingRegistry(); @Override 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 */ @Nullable 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
@Override 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
判断是否为处理器
@Override protected boolean isHandler(Class<?> beanType) { // 判断是否为处理器,即是否有 @Controller 或 @RequestMapping 注解 return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
AbstractHandlerMethodMapping.java
/** * 检查处理器方法, 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 的内部类,
/** * 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 ,统一打印请求映射处理信息。
@Autowired
@Qualifier(value = "requestMappingHandlerMapping")
private HandlerMapping requestMappingHandlerMapping;
// 处理器执行链
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
HandlerMethod handlerMethod = (HandlerMethod) handlerExecutionChain.getHandler();
MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (MethodParameter parameter : methodParameters) {
String string = parameter.getExecutable().toGenericString();
logger.info("Request Controller:{}", string);
}
输出信息:
Request Controller:public com.clearofchina.core.model.ResponseModel com.clearofchina.hisp.controller.FileInfoController.detail(com.clearofchina.hisp.entity.vo.IdVO)
获取所有映射的 URI
获取所有 @Controller 注解类上的 @RequestMapping 注解映射的 URI。
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
//@Autowired
//private WebApplicationContext webApplicationContext;
//RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean(RequestMappingHandlerMapping.class);
//获取 WebApplicationContext 来获取 requestMappingHandlerMapping
//WebApplicationContext webApplicationContext = (WebApplicationContext) request.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean("requestMappingHandlerMapping")
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
List<Map<String, String>> list = new ArrayList<>();
Set<String> uriSet = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
Map<String, String> controllerMap = new HashMap<>();
RequestMappingInfo mappingInfo = entry.getKey();
HandlerMethod handlerMethod = entry.getValue();
Set<String> patternSet = mappingInfo.getPatternsCondition().getPatterns();
Set<RequestMethod> methodSet = mappingInfo.getMethodsCondition().getMethods();
// uriSet.addAll(patterns);
for (String pattern : patternSet) {
uriSet.add(pattern);
controllerMap.put("url", pattern);
}
// 类注解
RequestMapping annotation = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequestMapping.class);
// 方法注解
RequestMapping methodAnnotation = handlerMethod.getMethodAnnotation(RequestMapping.class);
// 类名
controllerMap.put("className", handlerMethod.getMethod().getDeclaringClass().getName());
// 方法名
controllerMap.put("method", handlerMethod.getMethod().getName());
// 方法类型
for (RequestMethod requestMethod : methodSet) {
controllerMap.put("type", requestMethod.toString());
}
list.add(controllerMap);
}
logger.info("url set:" + JSON.toJSONString(uriSet));
logger.info("url controller:" + JSON.toJSONString(list));
Set<String> uriSet
打印:
[
"/error",
"/fileInfo/detail",
"/downloadLog/pageList",
"/downloadLog/detail"
]
List<Map<String, String>> list
打印:
[
{
"method":"detail",
"className":"com.clearofchina.hisp.controller.DownloadLogController",
"type":"GET",
"url":"/downloadLog/detail"
},
{
"method":"pageList",
"className":"com.clearofchina.hisp.controller.DownloadLogController",
"type":"GET",
"url":"/downloadLog/pageList"
},
{
"method":"detail",
"className":"com.clearofchina.hisp.controller.FileInfoController",
"type":"GET",
"url":"/fileInfo/detail"
},
{
"method":"error",
"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
"url":"/error"
},
{
"method":"errorHtml",
"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
"url":"/error"
}
]
获取所有Controller的beanName
@Autowired
private WebApplicationContext webApplicationContext;
String[] beanNames = webApplicationContext.getBeanNamesForAnnotation(Controller.class);
获取Spring容器中所有beanName
@Autowired
private WebApplicationContext webApplicationContext;
String[] beanNames = webApplicationContext.getBeanDefinitionNames();
// 或者
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
String[] beanNames = SpringContextHolder.getApplicationContext().getBeanDefinitionNames();
相关参考
注意:本文归作者所有,未经作者允许,不得转载