springMVC前端控制器九大组件之一HandlerMapping的初始化

前言

这篇文章里:Spring MVC容器启动时,web九大组件初始化详解,已经大概介绍过web九大组件;
在这篇文章里:springMVC前端控制器组件HandlerMapping的调用过程 中,我们已经了解HandlerMapping的调用过程
本文将聚焦于HandlerMapping的初始化展开讨论

AbstractHandlerMapping中的初始化

HandlerMapping接口是负责处理其调用的逻辑,故本文就不在讨论其getHandler方法

HandlerMapping有一个直系子类AbstractHandlerMapping。他定义了HandlerMapping初始化模版

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, ... {

	//默认的Handler,这边使用的Obejct,子类实现的时候,使用HandlerMethod,HandlerExecutionChain等
	private Object defaultHandler;
	// url路径计算的辅助类、工具类
	private UrlPathHelper urlPathHelper = new UrlPathHelper();
	// Ant风格的Path匹配模式~  解决如/books/{id}场景
	private PathMatcher pathMatcher = new AntPathMatcher();

	// 保存着拦截器们~~~
	private final List<Object> interceptors = new ArrayList<>();
	private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

	// 跨域相关的配置~
	private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
	private CorsProcessor corsProcessor = new DefaultCorsProcessor();

	private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered

	@Nullable
	private String beanName;
	
	@Override
	protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}

其类图如下:
在这里插入图片描述
可以看出其继承了回调接口ApplicationContextAware。利用其setApplicationContext进行初始化。AbstractHandlerMapping的父类ApplicationObjectSupport底层对setApplicationContext方法进行了包装,底层核心方法是initApplicationContext

ServletContextAware和BeanNameAware接口与HandlerMapping的初始化无关,这里就忽略了

public abstract class ApplicationObjectSupport implements ApplicationContextAware {
	@Override
	public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
		if (context == null && !isContextRequired()) {
			// Reset internal context state.
			this.applicationContext = null;
			this.messageSourceAccessor = null;
		}
		else if (this.applicationContext == null) {
			// Initialize with passed-in context.
			if (!requiredContextClass().isInstance(context)) {
				throw new ApplicationContextException("");
			}
			this.applicationContext = context;
			this.messageSourceAccessor = new MessageSourceAccessor(context);
			initApplicationContext(context);
		}
		else {
			// Ignore reinitialization if same context passed in.
			if (this.applicationContext != context) {
				throw new ApplicationContextException("");
			}
		}
	}
   protected void initApplicationContext(ApplicationContext context) throws BeansException {
		initApplicationContext();
	}

	protected void initApplicationContext() throws BeansException {
	}
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware {

	//重写父类的initApplicationContext(ApplicationContext context)方法
	protected void initApplicationContext(ApplicationContext context) {
	    //调用父类的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);
			}
		}
	}

AbstractHandlerMapping子类可以通过重写父类的initApplicationContext进行初始化,但是注意其子类只有AbstractUrlHandlerMapping系列通过initApplicationContext进行初始化,而AbstractHandlerMethodMapping系列则是通过bean的初始化方法进行初始化的,他们没有重写initApplicationContext方法。在这里插入图片描述

AbstractHandlerMethodMapping还有一个直系子类RouterFunctionMapping,这是WebFlux中的,本文暂且忽略

AbstractUrlHandlerMapping系列的初始化

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {

	// 根路径 / 的处理器~
	@Nullable
	private Object rootHandler;
	// 是否使用斜线/匹配   如果为true  那么`/users`它也会匹配上`/users/`  默认是false的
	private boolean useTrailingSlashMatch = false;
	// 设置是否延迟初始化handler。仅适用于单实例handler   默认是false表示立即实例化
	private boolean lazyInitHandlers = false;

	// 这个Map就是缓存下,URL对应的bean对象(注意这里不是chain),在容器初始化时进行设置
	private final Map<String, Object> handlerMap = new LinkedHashMap<>();
	...
	
	// =========该抽象类提供的这个方法就特别重要了:向handlerMap里面put值的唯一入口~~~  可以批量urls
	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;

		// 如果是beanName,并且它是立马加载的~~~~
		if (!this.lazyInitHandlers && handler instanceof String) {
			String handlerName = (String) handler;
			ApplicationContext applicationContext = obtainApplicationContext();
			// 并且还需要是单例的,那就立马实例化吧~~~~
			if (applicationContext.isSingleton(handlerName)) {
				resolvedHandler = applicationContext.getBean(handlerName);
			}
		}

		// 先尝试从Map中去获取
		Object mappedHandler = this.handlerMap.get(urlPath);
		if (mappedHandler != null) {
			// 这个异常错误信息,相信我们在开发中经常碰到吧:简单就是说就是一个URL只能映射到一个Handler上(但是一个Handler是可以处理多个URL的,这个需要注意)
			// 这个校验必不可少啊~~~~
			if (mappedHandler != resolvedHandler) {
				throw new IllegalStateException("");
			}
		} else {
			// 如果你的handler处理的路径是根路径,那太好了  你的这个处理器就很特殊啊~~~~
			if (urlPath.equals("/")) {
				setRootHandler(resolvedHandler);
			}
			// 这个路径相当于处理所有  优先级是最低的  所以当作默认的处理器来使用~~~~
			else if (urlPath.equals("/*")) {
				setDefaultHandler(resolvedHandler);
			}
			// 正常的路径了~~~
			else {
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isTraceEnabled()) {
					logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
				}
			}
		}
	}
}

该抽象类提供了一个Map,缓存着了URL和它对应的Handler(bean),这是个非常重要的缓存。它提供了registerHandler()允许子类调用,向缓存里注册url和handler的对应关系~

AbstractDetectingUrlHandlerMapping

这又是个抽象类,继承自AbstractUrlHandlerMapping。它就越来越具有功能化了:Detecting表明它是有检测URL的功能的~

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

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


   protected void detectHandlers() throws BeansException {
		ApplicationContext applicationContext = obtainApplicationContext();
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
				applicationContext.getBeanNamesForType(Object.class));

		// Take any bean name that we can determine URLs for.
		for (String beanName : beanNames) {
		     // 这是个抽象方法由子类去实现。  它的作用就是看看url和bean怎么才算是匹配呢?也就是说这个handler到底能够处理哪些URL呢?
			// 注意:此处还是类级别(Bean),相当于一个类就是一个Handler哦~~~~
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				// URL paths found: Let's consider it a handler.
				//使用父类的
				registerHandler(urls, beanName);
			}
		}
	}
protected abstract String[] determineUrlsForHandler(String beanName);

AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发看看到底是交给哪个Handler进行处理~,最后真的是得看它实现类的时候了

BeanNameUrlHandlerMapping

它是AbstractDetectingUrlHandlerMapping的唯一实现类

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		// 意思就是必须以/开头才行~~~~~~这算是一种约定吧~~~
		// 这种方式和@WebServlet方式一毛一样~~~~~
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		// 当然别名也是可以的
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}
}

BeanNameUrlHandlerMapping初始化是,会查询容器中所有以"/"开头的bean名字。把他们直接形成对应的映射关系,注册到handlerMap中

AbstractHandlerMethodMapping的初始化

AbstractHandlerMethodMapping系列的初始化不是使用ApplicationContextAware完成,而是利用InitializingBean完成

AbstractHandlerMethodMapping

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    //用于储存HandlerMethod的注册表,下面有说明
    private final MappingRegistry mappingRegistry = new MappingRegistry();
   
    //bean的初始化方法
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

	protected void initHandlerMethods() {
	   //遍历应用程序上下文中bean的名称
		for (String beanName : getCandidateBeanNames()) {
		      //如果beanName不是以scopedTarget开头
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			   //会在每个Bean里面找处理方法 HandlerMethod,然后注册进去到mappingRegistry中
				processCandidateBean(beanName);
			}
		}
		//这是输出日志方面,忽略
		handlerMethodsInitialized(getHandlerMethods());
	}
	
   protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
		    //获取对应bean的class对象
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// 一个无法解析的bean类型,可能来自一个懒惰的bean——让我们忽略它。
			if (logger.isTraceEnabled()) {
				logger.trace(" ");
			}
		}
		//如果获取的class对象不空,且是否是hanlder类,子类实现
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

    protected void detectHandlerMethods(Object handler) {
     
		Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
		   //如果当前handler是CGLIB代理对象,那么获取其父类,否则原对象返回
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			//获取当前对象的方法
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
						    //获取当前类的方法信息,子类实现,
						    //5.1版本其子类只有一个RequestMappingHandlerMapping,所以就是把处理器的匹配规则封装成RequestMappingInfo
						    //RequestMappingInfo是对@RequestMapping信息的封装对象
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException(" ");
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			//把方法注册到mappingRegistry中
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//把处理器和对应的匹配规则储存到注册表mappingRegistry中
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
	//5.1版本其子类只有一个RequestMappingHandlerMapping,T就是RequestMappingInfo
	//handler一般情况下是处理器方法从属bean的名字
	//method是处理器方法
    public void register(T mapping, Object handler, Method method) {
			if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
				throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
			}
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}
	//决定哪些方法才是HandlerMathod
    protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
    //决定哪些Bean是Handler类
    protected abstract boolean isHandler(Class<?> beanType);

该抽象类完成了所有的Handler以及handler里面所有的HandlerMethod的模版操作,但是决定哪些Bean是Handler类和哪些方法才是HandlerMathod,这些逻辑都是交给子类自己去实现,所以这层抽象可谓也是非常的灵活,并没有把Handler的实现方式定死,允许不同

这里面有个核心内容:那就是注册handlerMethod,是交给AbstractHandlerMethodMapping的一个内部类MappingRegistry去完成的,用来专门维持所有的映射关系,并提供方法去查找方法去提供当前url映射的方法。

AbstractHandlerMethodMapping.MappingRegistry:内部类注册中心

维护几个Map(键值对),用来存储映射的信息, 还有一个MappingRegistration专门保存注册信息
这个注册中心,核心是保存了多个Map映射关系,相当于缓存下来。在请求过来时需要查找的时候,可以迅速定位到处理器

class MappingRegistry {
	//对于RequestMappingHandlerMapping来说
	//保存着RequestMappingInfo和MappingRegistration的对应关系~
	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
	// 对于保存着mapping和HandlerMethod的对应关系~
	//对于RequestMappingHandlerMapping来说
	//保存着RequestMappingInfo和HandlerMethod的对应关系~
	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
	// 这里的Map不是普通的Map,而是MultiValueMap,它是个多值Map。其实它的value是一个list类型的值
	// 至于为何是多值?有这么一种情况  URL都是/api/v1/hello  但是有的是get post delete等方法   所以有可能是会匹配到多个MappingInfo的
	//对于RequestMappingHandlerMapping来说,保存着URL和RequestMappingInfo的关系~
	private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
	//对于RequestMappingHandlerMapping来说,保存着URL和HandlerMethod的关系~
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>()
	// 这两个就不用解释了
	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
	// 读写锁~~~ 读写分离  提高启动效率
	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

RequestMappingHandlerMapping:决定哪些Bean是Handler类和哪些方法才是HandlerMathod

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware {

	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	   //RequestMappingInfo是对@RequestMapping的解析
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).build().combine(info);
			}
		}
		return info;
	}
    //查询标注@RequestMapping注解的方法,把他封装成RequestMappingInfo对象
	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}
    //标注注解@Controller或者@RequestMapping的类是handler
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

RequestMappingHandlerMapping在解析@RequestMapping标注的控制器方法时,会把他解析成RequestMappingInfo其类如下:

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

	@Nullable
	private final String name;
    //储存url储存规则
	private final PatternsRequestCondition patternsCondition;
    //储存@RequestMapping的method属性
	private final RequestMethodsRequestCondition methodsCondition;
    //储存@RequestMapping的params属性
	private final ParamsRequestCondition paramsCondition;
   //储存@RequestMapping的headers属性
	private final HeadersRequestCondition headersCondition;
   //储存@RequestMapping的consumes属性
	private final ConsumesRequestCondition consumesCondition;
    //储存@RequestMapping的produces属性
	private final ProducesRequestCondition producesCondition;
    
	private final RequestConditionHolder customConditionHolder;

总结:RequestMappingHandlerMapping在初始化时,会搜集并分析每个注解@Controller或者@RequestMapping的类,从中提取对应标注
@RequestMapping注解的类,获取对应的"<请求匹配条件,控制器方法>“映射关系,并注册到映射关系表中。请求匹配条件"通过RequestMappingInfo包装,而"控制器方法"则通过HandlerMethod来包装和表示

发布了34 篇原创文章 · 获赞 0 · 访问量 1361

猜你喜欢

转载自blog.csdn.net/qq_41071876/article/details/104742772