参考资料:
《SpringMVC源码解析系列》
《SpringMVC源码分析》
《Spring MVC源码》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
前文:
《SpringMVC源码:DispatcherServlet初始化流程》
在前文中我们介绍了DispatcherServlet的初始化,其中有两个重要的过程分别是initHandlerMappings和initHandlerAdapters,本文我们介绍下这两个组件。
目录
一、HandlerMapping
1、HandlerMapping接口
2、AbstractHandlerMapping类
2.1、类属性
2.2、类方法
二、AbstractUrlHandlerMapping类
简介
类方法
registerHandler
getHandlerInternal
lookupHandler
buildPathExposingHandler
实现类
SimpleUrlHandlerMapping类
initApplicationContext
registerHandler
AbstractDetectingUrlHandlerMapping类
detectHandlers
BeanNameUrlHandlerMapping#determineUrlsForHandler
一、HandlerMapping
1、HandlerMapping接口
HandlerMapping接口定义主要用来提供request 请求对象和Handler对象 映射关系的接口。所谓request对象比如我们web应用中的http 请求,Handler对象则指的是对应rquest请求的相关处理逻辑(可以理解为我们对应改请求的业务处理逻辑)。
该接口只有一个方法getHandler(),入参是Http对应的请求对象,返回会值是HandlerExecutionChain对象,该对象包含一个处理器对象Handler和匹配该处理器的若干interceptors 拦截器列表。
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";HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;}
下图是HandlerMapping接口以及其目前所有子类的UML类图:
从类图中可以看到HandlerMapping有一个初始的抽象类AbstractHandlerMapping,在该抽象类,分成两个体系一个是以AbstractUrlHandlerMapping(url和类级别的Handler处理器映射 url ->Controller),另一个是AbstractHandlerMethodMapping(url和method方法级别的Handler处理器)。
2、AbstractHandlerMapping类
AbstractHandlerMapping是HandlerMapping的抽象实现,同时实现了Order接口继承了WebApplicationObjectSupport类,order接口主要是为了如果spring容器中有多个HandlerMapping,则按照order 排序去一次使用HandlerMapping获取handler对象,order小的优先被使用。
由于该处理器抽象类继承WebApplicationObjectSupport对象,通过查看WebApplicationObjectSupport的继承关系,其也间接实现了ApplicationContextAware和ServlerContextAware,意味着该类在spring容器中被初始化完成后自动拥有ApplicationContext对象和ServletContext对象。
//WebApplicationObjectSupport.java@Overridepublic final void setServletContext(ServletContext servletContext) {if (servletContext != this.servletContext) {this.servletContext = servletContext;if (servletContext != null) {initServletContext(servletContext);}}}@Overrideprotected void initApplicationContext(ApplicationContext context) {super.initApplicationContext(context);if (this.servletContext == null && context instanceof WebApplicationContext) {this.servletContext = ((WebApplicationContext) context).getServletContext();if (this.servletContext != null) {initServletContext(this.servletContext);}}}
2.1、类属性
AbstractHandlerMapping包含以下类属性:
//如果处理器映射器没有获取到对应的Handlerm,则使用的默认Handler, 可以通过xml/注解的形式配置。private Object defaultHandler;//Url路径匹配的帮助类,包含ur编码方式设置获取,或者根据请求请求路径等方法private UrlPathHelper urlPathHelper = new UrlPathHelper();//Spring提供的路径匹配策略接口的路径匹配器private PathMatcher pathMatcher = new AntPathMatcher();//通过该属性来设置相关的拦截器,设置的方式可以通过xml配置private final List<Object> interceptors = new ArrayList<Object>();//该集合下的拦截器不需要匹配会通过getHandler()方法全部添加到HandlerExecutionChain中private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();//排序属性 默认最大值,如果不设置意味着其对应的处理器对象优先级最小private int order = Ordered.LOWEST_PRECEDENCE;
2.2、类方法
initApplicationContext()方法:对拦截器集合做处理
@Overrideprotected void initApplicationContext() throws BeansException {//spring提供钩子函数,子类可以实现该方法来为集合设置拦截器extendInterceptors(this.interceptors);//从spring容器中获取所有MappedInterceptordetectMappedInterceptors(this.adaptedInterceptors);//将所有设置在interceptors中的拦截器添加到adaptedInterceptors中initInterceptors();}//从spring容器中获取所有MappedInterceptor并添加到adaptedInterceptors中protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), MappedInterceptor.class, true, false).values());}//将所有设置在interceptors中的拦截器添加到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));}}}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());}}
getHandler()方法:核心方法,主要作用是根据请求获取包装了Handler对象和拦截器的HandlerExecutionChain对象。该方法采用模板方法获取拦截器处理器链。
@Overridepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//交由子类实现的根据请求获取处理器的具体逻辑 不同的子类获取Handler的逻辑不同Object handler = getHandlerInternal(request);//如果没有获取到Handler 则获取默认的Handler//默认的处理器可以通过配置映射器实例的时候通过属性设置if (handler == null) {handler = getDefaultHandler();}//如果默认的Handler还是没有则返回空if (handler == null) {return null;}//Handler处理器是字符串类型 说明获取的Handler是beanName//则从spring 容器中获取bean实例对象if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}//为Handler添加拦截器,并最终返回HandlerExecutionChain对象HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);if (CorsUtils.isCorsRequest(request)) {CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;}
getHandlerExecutionChain():将getHandlerInternal()方法返回的Handler对象包装成HandlerExecutionChain对象。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {//判断如果当前的Handler本身是HandlerExecutionChain对象 则强转//否则创建HandlerExecutionChain对象HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));//根据request找寻对应的逻辑处理路径 lookupPathString lookupPath = this.urlPathHelper.getLookupPathForRequest(request);//遍历所有拦截器for (HandlerInterceptor interceptor : this.adaptedInterceptors) {//判断是否为MappedInterceptor类型if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;//判断该拦截器是否与当前请求地址适配if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {//将拦截器加入到当前请求的执行链中chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;}
二、AbstractUrlHandlerMapping类
简介
AbstractUrlHandlerMapping继承自AbstractHandlerMapping类,从名字可以看出该类是一个urlPath和Handler对象对一一对应的HandlerMapping。
换句话说一个rquest可以解析出一个对应的路径urlPath 则一个路径可能会匹配最符合的一个与之对应的Handler对象,所以其有一个存储url和Handler对象对应关系的Map集合属性handlerMap。其还有两个属性一个是与url “/”对应的特定Handler对象,以及是否Handler 懒加载 lazyInitHandlers。
AbstractUrlHandlerMapping类有如下属性:
// /与"/" 匹配的特定RootHandler 用户可以在配置AbstractUrlHandlerMapping实例进行设置private Object rootHandler;// 是否懒加载Handler 如果是true 则map中可以存储string,在使用的时候才从spring容器中进行加载private boolean lazyInitHandlers = false;// 用于存储url和handler匹配的map集合private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
类方法
registerHandler
对urlpath 和Handler进行处理存放。
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {Assert.notNull(urlPaths, "URL path array must not be null");for (String urlPath : urlPaths) {registerHandler(urlPath, beanName);}}protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {Assert.notNull(urlPath, "URL path must not be null");Assert.notNull(handler, "Handler object must not be null");Object resolvedHandler = handler;//lazyInitHandlers 为false 则不允许懒加载 对于Handler是string类型 需要从容器中获取到Handler实例存放到Map中//如果允许懒加载则 不需要进行实力获取 直接存储在使用的时候再从spring容器中获取if (!this.lazyInitHandlers && handler instanceof String) {String handlerName = (String) handler;if (getApplicationContext().isSingleton(handlerName)) {resolvedHandler = getApplicationContext().getBean(handlerName);}}//判断集合中对应的urlPath已存在且对应的Handler和要存储的Handler不一样抛出异常Object mappedHandler = this.handlerMap.get(urlPath);if (mappedHandler != null) {if (mappedHandler != resolvedHandler) {throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");}}else {//集合中对应的urlPath不存在//对于 / 和/*进行特殊处理 / 讲Handler 存储在rootHandler中 /* 设置为defaultHandlerif (urlPath.equals("/")) { if (logger.isInfoEnabled()) {logger.info("Root mapping to " + getHandlerDescription(handler));}setRootHandler(resolvedHandler);}else if (urlPath.equals("/*")) {if (logger.isInfoEnabled()) {logger.info("Default mapping to " + getHandlerDescription(handler));}setDefaultHandler(resolvedHandler);}else {//非特殊情况则存放进map中this.handlerMap.put(urlPath, resolvedHandler);if (logger.isInfoEnabled()) {logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));}}}}
getHandlerInternal
整个请求分为两部分,首先根据请求路径获取到对应的Handler,然后对于获取到的Handler进行包装构造。在该方法中核心的两个方式是lookupHandler方法和buildPathExposingHandler方法
@Overrideprotected Object getHandlerInternal(HttpServletRequest request) throws Exception {//通过UrlPathHelper对象从request解析出urlPathString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);//使用lookupPath 从HandlerMap中获取handler (精确匹配和Pattern匹配)Object handler = lookupHandler(lookupPath, request);//如果没有获取到 则可能是特殊路径或者该路径没有可以匹配的Handlerif (handler == null) {//如果没有获取到 则可能是特殊路径 需要特殊处理 比如 /路径匹配RootHandlerObject rawHandler = null;if ("/".equals(lookupPath)) {rawHandler = getRootHandler();}//没有匹配的Handler 则使用默认的handlerif (rawHandler == null) {rawHandler = getDefaultHandler();}//因为有可能有懒加载,需要对string类型的handler获取其实例if (rawHandler != null) {if (rawHandler instanceof String) {String handlerName = (String) rawHandler;rawHandler = getApplicationContext().getBean(handlerName);}//子类扩展 用于校验获取到的Handler是否匹配业务逻辑validateHandler(rawHandler, request);//返回添加了拦截器的HandlerExecutionChainhandler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);}}if (handler != null && logger.isDebugEnabled()) {logger.debug("Mapping [" + lookupPath + "] to " + handler);}else if (handler == null && logger.isTraceEnabled()) {logger.trace("No handler mapping found for [" + lookupPath + "]");}return handler;}
lookupHandler
该方法实现根据urlPath获取Handler。此时有两种情况 ,一是精确匹配,二是Pattern模式匹配,该方法中对这两种方式进行相关处理,其中Pattern模式匹配比较复杂,首先该方式可能会匹配到多个Handler,这里需要进行排序获取最优,同时针对最优有多个的情况进行处理,该方法中多次使用PathMatcher对象。
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {//先尝试从已有的映射关系中获取Object handler = this.handlerMap.get(urlPath);if (handler != null) {//获取Handler实例并构造HandlerExecuteChain对象返回if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}validateHandler(handler, request);return buildPathExposingHandler(handler, urlPath, urlPath, null);}//handler 支持Pattern匹配 比如 /manager/student/* 类似的Pattern//该方式下无法直接获取而且 需要使用一种匹配方法 一个pathern可能匹配多个url 则需要获取其最优的HandlerList<String> matchingPatterns = new ArrayList<String>();//遍历handlerMap 获取所有PathMatcher匹配成功的Handler存放集合中for (String registeredPattern : this.handlerMap.keySet()) {if (getPathMatcher().match(registeredPattern, urlPath)) {matchingPatterns.add(registeredPattern);}else if (useTrailingSlashMatch()) {if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {matchingPatterns.add(registeredPattern + "/");}}}//根据PathMatcher获取到的排序器 对matchingPatterns 进行匹配 获取最优的Handler对象String bestMatch = null;Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);if (!matchingPatterns.isEmpty()) {Collections.sort(matchingPatterns, patternComparator);if (logger.isDebugEnabled()) {logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);}bestMatch = matchingPatterns.get(0);}//如果有最优Handler 则获取Handler实例对象if (bestMatch != null) {handler = this.handlerMap.get(bestMatch);if (handler == null) {if (bestMatch.endsWith("/")) {handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));}if (handler == null) {throw new IllegalStateException("Could not find handler for best pattern match [" + bestMatch + "]");}}if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}validateHandler(handler, request);String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);//对于可能会有多个最优解的情况此处将所有的最优 即多个最优排序一样只是集合添加使其有先后Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();for (String matchingPattern : matchingPatterns) {if (patternComparator.compare(bestMatch, matchingPattern) == 0) {Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);uriTemplateVariables.putAll(decodedVars);}}if (logger.isDebugEnabled()) {logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);}return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);}// No handler found...return null;}
buildPathExposingHandler
为执行链增加两个拦截器PathExposingHandlerInterceptor与UriTemplateVariablesHandlerInterceptor。
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,String pathWithinMapping, Map<String, String> uriTemplateVariables) {HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));if (!CollectionUtils.isEmpty(uriTemplateVariables)) {chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));}return chain;}
实现类
从UML类图中可以大概清楚,在其抽象类下有两个分支,SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMappin,同时该类下还有一个子类BeanNameUrlHandlerMapping。在前文中我们介绍了AbstractUrlHandlerMapping类通过模板的方式实现了从请求request到Handler对象的主要逻辑,那么可想而知其后面的子类主要的功能点是初始化url和Handler的映射关系。
SimpleUrlHandlerMapping类
该类只有一个核心属性urlMap,用于存储url和handler的映射关系
private final Map<String, Object> urlMap = new LinkedHashMap<String, Object>();
initApplicationContext
因为该类间接实现了WebApplicationObjectSupport,所以该类重写了该方法。方法内容主要是初始化url和handler的映射关系,用于后续使用过程中的request请求映射。
@Overridepublic void initApplicationContext() throws BeansException {//调用父类的initApplicationContext()方法,设置相关的拦截器super.initApplicationContext();//核心方法注册相关urlMapregisterHandlers(this.urlMap);}
registerHandler
SimpleUrlHandlerMapping类重写了父类的registerHandler方法
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {if (urlMap.isEmpty()) {logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");}else {//遍历urlMap 对url进行处理for (Map.Entry<String, Object> entry : urlMap.entrySet()) {String url = entry.getKey();Object handler = entry.getValue();// 如果没有‘/’前缀,则添加前缀‘/’if (!url.startsWith("/")) {url = "/" + url;}// Remove whitespace from handler bean name.if (handler instanceof String) {handler = ((String) handler).trim();}//调用父类注册的核心方法最终将其存储在handlerMap集合registerHandler(url, handler);}}}
SimpleUrlHandlerMapping很简单,几乎所有的核心方法都在父类中,SimpleHandlerMapping只是用于做url映射的初始化,这种映射是直接匹配的形式,即通过request解析出其中的urlPath,根据urlPath路径直接从handlerMap中获取urlPath匹配的Handler对象。
AbstractDetectingUrlHandlerMapping类
AbstractDetectingUrlHandlerMapping类和上面SimpleUrlHandlerMapping的结构相关,都是重写了其中的initApplicationContext()方法,只是其注册逻辑不是需要显示的设置相关的url而是从spring容器中获取所有的bean实例,通过其BeanName(Handler对象)获取其匹配的url列表。
detectHandlers
该方法获取所有的beanName并获取其url列表后进行注册。
@Overridepublic void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();detectHandlers();}protected void detectHandlers() throws BeansException {if (logger.isDebugEnabled()) {logger.debug("Looking for URL mappings in application context: " + getApplicationContext());}//detectHandlersInAncestorContexts true 则获取其容器和父容器中所有的bean实例对象//为false 则只从当前容器对象中获取bean实例String[] beanNames = (this.detectHandlersInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));//遍历所有的beanName 根据beanName获取其逻辑匹配的urls 然后调用父类的registerHandler()方法注册相关的映射关系for (String beanName : beanNames) {//交由子类实现根据beanName获取url列表String[] urls = determineUrlsForHandler(beanName);if (!ObjectUtils.isEmpty(urls)) {registerHandler(urls, beanName);}else {if (logger.isDebugEnabled()) {logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");}}}}
BeanNameUrlHandlerMapping#determineUrlsForHandler
@Overrideprotected String[] determineUrlsForHandler(String beanName) {List<String> urls = new ArrayList<String>();//使用beanName 作为urlif (beanName.startsWith("/")) {urls.add(beanName);}String[] aliases = getApplicationContext().getAliases(beanName);//bean实例的所有别名作为urlfor (String alias : aliases) {if (alias.startsWith("/")) {urls.add(alias);}}return StringUtils.toStringArray(urls);}
本文我们整理了HandlerMapping接口、AbstractHandlerMapping类、AbstractUrlHandlerMapping类,介绍了URL与类对应关系的映射注册,下文我们将介绍AbstractHandlerMethodMapping类与我们最常用的实现类RequestMappingHandlerMapping。