> 文章列表 > SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

回顾:

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {if (this.contextId != null) {wac.setId(this.contextId);}else {wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());}}//将ServletContext和ServletConfig都绑定到servlet子上下文对象中wac.setServletContext(getServletContext());wac.setServletConfig(getServletConfig());wac.setNamespace(getNamespace());wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());}postProcessWebApplicationContext(wac);applyInitializers(wac);//最后初始化子容器,和上面根容器初始化一样wac.refresh();}

在前面容器创建完成后,调用refresh方法时,spring会把所有的单例bean加载完成刷新后,会调用onApplicationEvent()监听事件,紧接着就会在调用DispatcherServlet的onRefresh()方法,进行9大组件的加载

   protected void onRefresh(ApplicationContext context) {this.initStrategies(context);}protected void initStrategies(ApplicationContext context) {this.initMultipartResolver(context);this.initLocaleResolver(context);this.initThemeResolver(context);this.initHandlerMappings(context);this.initHandlerAdapters(context);this.initHandlerExceptionResolvers(context);this.initRequestToViewNameTranslator(context);this.initViewResolvers(context);this.initFlashMapManager(context);}

Spring MVC九大组件的名称和作用。
SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings


由于精力有限,就通过initHandlerMappings的源码来看看springMVC是怎么去寻找我们设置的controller的,怎么去找到存储我们对RequestMapping对应请求的。

initHandlerMappings:

HandlerMapping是用来查找Handler的,也就是处理器,具体的表现形式可以是类,也可以是方法。比如,标注@RequestMapping的每个方法都可以看成一个Handler。Handler负责实际的请求处理,在请求到达后,HandlerMapping的作用便是找到请求相应的处理器Handler和Interceptor。流程图如下

SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

  1. initHandlerMappings
    这一步主要是springmvc看spring容器是否有对应handlemapping的Bean对象,如果没有就去创建自己配置的
    private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;// 在spring容器找是否有符合的HandlerMapping的Beanif (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);}} else {try {HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);} catch (NoSuchBeanDefinitionException var4) {}}// 如果没有就去创建springMVC自己的handleMappingif (this.handlerMappings == null) {this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}Iterator var6 = this.handlerMappings.iterator();while(var6.hasNext()) {HandlerMapping mapping = (HandlerMapping)var6.next();if (mapping.usesPathPatterns()) {this.parseRequestPath = true;break;}}}

2.getDefaultStrategies

这一步就是spring容器如果没有handlemapping,那么就会从DispatcherServlet.properties配置文件去读取相应的bean,配置文件如下

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {if (defaultStrategies == null) {try {// 查找springmvc自己的DispatcherServlet.properties文件,获取对应的配置ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);} catch (IOException var15) {throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + var15.getMessage());}}String key = strategyInterface.getName();String value = defaultStrategies.getProperty(key);if (value == null) {return Collections.emptyList();} else {String[] classNames = StringUtils.commaDelimitedListToStringArray(value);List<T> strategies = new ArrayList(classNames.length);String[] var7 = classNames;int var8 = classNames.length;for(int var9 = 0; var9 < var8; ++var9) {String className = var7[var9];try {  Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());// 获取配置文件的bean进行创建Object strategy = this.createDefaultStrategy(context, clazz);strategies.add(strategy);} catch (ClassNotFoundException var13) {throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);} catch (LinkageError var14) {throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var14);}}return strategies;}}

DispatcherServlet.properties配置文件,比如我们HandlerMapping就可以在配置文件中获取三个类RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,RouterFunctionMapping等。获取到之后就会循环遍历去创建相应的bean。

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverHandlerMapping
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\\org.springframework.web.servlet.function.support.RouterFunctionMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\\org.springframework.web.servlet.function.support.HandlerFunctionAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

3.createDefaultStrategy
在会去调用createDefaultStrategy方法去创建spring的bean对象。

 protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {return context.getAutowireCapableBeanFactory().createBean(clazz);}

RequestMappingHandlerMapping对象创建:

SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings
根据RequestMappingHandlerMapping的类继承图我们发现,RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,也就意味着在我们spring的生命周期中,spring容器在创建bean时如果实现了InitializingBean接口,那就会回调我们实现的afterPropertiesSet()方法。流程图如下
收集RequestMapping()请求的bean。
SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

  1. 调用RequestMappingHandlerMapping重写的 afterPropertiesSet方法()
    public void afterPropertiesSet() {this.config = new RequestMappingInfo.BuilderConfiguration();this.config.setTrailingSlashMatch(this.useTrailingSlashMatch());this.config.setContentNegotiationManager(this.getContentNegotiationManager());if (this.getPatternParser() != null) {this.config.setPatternParser(this.getPatternParser());Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser.");} else {this.config.setSuffixPatternMatch(this.useSuffixPatternMatch());this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch());this.config.setPathMatcher(this.getPathMatcher());}// 调用父类的AbstractHandlerMethodMapping的afterPropertiesSet();super.afterPropertiesSet();}

2.在调用父类AbstractHandlerMethodMapping的afterPropertiesSet()方法

    public void afterPropertiesSet() {this.initHandlerMethods();}protected void initHandlerMethods() {String[] var1 = this.getCandidateBeanNames();int var2 = var1.length;// 获取所有beanfor(int var3 = 0; var3 < var2; ++var3) {String beanName = var1[var3];if (!beanName.startsWith("scopedTarget.")) {// this.processCandidateBean(beanName);}}this.handlerMethodsInitialized(this.getHandlerMethods());}

这里就是获取所有bean,在判断bean的类型是否是controller

    protected void processCandidateBean(String beanName) {Class<?> beanType = null;try {// 获取每个bean的类型beanType = this.obtainApplicationContext().getType(beanName);} catch (Throwable var4) {if (this.logger.isTraceEnabled()) {this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);}}// 调用RequestMappingHandlerMapping的isHandler方法if (beanType != null && this.isHandler(beanType)) {this.detectHandlerMethods(beanName);}}

3.调用RequestMappingHandlerMapping的isHandler方法

其实这里就是判断当前bean是否是controller的bean,如果是紧接着就调用detectHandlerMethods()方法查找controller的bean对应的方法是不是requestmapping请求的。

   protected boolean isHandler(Class<?> beanType) {return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);}

4.调用 父类AbstractHandlerMethodMapping的detectHandlerMethods()方法

  protected void detectHandlerMethods(Object handler) {Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();if (handlerType != null) {Class<?> userType = ClassUtils.getUserClass(handlerType);// 循环所有方法Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {try {// 调用子类RequestMappingHandlerMapping的方法return this.getMappingForMethod(method, userType);} catch (Throwable var4) {throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);}});if (this.logger.isTraceEnabled()) {this.logger.trace(this.formatMappings(userType, methods));} else if (this.mappingsLogger.isDebugEnabled()) {this.mappingsLogger.debug(this.formatMappings(userType, methods));}//把请求存储到mappingRegistry中methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);this.registerHandlerMethod(handler, invocableMethod, mapping);});}}

调用RequestMappingHandlerMapping

这里就找相应controller下面的方法是否有requestmapping注解

RequestMappingInfo 就是存储你当前的请求路径以及你的请求是get还是post,所以这里会对类和方法是requestmapping做拼接。如果存储在RequestMappingInfo 当中,

  @Nullableprotected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {// 方法上的requestmappingRequestMappingInfo info = this.createRequestMappingInfo(method);if (info != null) {// 类上的requestmappingRequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);if (typeInfo != null) {info = typeInfo.combine(info);}// 合并路径String prefix = this.getPathPrefix(handlerType);if (prefix != null) {info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);}}return info;}
  1. 存储在mappingRegistry Map当中

然后将处理好的请求都存储在Map<Method, T> methods当中进行存储SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings
然后循环遍历存储在我们 mappingRegistry 的map中,而这个map就是我们后面所请求requestmaping需要的map。

   private final AbstractHandlerMethodMapping<T>.MappingRegistry mappingRegistry = new MappingRegistry();

SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings


BeanNameUrlHandlerMapping对象创建:

BeanNameUrlHandlerMapping和上面RequestMappingHandlerMapping初始化是差不多的,只不过的是他的父类ApplicationObjectSupport实现了ApplicationContextAware接口,也就是在spring生命周期周期过程中会进行aware回调的工作。
SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

BeanNameUrlHandlerMapping主要是收集bean名称带“/”的bean,并且实现Controller接口。
SpringMVC(二):加载SpringMVC的九大组件之initHandlerMappings

  1. 回调ApplicationObjectSupport的setApplicationContext方法,进行aware回调
    public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {if (context == null && !this.isContextRequired()) {this.applicationContext = null;this.messageSourceAccessor = null;} else if (this.applicationContext == null) {if (!this.requiredContextClass().isInstance(context)) {throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]");}this.applicationContext = context;this.messageSourceAccessor = new MessageSourceAccessor(context);// 执行子类方法,遍历bean寻找请求this.initApplicationContext(context);} else if (this.applicationContext != context) {throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]");}}

2.调用父类AbstractDetectingUrlHandlerMapping的initApplicationContext方法

    public void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();this.detectHandlers();}

遍历所有bean处理对应请求,进行存储

  protected void detectHandlers() throws BeansException {ApplicationContext applicationContext = this.obtainApplicationContext();// 1.拿到所有的beanString[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class);String[] var3 = beanNames;int var4 = beanNames.length;// 循环遍历beanfor(int var5 = 0; var5 < var4; ++var5) {String beanName = var3[var5];// 查看当前bean是否以斜线开头,如果是斜线开头就保存。@Component("/test")String[] urls = this.determineUrlsForHandler(beanName);if (!ObjectUtils.isEmpty(urls)) {// 然后存储至一个map当中//   private final Map<String, Object> handlerMap = new LinkedHashMap();this.registerHandler(urls, beanName);}}if (this.mappingsLogger.isDebugEnabled()) {this.mappingsLogger.debug(this.formatMappingName() + " " + this.getHandlerMap());} else if (this.logger.isDebugEnabled() && !this.getHandlerMap().isEmpty() || this.logger.isTraceEnabled()) {this.logger.debug("Detected " + this.getHandlerMap().size() + " mappings in " + this.formatMappingName());}}

后面的RouterFunctionMapping就不在进行详解了。