04-RequestMappingHandlerMapping

RequestMappingHandlerMapping

  • HandlerMapping是SpringMVC的策略组件之一,称作处理器映射器,其作用是根据请求获取到对应的处理器,是HandlerMapping的实现类。
  • RequestMappingHandlerMapping是HandlerMapping接口的一个实现类,用于根据请求来获取处理器对象(实际是获取HandlerExecutionChain),按照调试过程发现真正处理的HandlerMapping实现类是RequestMappingHandlerMapping,因此作为HandlerMapping接口的典型实现类来分析它。

一、初始化

1.1 继承关系

在这里插入图片描述

  • RequestMappingHandlerMapping经过几层继承关系,最终是继承自AbstractHandlerMapping这个HandlerMapping的抽象实现类。因为继承关系复杂,我们按照主流程来看看关键代码。主要是初始化扫描Handler处理器、获取Handler处理器扫描拦截器。
  • 这里的Handler处理器抽象来说就是指我们处理请求的方法,比如我们代码中写的@PostMapping的方法,但是在框架内部可以回进行一定的包装以保存方法的更多信息。

1.2 Handler初始化

  • RequestMappingHandlerMapping实现了InitializingBean接口,对应的afterPropertiesSet方法会在初始化阶段被调用,我们先看看RequestMappingHandlerMapping的afterPropertiesSet方法会完成什么。(关于InitializingBean接口的afterPropertiesSet方法的调用时机在Bean的生命周期中很关键,可以参考:spring bean的生命周期初探 )
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements MatchableHandlerMapping, EmbeddedValueResolverAware {
	
	//****省略****
			
	@Override
	public void afterPropertiesSet() {
	    //1.初始化配置
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());
        
        //2.调用AbstractHandlerMethodMapping中afterPropertiesSet的逻辑
		super.afterPropertiesSet();
	}

		//****省略****
}
  • 可以看到RequestMappingHandlerMapping在afterPropertiesSet回调方法中并未做太多操作,主要是做了一些配置的赋值,主要的事情父类 AbstractHandlerMethodMapping已经完成了,只需要super调用即可。
  • AbstractHandlerMethodMapping#afterPropertiesSet:这是初始化的核心流程
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
  • 主要逻辑在initHandlerMethods方法内部,代码如下:
  • 小结一下initHandlerMethods流程就是获取全部的Bean,然后根据注解去判断是否需要处理,需要处理的就将类和方法的注解信息封装成对象保存到内部的MappingRegistry,MappingRegistry内部会有几个Map分类保存映射信息,最后有一个扩展的回调方法。
	protected void initHandlerMethods() {
	    //1.获取容器中全部的Bean的名称
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));

        //2.根据名字获取类,判断该类是不是要转换的类型
		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = obtainApplicationContext().getType(beanName);
				}
				catch (Throwable ex) {
					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
					}
				}
				//3.处理有Controller注解和RequestMapping注解的Bean
				if (beanType != null && isHandler(beanType)) {
					detectHandlerMethods(beanName);
				}
			}
		}
		//4.所有映射方法都处理完成之后的回调方法,交给子类扩展,我貌似尚未找到实现方法,或许以后会扩展
		handlerMethodsInitialized(getHandlerMethods());
	}
	
	//根据Controller和RequestMapping注解来决定Bean是否需要处理
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}
  • detectHandlerMethods: 遍历并且获取方法、信息封装合并并注册的主流程;(有些注解在类上,有些注解在方法上,需要合并)
    protected void detectHandlerMethods(final Object handler) {
		//1.根据Bean名称获取Bean,或者根据类型获取Bean
		Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
		    //2.获取类对象
			final Class<?> userType = ClassUtils.getUserClass(handlerType);
			//3.获取并且遍历类的所有方法
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
						    //4.getMappingForMethod方法用于返回处理器的对应方法,这个逻辑是在RequestMappingHandlerMapping
						    //中实现的,在AbstractHandlerMethodMapping中没有实现
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
            //5.注册扫描到的handler的方法
			for (Map.Entry<Method, T> entry : methods.entrySet()) {
			    //Aop方法的处理
				Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
				T mapping = entry.getValue();
				registerHandlerMethod(handler, invocableMethod, mapping);
			}
		}
	}
  • registerHandlerMethod:将处理器方法注册到MappingRegistry属性内部的集合,
	//注册扫描到的handler的方法,本质是保存在一个Map集合中
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}
	
	//注册请求和处理器映射关系
	public void register(T mapping, Object handler, Method method) {
	        //1.加锁
			this.readWriteLock.writeLock().lock();
			try {
			    //2.新建HandlerMethod对象
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);
                //3.加入mappingLookup,请求对象对应的全部处理方法对象映射集合
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				//4.加入urlLookup,Url映射集合,获取请求路径,并将路径和处理器的映射关系保存到urlLookup
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

                //5.加入nameLookup,
				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}
                
                //5.加入corsLookup,跨域相关
				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}
                
                //6.加入registry集合 RequestMappingInfo -> MappingRegistration的映射关系
				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally { 
				this.readWriteLock.writeLock().unlock();//解锁
			}
		}
  • getMappingForMethod: detectHandlerMethods中重写的用于从处理器类中获取到全部的处理方法,封装成一个RequestMappingInfo 对象返回,方法由RequestMappingHandlerMapping实现,定义在AbstractHandlerMethodMapping;
	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
			    //信息合并
				info = typeInfo.combine(info);
			}
		}
		return info;
	}

1.3 MappingRegistry

  • AbstractHandlerMethodMapping.MappingRegistry是AbstractHandlerMethodMapping的一个内部类,用于保存映射关系,其内部通过几个Map保存了请求和处理器之间的映射关系;
private final MappingRegistry mappingRegistry = new MappingRegistry();

class MappingRegistry {
	    //RequestMappingInfo -> MappingRegistration的映射关系
		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

	    //RequestMappingInfo -> HandlerMethod的映射关系
		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

	    //请求的Url -> RequestMappingInfo的映射关系,这里的T是一个LinkedHashMap,
	    //一个Url可能映射到多个RequestMappingInfo
	    //这些RequestMappingInfo就保存在一个LinkedHashMap里面
		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
}
  • 到这主体的初始化流程我们基本上摸清楚了,通过InitializingBean接口的afterPropertiesSet回调方法,扫描全部的Bean,得到处理器类和方法,封装好并通过映射关系保存在MappingRegistry属性的内部集合中,集合中包括多种映射关系。
  • 不过还有一个疑问,RequestMappingHandlerMapping的afterPropertiesSet方法被回调,说明它自身也作为一个Bean在被初始化,入口是啥?没什么特别的,RequestMappingHandlerMapping也就是一个业务Bean,入口就是refresh -> finishBeanFactoryInitialization -> 进去根据Bean定义的BeanName初始化,本身没有什么特殊的。

1.4 RequestMappingHandlerMapping初始化入口

  • RequestMappingHandlerMapping初始化和业务注册的Bean一样
AbstractApplicationContext#refresh
ConfigurableListableBeanFactory#preInstantiateSingletons
AbstractBeanFactory#getBean(java.lang.String)
AbstractBeanFactory#doGetBean
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

在这里插入图片描述

二、获取Handler

  • 由前面的内容我们知道了SpringMvc框架初始化RequestMappingHandlerMapping的过程,在初始化RequestMappingHandlerMapping的过程在,通过InitializingBean接口方法完成对Handler处理器方法的扫描并注册到内部的注册中心。后面我们来看看当框架收到有一个请求时候,如何找到对应的处理器方法。

2.1 HandlerMapping.getHandler

  • getHandler方法定义在顶层接口HandlerMapping,通过HttpServletRequest参数,来获取该请求对应的处理器链对象HandlerExecutionChain,我们看看getHandler方法在AbstractHandlerMapping中的实现逻辑:
    @Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//1.获取到处理request的HandlerMethod对象(参考2.2)
		Object handler = getHandlerInternal(request);
		//2.没有获取到就使用默认的
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		//3.如果获取到的是String类型的BeanName,就从容器获取对应的Bean实例
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
        //4.获取执行链(获取执行链的细节和Spring监听器有关,可以后面再分析,即使自己没有写拦截器,默认也有框架内部的拦截器)
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		//5.支持跨域的请求处理,默认不是的
		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);
		}
		//6.返回执行链
		return executionChain;
	}

2.2 getHandlerInternal

  • getHandlerInternal方法根据request请求对象获取HandlerMethod对象,HandlerMethod封装了目标对象和目标方法等信息。
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		//1.获取请求相对路径,比如"/hello"
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		//2.加读锁
		this.mappingRegistry.acquireReadLock();
		try {
		    //3.通过请求路径和请求对象寻找HandlerMethod处理对象
		    //HandlerMethod封装了目标方法等信息
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			//4.返回handlerMethod;createWithResolvedBean方法是将String类型的BeanName替换为从beanFactory获取的真正实例
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
  • lookupHandlerMethod:根据Url和映射关系找到请求对应的处理方法对象HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		//1.从urlLookup获取url对应映射的RequestMappingInfo集合
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		//2.如果找到了,就把加到matches这个匹配集合
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		//3.如果集合为空,说明没找到,此时就扫描所有的映射集合
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			//4.找到最匹配的映射对象
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}
  • 到此我们对获取处理器的流程有了大概的眉目。通过HandlerMapping的getHandler方法获取HandlerExecutionChain处理器执行链,获取过程如下:
通过请求对象获取HandlerMethod处理器对象->
先根据Url获取,如果获取失败则会根据全部集合去寻找,
如果匹配对象一样会抛出异常
然后根据HandlerMethod获取执行链HandlerExecutionChain
  • 下图是获取到处理器执行链之后的截图,内部包含处理器方法和拦截器链

在这里插入图片描述

三、拦截器初始化

  • 拦截器初始化的初始化原本应该在第一步分析,因为从时机来说它在Handler初始化之前,我们前面看到过获取Handler处理器其实获取到的是一个执行链,内部封装了处理器Handler和一系列的拦截器,而且调试过程发现默认是有拦截器的,也可自定义,那么获取这些自定义的拦截器是在什么时候?我们慢慢分析。

3.1 定义拦截器

  • 定义一个拦截器
@Component
public class TestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("preHandle...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ...");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion...");
    }
}
  • 注册前面的拦截器
@Component
public class RequestInterceptorRegister extends WebMvcConfigurerAdapter {

    @Autowired
    TestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor).addPathPatterns("/**");
    }
}
  • 到此就可以实现一个自定义的拦截器来拦截所有的请求,不过具体拦截哪一些请求是可以配置的,前面的addPathPatterns("/**")是拦截全部的请求。

3.2 加载拦截器

  • 回顾Bean的生命周期,在Bean的初始化之前会回调Aware系列接口(如果Bean实现了Aware系列接口,比如ApplicationContextAware),而我们的RequestMappingHandlerMapping继承了ApplicationObjectSupport,ApplicationObjectSupport是实现了ApplicationContextAware接口的,因此就会在这里回调ApplicationContextAware#setApplicationContext方法,在ApplicationObjectSupport父类中的setApplicationContext方法中实现了部分逻辑,并留下了Protected方法initApplicationContext给子类重写,而RequestMappingHandlerMapping正是通过这个重写的接口来完成拦截器的初始化的。(我们先看看下面的调用栈,然后分析源码)

在这里插入图片描述

3.2.1 AbstractHandlerMapping#initApplicationContext
  • 先是invokeAwareInterfaces调用Aware系列接口,然后走到ApplicationObjectSupport的setApplicationContext,最终会回调AbstractHandlerMapping的initApplicationContext方法,代码如下:
    //AbstractHandlerMapping#initApplicationContext
	@Override
	protected void initApplicationContext() throws BeansException {
	    //1.空方法,子类扩展,子类可以在这里添加拦截器
		extendInterceptors(this.interceptors);
		//2.侦测容器中的MappedInterceptor,并添加到adaptedInterceptors属性集合(一个List)
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}
  • 再看看是如何在容器中侦测拦截器的
	protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
		mappedInterceptors.addAll(
				BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false).values());
	}
  • 下面是根据类型获取Bean的beansOfTypeIncludingAncestors方法,beansOfTypeIncludingAncestors方法会返回一个Map集合,因此前面通过values返回所有的拦截器Bean,这个方法内部代码不解析了,他的内部直接调用IOC容器根据类型去获取Bean对象,调试的时候发现,自己写的拦截器和框架内部的拦截器其实已经设置到interceptors属性了,也就是说initApplicationContext方法开始执行的时候,实际上拦截器早在此之前就扫描好了,由此推测拦截器的加载在此之前就加载好了,如下图所示:
BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false)

在这里插入图片描述

  • 根据这点往回走,发现是在构造RequestMappingHandlerMapping这个Bean的时候设置的,代码如下:
//WebMvcConfigurationSupport#line280
    @Bean
	public RequestMappingHandlerMapping requestMappingHandlerMapping() {
		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);//可以将断点打在这一行调试进入下面的getInterceptors方法
		mapping.setInterceptors(getInterceptors()); //设置拦截器
		mapping.setContentNegotiationManager(mvcContentNegotiationManager());
		mapping.setCorsConfigurations(getCorsConfigurations());

		PathMatchConfigurer configurer = getPathMatchConfigurer();
		Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
		Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
		Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
		if (useSuffixPatternMatch != null) {
			mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
		}
		if (useRegisteredSuffixPatternMatch != null) {
			mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
		}
		if (useTrailingSlashMatch != null) {
			mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
		}

		UrlPathHelper pathHelper = configurer.getUrlPathHelper();
		if (pathHelper != null) {
			mapping.setUrlPathHelper(pathHelper);
		}

		PathMatcher pathMatcher = configurer.getPathMatcher();
		if (pathMatcher != null) {
			mapping.setPathMatcher(pathMatcher);
		}

		return mapping;
	}
  • 而这里面设置拦截器的时候,通过getInterceptors() 方法获取拦截器,这比较关键:
//WebMvcConfigurationSupport#line329
	protected final Object[] getInterceptors() {
	    //1.首次进来是null
		if (this.interceptors == null) {
			InterceptorRegistry registry = new InterceptorRegistry();
			//2.将DelegatingWebMvcConfiguration的configurers中保存的拦截器添加到registry对象
			addInterceptors(registry);
			//3.添加系统拦截器
			registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
			registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
			this.interceptors = registry.getInterceptors();
		}
		//4.返回全部拦截器
		return this.interceptors.toArray();
	}
  • 到此我们不需要往RequestMappingHandlerMapping的构造方法后面看了,我们知道了构造方法给RequestMappingHandlerMapping注入了全部的拦截器,但是拦截器是从DelegatingWebMvcConfiguration的configurers属性中得到的,那么configurers属性是什么时候赋值的?什么时候将全部的拦截器扫描然后保存到这个属性的?这就需要看后面的DelegatingWebMvcConfiguration的初始化过程了。
3.2.2 DelegatingWebMvcConfiguration
  • 从前面一步一步我们都没有找到框架扫描拦截器的最早的地方,实际上是依靠DelegatingWebMvcConfiguration这个类来实现自定义拦截器的扫描和注册的,如下是关键代码。这个set注入方法将所有的WebMvcConfigurer组件都注册进去,而我们前面注册自定义的拦截器就是通过实现WebMvcConfigurerAdapter来注册的,(WebMvcConfigurerAdapter是WebMvcConfigurer的子类,因此我们的RequestInterceptorRegister也会在这里进行注入,并且会执行addWebMvcConfigurers方法将传入的拦截器对象添加到configurers属性)。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
    @Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
		    //1.遍历集合,将全部的连接器保存到configurers属性 (最终保存到了configurers对象的delegates属性)
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}
}
  • WebMvcConfigurerComposite通过下面的addWebMvcConfigurers方法将所有的拦截器添加到delegates集合中去
class WebMvcConfigurerComposite implements WebMvcConfigurer {
	
	private final List<WebMvcConfigurer> delegates = new ArrayList<>();

	public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.delegates.addAll(configurers);
		}
	}
}
  • 进一步的问题是,这个setConfigurers方法是什么时候执行并扫描全部的拦截器组件的?而且我还有一个疑问就是,如果在实例化DelegatingWebMvcConfiguration这个Bean的过程中执行setConfigurers方法,此时我们的拦截器Bean尚未初始化好怎么办?这里并不是循环依赖,如果是循环依赖可以先创建需要依赖的Bean,带着这个疑问进一步看。
  • 回头想想@Autowire的大概原理机制是怎么样的?可以参考:AutowiredAnnotationBeanPostProcessor
    在WebMvcAutoConfiguration$EnableWebMvcConfiguration这个Bean的实例化过程中,在populateBean阶段会进入属性注入,(这里需要一些Bean生命周期的知识和对后置处理器的了解),在@Autowire字段注入的时候,依赖AutowiredAnnotationBeanPostProcessor处理器的处理,对setConfigurers方法进行处理。大概的调用栈我写出来了,并没有一一分析每一个方法。
//处理setConfigurers方法的调用栈,在populateBean阶段处理
AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject(line 622)
DefaultListableBeanFactory#resolveDependency(line 1047)
DefaultListableBeanFactory#doResolveDependency(line 1072)
DefaultListableBeanFactory#resolveMultipleBeans(line 1158)
DefaultListableBeanFactory#findAutowireCandidates(line 1273)

BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<?>, boolean, boolean) (line 218)
DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean) (line 388)
DefaultListableBeanFactory#doGetBeanNamesForType (line405)

  • 最后我们看看DefaultListableBeanFactory#findAutowireCandidates方法,该方法就是最终的@Autowire 方法注入的时候,根据BeanType去寻找目标Bean注入到指定BeanName的实例,参数如下:
beanName=WebMvcConfigurer
requiredType=WebMvcConfigurer(我们定义的拦截器就是这个类型的子类)
  • 调试图如下:

在这里插入图片描述

  • 到这我们基本知道了,DelegatingWebMvcConfiguration就是 通过@Autowire注入了全部的WebMvcConfigurer类型的Bean,我们自己写的拦截器也是在这一步注入的,不过后面的DefaultListableBeanFactory#doGetBeanNamesForType 方法,根据名字找Bean的类型,方法可以看看:
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		// Check all bean definitions.
		for (String beanName : this.beanDefinitionNames) {
			// Only consider bean as eligible if the bean name
			// is not defined as alias for some other bean.
			if (!isAlias(beanName)) {
				try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
					if (!mbd.isAbstract() && (allowEagerInit ||
							((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
						// In case of FactoryBean, match object created by FactoryBean.
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
						boolean matchFound =
								(allowEagerInit || !isFactoryBean ||
										(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
								(includeNonSingletons ||
										(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
								isTypeMatch(beanName, type);
						if (!matchFound && isFactoryBean) {![在这里插入图片描述](https://img-blog.csdnimg.cn/20191105201555785.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L215X21vbW9fY3Nkbg==,size_16,color_FFFFFF,t_70)
							// In case of FactoryBean, try to match FactoryBean instance itself next.
							beanName = FACTORY_BEAN_PREFIX + beanName;
							matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
						}
						if (matchFound) {
							result.add(beanName);
						}
					}
				}
				catch (CannotLoadBeanClassException ex) {
				    //省略...
				}
			}
		}
        //省略....		
		return StringUtils.toStringArray(result);
}
  • 从这里我们看到,他说从beanDefinitionNames中去寻找的,而不是直接去找已经创建好了的Bean,我想这应该是不必担心Bean创建顺序影响对拦截器扫描的原因吧。
  • 下图可以看到,根据WebMvcConfigurer类型找到了两个Bean,其中一个是自定义的拦截器。

在这里插入图片描述

  • 这一小节我们倒序梳理了拦截器的初始化大概的脉络。总结来说的话是通过DelegatingWebMvcConfiguration的初始化过程,通过@Autowire注解注入属性,其内部通过Bean的类型去到beanDefinitionNames中获取Bean名称,然后根据名称去IOC获取Bean实例。

四、小结

最后我们按照时序梳理一下:首先是扫描拦截器,流程如下:

在DelegatingWebMvcConfiguration的初始化过程,在其生命周期的属性注入阶段处理@Autowire的时候,通
过setConfigur方法,从beanDefinitionNames中创建并获取全部WebMvcConfigurer类型的Bean,然后保存在
其内部属性
  • 然后是RequestMappingHandlerMapping的创建,构造方法部分
在RequestMappingHandlerMapping的创建过程中,其构造方法中会调用setInterceptors初始化内部的拦截器
属性,这一步是在WebMvcConfigurationSupport中创建的,他会将子类DelegatingWebMvcConfiguration中已经
初始化好的拦截器赋值给RequestMappingHandlerMapping
  • 然后是RequestMappingHandlerMapping建立请求和处理器映射关系
RequestMappingHandlerMapping构造方法执行完毕之后,后面会经过Bean的生命周期中的InitializingBean接口回调,
借助该接口的afterPropertiesSet方法,在该方法中做初始化的过程,主要是在父类AbstractHandlerMethodMapping中
完成。他会从继承的ApplicationObjectSupport中获取到上下文容器applicationContext,从中获取到全部的Bean,然
后根据bean是否有Controller或者RequestMapping注解来决定是否需要处理这个Bean,对于我们写的Controller类肯定
是需要处理的,继而会获取上面的RequestMapping信息并封装成一个RequestMappingInfo对象,这里注意如果类型和方
法都添加了RequestMapping注解,会将两份信息都获取了并做合并。(RequestMapping注解既可以在类上也可以在方法上)。
然后将映射关系保存到Map集合,key是RequestMappingInfo对象或者Url,value是一个MappingRegistration对象,其内
部封装了RequestMappingInfo,处理方法(handlerMethod),Url(directUrls)等信息。
  • 最后是Handler的获取
获取的时候,去RequestMappingHandlerMapping中获取处理器,首先会从请求对象中获取请求路径,根据Url去获取,
过程中会有一些完善机制,比如获取不到,异常等。获取到的是一个执行链,内部封装了目标方法和拦截器。
  • 下面是我画的简图

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值