thomas0yang 2019-06-27
本系列文章是基于Spring5.0.5RELEASE。
在分析类之前,先了解下类的继承关系,如下图:
红框的类就是我们本章要分析的类。
从类图关系上可以看出,SimpleUrlHanderMapping类最终实现了ApplicationContextAware接口,该接口定义了方法setApplicationContext(applicationContext),其作用是实现该接口的类,在Spring实例化类时,自动调用setApplicationContext(applicationContext)方法。
ApplicationObjectSupport抽象类实现了ApplicationContextAware接口的setApplicationContext(applicationContext)方法。
由于WebApplicationObjectSupport抽象类重写了父类ApplicationObjectSupport的initApplicationContext(context)方法,所以此时会调用WebApplicationObjectSupport的initAppliationContext(context)方法,在该方法中通过suppr调用父类的initApplicationContext(context)方法,该方法通过模板方法模式最终调到SimpleUrlHandlerMapping类的initApplicationContext()方法。
整个创建涉及的类比较多,过程比较复杂,文字描述也很乏味,所以我画了调用时序图,可供大家参考:
分析到此,我们就找到了SimpleUrlHandlerMapping类的入口方法,即本类的initApplicationContext()方法。
该类间接实现了org.springframework.web.servlet.HandlerMapping接口,直接实现该接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象类,映射Url与请求handler bean。支持映射bean实例和映射bean名称。源代码如下:
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { // 存储url和bean映射 private final Map<String, Object> urlMap = new LinkedHashMap<>(); // 注入property的name为mappings映射 public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } // 注入property的name为urlMap映射 public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); } public Map<String, ?> getUrlMap() { return this.urlMap; } // 实例化本类实例入口 @Override public void initApplicationContext() throws BeansException { // 调用父类AbstractHandlerMapping的initApplicationContext方法,只要完成拦截器的注册 super.initApplicationContext(); // 处理url和bean name,具体注册调用父类完成 registerHandlers(this.urlMap); } // 注册映射关系,及将property中的值解析到map对象中,key为url,value为bean id或name protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { urlMap.forEach((url, handler) -> { // 增加以"/"开头 if (!url.startsWith("/")) { url = "/" + url; } // 去除handler bean名称的空格 if (handler instanceof String) { handler = ((String) handler).trim(); } // 调用父类AbstractUrlHandlerMapping完成映射 registerHandler(url, handler); }); } } }
从以上代码可知,SimpleUrlHandlerMapping类主要接收用户设定的url与handler的映射关系,其实际的工作都是交由其父类来完成的。
在创建初始化SimpleUrlHandlerMapping类时,调用其父类的initApplicationContext()方法,该方法完成拦截器的初始化,代码如下:
@Override protected void initApplicationContext() throws BeansException { // 空实现。 // 子类可重写此方法以注册额外的拦截器 extendInterceptors(this.interceptors); // 从上下文中查询拦截器并添加到拦截器列表中 detectMappedInterceptors(this.adaptedInterceptors); // 初始化拦截器 initInterceptors(); } // 查找实现了MappedInterceptor接口的bean,并添加到映射拦截器列表 protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); } // 将自定义bean设置到适配拦截器中,bean需实现HandlerInterceptor或WebRequestInterceptor 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)); } } }
在创建初始化SimpleUrlHandlerMapping类时,调用AbstractUrlHandlerMapping类的registerHandler(urlPath,handler)方法,该方法源码如下:
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; // 不是懒加载,默认为false,即不是,通过配置SimpleUrlHandlerMapping属性lazyInitHandlers的值进行控制 // 如果不是懒加载并且handler为单例,即从上下文中查询实例处理,此时resolvedHandler为handler实例对象; // 如果是懒加载或者handler不是单例,即resolvedHandler为handler逻辑名 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); // 如果handler是单例,通过bean的scope控制 if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } 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 { if (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 { // 把url与handler(名称或实例)放入map,以供后续使用 this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
到此,SimpleUrlHandlerMapping类在容器启动期间的初始化完成。
本文分析了SimpleUrlHandlerMapping类初始化过程,其实核心就是把url和handler进行了映射,供后续访问使用,单靠看文章无法掌握。整个过程调用很复杂,大家多debug跟踪,一定能了解其内部的逻辑。大家共勉!
最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!