一、前言
上一篇文章介绍了SpringMVC的请求过程,其中在DispatcherServlet中的 doDispatch方法中,说到了根据 request 查找具体Handler的,这篇文章主要介绍 Handler的查找,即为怎么根据Request 请求URL查找到 Controller 。
二、查找Handler
2.1、回顾 doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
try {
// Determine handler for the current request.
//根据 request 查找Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
}catch (Exception ex) {
dispatchException = ex;
}
}catch (Exception ex) {
}catch (Throwable err) {
}finally {
}
}
2.2、查看 getHandler方法
-
@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起始返回的是 HandlerExecutionChain,其实是在 handlerMappings中查找的,那handlerMappings又是从哪来的呢,接下来分析 handlerMappings的出处。
2.3、handlerMappings的前世今生
上篇文章中结束Dispatcher的初始化时介绍到了,在DispatcherServlet的 onRefresh 方法中调用了 initHandlerMappings 方法,接下来看看initHandlerMappings方法的实现逻辑。
-
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 在SpringContext的容器中查找 HandlerMapping 接口的所有实例。如果查询到进行排序 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { 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) { } } // 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"); } } }
查看看 handlerMappings中的HandlerMapper,如下图所示,其中主要解析Controller是在 RequestMappingHandlerMapping中。
接下来看看 HandlerMapping 类的实现结构
接下来重点看HandlerMapping的实现类, RequestMappingHandlerMapping 类。
1、先查看下ReqeustMappingHandlerMapping类的继承关系图
根据继承关系图可知,RequestMappingHandlerMapping同时实现了 InitializingBean和ApplicationContextAware两个接口,熟悉这两个接口的都知道,其中InitializingBean 的方法 afterPropertiesSet 是在类初始化完成且设置属性完成后调用,那么接下来看看 RequestMappingHandlerMapping对afterPropertiesSet的实现。
-
public void afterPropertiesSet() { 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()); //用调用了父类的 方法 super.afterPropertiesSet(); }
此实现中除了设置一下参数配置,又调用了父类的afterPropertiesSet的实现,在看看父类的方法
-
@Override public void afterPropertiesSet() { initHandlerMethods(); } /** * Scan beans in the ApplicationContext, detect and register handler methods. * @see #getCandidateBeanNames() * @see #processCandidateBean * @see #handlerMethodsInitialized */ protected void initHandlerMethods() { //在Spring容器中获取所有的Bean, for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { //解析Bean processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
根据源码可知:
- 直接调用了 initHandlerMethods方法
- initHandlerMethods方法是获取了Spring容器中的所有beanName。
- 然后调用了processCandidateBean方法。
3、查看 processCandidateBean 方法
-
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { //获取beanNamed对应的类型 beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { } //判断类型是否为空,和 此类型是否为 Handler if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } //此方法是RequestMappingHandlerMapping类中实现的,判断了此类型是否添加了 // Controller 和 RequestMapping 注解。 @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
有源码可知
- 如果判断类型为Handler,即类添加了 Controller 和RequestMapping 注解。
- 如果是 则调用 detectHandlerMethods 方法,此方法即为生成 RequestMappingInfo 的方法。此方法在父类
接下来看看 detectHandlerMethods 方法的实现
-
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { // 解析类中的方法,生成 RequestMappingInfo 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); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
detectHandlerMethods 方法说明:
- 通过反射机制获取 handlerType的所有方法,并调用 getMappingForMethod() 方法,把方法解析为 RequestMappingInfo 类型对象。
- 解析完所有的方法后,在调用 registerHandlerMethod 方法注册到HandlerMapping中。具体注AbstractHandlerMethodMapping内部类MappingRegistry的 register 方法,再次方法中 把 handler 和 具体的方法再次封装为 HandlerMethod。然后进行注册。
至此所有的查找注册工作完成了。
三、补充说明
查看RequestMappingHandlerMapping类的创建。
1、通过 <annotation-driven/> 方式
此方式是在解析类 AnnotationDrivenBeanDefinitionParser 中的 parse 方法中实现的。如下所示:
2、SpringBoot方式
此种方式是在 类 WebMvcConfigurationSupport 中创建的,如下所示:
四、总结
- 在系统启动时,会在Spring容器中查找所有通过 Controller和RequestMapping注解的类,并解析器方法,把所有符合条件的方法都以RequestMappingInfo及HandlerMethod 注册到 RequestMappingHandlerMapping中。
- 在Servlet初始化时,会在Spring容器中查找所有的 HandlerMapping对象。
- 在请求是主要通过DispatcherServlet中的 doDispatch方法进行转发。
- 在doDispatch中调用 getHandler中,在此方法中循环所欲的HandlerMapping,找到符合条件的HandlerMapping进行处理