该组件作用是根据请求信息获取对应的处理请求的handler,职责很单一,只干这一件事。该组件对应的接口是org.springframework.web.servlet.HandlerMapping,只有唯一的一个用来根据请求获取handler的方法getHandler(req),源码如下:
public interface HandlerMapping { String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler"; String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }其它的还有一些供子类使用的常量,比如最匹配handler常量BEST_MATCHING_HANDLER_ATTRIBUTE,按照一般的编程套路,会给该接口提供一个默认的抽象实现类,提供骨架逻辑来获取处理器+拦截器数组,然后暴露一个模板方法供子类实现,确实是如此,而这个抽象骨架类就是org.springframework.web.servlet.handler.AbstractHanlderMapping,这个模板方法是handleInernal(req),该方法是一个抽象方法,强制子类实现,定义如下:
@Nullable protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;好的,下面我们就从这个骨架抽象类开始吧!
因为实现了org.spingframework.web.context.ServletContextAware因此会通过方法setServletContext(sc)来索要javax.servlet.ServletContext,然后再看ApplicationObjectSupport,其定义如下:
public abstract class ApplicationObjectSupport implements ApplicationContextAware {}那么是通过setApplicationContext(sc)来索要ApplicationContext对象吗?其实不是的,setApplicationContext方法是在ApplicationObjectSupport方法中定义,但是该方法会调用模板方法initApplicationContext,而AbstractHandlerMapping正式通过重写该模板方法,从而实现加入spring的启动过程,从而实现handler interceptor的初始化工作的,那么在AbstractHandlerMapping中应该有setServletContext(sc)和initWebApplicationContext两个方法分别来索要代表web容器的ServletContext和加入spring加载过程的intiApplicationContext,其实只有后一个方法,前一个方法是在WebApplicationObjectSupport中定义的,源码如下:
org.springframework.web.context.support.WebApplicationObjectSupport @Override public final void setServletContext(ServletContext servletContext) { } org.springframework.web.servlet.handler.AbstractHandlerMapping @Override protected void initApplicationContext() throws BeansException { }这里的org.springframework.web.servlet.handler.AbstractHandlerMapping#initApplicationContext方法也正是HandlerInterceptor初始化的入口了。
实现Orderd接口 当有多个时可以指定顺序,值越小优先级越高。实现BeanNameAware接口 通过方法setBeanName(name)索要自己作为spring容器bean的名称,源码如下: @Override public void setBeanName(String name) { this.beanName = name; }源码如下:
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }方法extendInterceptors(this.interceptors)是一个模板方法,目前并没有用到,可以忽略,方法detectMappedInterceptors(this.adptedInterceptors)方法用于从容器中获取org.springframework.web.servlet.handler.MappedInterceptor以及其祖先类型的bean集合,方法源码如下:
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); }直接使用BeanFactoryUtils工具类来获取,如果我们定义了如下两个拦截器:
public class DemoHandlerInterceptor implements HandlerInterceptor {} public class DemoHandlerInterceptor1 implements HandlerInterceptor {}并通过如下方式注册:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/testinterceptor/hi"/> <bean class="dongshi.handlerinterceptor.DemoHandlerInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/testinterceptor/hi"/> <bean class="dongshi.handlerinterceptor.DemoHandlerInterceptor1"/> </mvc:interceptor> </mvc:interceptors>此时debug看的话,就能够看到我们定义的HandlerInterceptor: 一共三个元素,其中第一个位置为springmvc自己的,可以忽略,第二个和第三个就是我们自己定义的拦截器。方法initInterceptors方法是将private final List<Object> interceptors = new ArrayList<>();中的拦截器转换为HandlerInterceptor然后添加到adaptedInterceptors集合中,方法如下:
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)); } } }其中方法adtedInterceptor(interceptor)方法是转换的方法,支持HanlderInteceptor,WebReqeustInterceptor两种类型,源码如下:
protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } }到这里HandlerExecutionChain中的所需要的HandlerInterceptor已经全部准备好了,其实在initApplicationContext也就做了这么多工作,这个过程是在启动的过程中完成的,剩下的一部分就是Handler的获取了,这部分内容的获取是在请求过程中动态执行的,调用的方法是org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler。
当请求到达,最终会通过org.springframework.web.servlet.DispatcherServlet#doDispatch处理,具体可以看一次GET请求在springmvc中是的处理流程 ,好的,入口我们也就找到了,源码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... mappedHandler = getHandler(processedRequest); ... }其中getHandler方法会循环HandlerMapping实现类获取handler,代码如下:
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; }这里我们测试的接口如下:
@Controller @RequestMapping("/testinterceptor") public class TestinterceptorController { @RequestMapping("/hi") @ResponseBody public String hi() { String msg = "testinterceptor hi"; System.out.println(msg); return msg; } }因此最终会使用RequestMappingHandlerMapping来获取这个@RequestMapping("/hi")的handler。访问接口/testinterceptor/hidebug查看: 说明一下这个getHandlerInternal是一个模板方法,调用的是RequestMappingHandlerMapping的具体实现,这里先不详细看内部细节,接下来是通过方法getHandlerExecutionChain方法获取handler相关的HandlerInterceptor,源码如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // <1> String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // <2> for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }<1>处代码获取请求的路径,<2>处代码遍历所有的adaptedInterceptors如果是org.springframework.web.servlet.handler.MappedInterceptor类型,则判断拦截路径匹配才添加到chain中,否则直接添加,比如我们自定义的如下的两个HandlerInterceptor:
public class DemoHandlerInterceptor implements HandlerInterceptor {} public class DemoHandlerInterceptor1 implements HandlerInterceptor {}这里只看定义虽然和MappedInterceptor没关系,但是实际是会通过MappedInterptor进行封装,如下debug: 我们自定义的DemoHandlerInterceptor被封装为了MappedInterceptor的一个属性而已。因此代码interceptor instanceof MappedInterceptor为true。最终自定义的两个handler都会被添加到chain中,debug如下: 到这就获取到了请求对应的org.springframework.web.servlet.HandlerExecutionChain对象了。
AbstractHandlerMapping只是HandlerMapping的其中一个子类,其类图如下图: 可以看到其另一个子接口MatchableHandlerMapping,其接口定义如下:
public interface MatchableHandlerMapping extends HandlerMapping { @Nullable RequestMatchResult match(HttpServletRequest request, String pattern); }只有一个方法match(req, res),提供了具体实现的子类有RequestMappingHandlerMapping和AbstractUrlHandlerMapping添加这两个类后uml图如下:
DispatcherServlet的org.springframework.web.servlet.DispatcherServlet#handlerMappings是维护了所有的HandlerMapping的集合,会在请求到达时用来获取对应的handler。该变量的初始化是通过调用DispatcherServlet的org.springframework.web.servlet.DispatcherServlet#initHandlerMappings方法完成的,其实就是web容器按照servlet规范的要求来加载org.springframework.web.servlet.DispatcherServlet的时候来调用的,源码如下:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // <1> if (this.detectAllHandlerMappings) { Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerMappings); } } // <2> else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } // <3> 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"); } } }<1>处代码判断detectAllHandlerMappings如果是为true(该值默认为true,一般也不需要修改,如果有特殊需要修改,可以通过配置进行设置),则获取容器中HandlerMapping包括了其祖先类型的bean,debug查看变量Map<String, HandlerMapping> matchingBeans的结果如下: 获取后则设置到全局变量this.handlerMappings。<2>处代码是直接从容器中获取bean名称为HANDLER_MAPPING_BEAN_NAME=handlerMapping的handlermapping。正常<3>处代码不会进入,我们可以通过在web.xml中设置检测开关为false,然后,容器中不要有名称为handlerMapping的HandlerMapping的bean,就可以了,设置如下:
<servlet> <servlet-name>letsGO</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/letsGO-servlet.xml</param-value> </init-param> <init-param> <param-name>detectAllHandlerMappings</param-name> <param-value>false</param-value> </init-param> </servlet>代码getDefaultStrategies(context, clz)源码如下:
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { // <1> String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) { // <2> String[] classNames = StringUtils.commaDelimitedListToStringArray(value); // <3> List<T> strategies = new ArrayList<>(classNames.length); // <4> for (String className : classNames) { try { Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); // <5> Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { ... } catch (LinkageError err) { ... } } return strategies; } else { return new LinkedList<>(); } }先看下变量defaultStrategies其初始化源码如下:
private static final Properties defaultStrategies; static { try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { } }直接加载和DispatcherServlet在相同路径的DEFAULT_STRATEGIES_PATH=DispatcherServlet.properties的文件的内容,该文件配置的都是一些默认的组件类,和此处相关的配置内容为:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping将文件中键值对加载到变量defaultStrategies中。<1>处获取接口的名称其实就是配置文件中HandlerMapping的key,然后获取对应的值,debug看的话如下: <2>处代码为将逗号分割字符串格式数据分割为String的数组,<3>处代码为最终的结果集合,<4>为循环创建Class实例对象,最终结果debug如下: <5>处代码createDefaultStrategy(context, clz)是通过spring ioc容器创建对象,代码如下:
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { return context.getAutowireCapableBeanFactory().createBean(clazz); }