spring基本使用(21)-springMVC5-SpringMVC九大组件之处理器映射器HandlerMapping

1、前面在将组件初始化的时候我们通过SpringMVC的组件驱动<mvc:annotation-driven />注册了一个类型是

      RequestMappingHandlerMapping.class
RequestMappingHandlerMapping处理器映射器返回的HandlerExecutionChain中的Handler类型是HandlerMethod.class 也就是我们在Controller中使用@RequestMapping注解定义的方法,那么RequestMappingHandlerMapping是在什么时候去解析@RequestMapping注解将其构建成为一个HandlerMethod实例的呢?

   在目前我们使用SpringMVC主流的方式就是注解驱动,而注解驱动只是向SpringMVC容器上下文中注册了一个处理器映射器即

   RequestMappingHandlerMapping,因此我们主要讲解RequestMappingHandlerMapping这个处理器映射器,其他的基本上已经   

   过时,我们就不在讨论。

            HandlerMapping接口类图 : 

                      

                      HandlerMapping接口方法列表:

      根据请求的实例来获取一个处理器执行链
      HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

2、接下来我们来剖析RequestMappingHandlerMapping的实现原理以及工作机制:

      2.1、在SpringMVC解析配置阶段(如解析spring-mvc.xml阶段),通过定义注解驱动<mvc:annotation-driven />会注册一个

RequestMappingHandlerMapping的BeanDefinition,然后在SpringMVC容器实例化+初始化 单例bean阶段,会实例化+初始化

RequestMappingHandlerMapping实例,来剖析一下RequestMappingHandlerMapping实例化+初始化阶段到底做了什么事情?

      2.2、RequestMappingHandlerMapping的类图如下:

               

               从类图上可以看出RequestMappingHandlerMapping实现了InitializingBean初始化接口,那么我们主要看看这个方法里面做了什么事情,方法源码如下:

	@Override
	public void afterPropertiesSet() {
                获取一个RequestMappingInfo.BuilderConfiguration实例用于描述
                                          RequestMappingHandlerMapping的配置。
		this.config = new RequestMappingInfo.BuilderConfiguration();

                设置一个Url路径的帮组者,用于获取各种请求路径
		this.config.setUrlPathHelper(getUrlPathHelper());
     
                设置一个路径的匹配器,用于判断请求路径是否跟我们的HnadlerMethod匹配。
		this.config.setPathMatcher(getPathMatcher());

                设置是否指定前缀匹配
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);

                设置是否尾部斜杠匹配
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);

                设置是否使用已经注册的前缀匹配
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);

                设置内容协调器,在注册RequestMappingHandlerMapping的BeanDefinition的时候已经指定。
		this.config.setContentNegotiationManager(getContentNegotiationManager());

                核心在这里,调用父类的初始化方法。
		super.afterPropertiesSet();
	}

                   我们来到父类AbstractHandlerMethodMapping中查看其初始化方法:

	@Override
	public void afterPropertiesSet() {
        初始化HnadlerMethods初始化所有的处理器方法,我们知道对于RequestMappingHandlerMapping来说返回的HandlerExecutionChain实例里面包含了各种匹配的拦截器+真正的处理器,而这个真正的处理器就是HandlerMethod,就是在此处初始化的。
		initHandlerMethods();
	}


        初始化所有的处理器方法列表:
	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		从SpringMVC应用上下文中获取到所有的组件的beanName列表
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

                循环来发现真正的处理器列表HandlerMethods
		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = getApplicationContext().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);
					}
				}
				如果beanType不为空,且是一个处理器
				if (beanType != null && isHandler(beanType)) {
				    发现当前bean的处理器列表,此处就是查找Controller中的@RequestMapping标注的方法。
					detectHandlerMethods(beanName);
				}
			}
		}
		初始化处理器列表HandlerMethods完成后做一下通知业务。
		handlerMethodsInitialized(getHandlerMethods());
	}


        判断容器中的bean是否是处理器的方法在RequestMappingHandlerMapping中如下:
	@Override
	protected boolean isHandler(Class<?> beanType) {
	    就是查看当前的beanClass是否有注解@Contrller 或者@RequestMapping 进行标注。
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

                   在父类AbstractHandlerMethodMapping中有查找处理器方法的逻辑如下:

	protected void detectHandlerMethods(final Object handler) {
	        1、入参handler 就是SpringMVCh中的beanName,因此先通过BenaName获取到beanClass
		Class<?> handlerType = (handler instanceof String ?
				getApplicationContext().getType((String) handler) : handler.getClass());
		final Class<?> userType = ClassUtils.getUserClass(handlerType);

                2、通过beanClass userType来获取类中的方法跟请求映射RequestMappingInfo的映射关系Map, 这个map在RequestMappingHandlerMapping种是这样的值:
		            key=Controller中的Method实例
		            value=RequestMappingInfo实例,这个实例里面描述的是url 即我们定义的
                            @RequestMapping定义的url、参数描述、请求方式描述、请求头描述、Consumes描   
                            述、Produces描述等信息。
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				new MethodIntrospector.MetadataLookup<T>() {
					@Override
					public T inspect(Method method) {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					}
				});

		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}

		3、获取Controller类中的方法跟请求映射RequestMappingInfo的映射关系Map后循环来进行注册
		for (Map.Entry<Method, T> entry : methods.entrySet()) {
		        获取到key也就是Controller中的Method实例。
			Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
			
			获取到value也就是与之对应的RequestMappingInfo实例。
			T mapping = entry.getValue();
			
			注册处理器方法到映射注册中心入参是:
			                   handler=Controller类在SpringMVC上下文中的beanName。
				           invocableMethod=就是Controller中的Method实例。
				           mapping=对应的RequestMappingInfo实例。
			registerHandlerMethod(handler, invocableMethod, mapping);
		}
	}

                   RequestMappingHandlerMapping中通过方法获取映射信息的实现如下:

	RequestMappingHandlerMapping中通过方法获取映射信息的实现如下:
	@Override
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	    入参是Controller中的Method实例,handlerType是Contriller的类信息Class
		
		根据方法创建一个请求映射信息RequestMappingInfo实例
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
		}
		return info;
	}

                   RequestMappingHandlerMapping中通过方法实例Method来创建RequestMappingInfo实例实现如下:

	RequestMappingHandlerMapping中通过方法实例Method来创建RequestMappingInfo实例实现如下:
	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	        1、先获取方法上的@RequestMapping注解
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		
	        2、解析一下用户自定义的请求条件
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		3、根据@RequestMapping跟请求条件创建一个RequestMappingInfo实例返回
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}


	根据@RequestMapping注解实例+自定义请求条件来创建一个RequestMappingInfo实例实现如下:
	protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, RequestCondition<?> customCondition) {

                 使用@RequestMapping注解的成员属性来构建。
		return RequestMappingInfo
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
				.methods(requestMapping.method())
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name())
				.customCondition(customCondition)
				.options(this.config)
				.build();
	}

                   获取Controller类中的方法跟请求映射RequestMappingInfo的映射关系Map后循环来进行注册,在AbstractHandlerMethodMapping注册处理器方法的实现如下:

	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	        使用父类AbstractHandlerMethodMapping的mappingRegistry来进行注册
		this.mappingRegistry.register(mapping, handler, method);
	}

                   MappingRegistry映射注册器中的实现,在AbstractHandlerMethodMapping中内部类MappingRegistry中:


	public void register(T mapping, Object handler, Method method) {
	          1、入参是:
			      handler=Controller类在SpringMVC上下文中的beanName。
				  method=就是Controller中的Method实例。
				  mapping=对应的RequestMappingInfo实例。
		this.readWriteLock.writeLock().lock();
		try {
		    2、使用Controller类的beanName + 方法Controller中的Method实例来创建一个
                     HandlerMethod实例,创建的方式就是new一个。在这一步中会解析方法的入参类型!!!!

			HandlerMethod handlerMethod = createHandlerMethod(handler, method);

			3、handlerMethod 判重
			assertUniqueMethodMapping(handlerMethod, mapping);

			if (logger.isInfoEnabled()) {
				logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
			}

			4、添加到MappingRegistry实例的mappingLookupMap中key=RequestMappingInfo实例
			                                              value=创建好的handlerMethod实例
			this.mappingLookup.put(mapping, handlerMethod);

                        5、通过RequestMappingInfo实例来获取url集合,也就是说我们的一个 
                           Controller中的单个方法Method是可以匹配多个请求路径的。
			List<String> directUrls = getDirectUrls(mapping);
			for (String url : directUrls) {
			    6、将匹配的路径添加到添加到MappingRegistry实例的urlLookup中:
                                                key = url
	                      value=RequestMappingInfo实例
				this.urlLookup.add(url, mapping);
			}

            
			String name = null;
			if (getNamingStrategy() != null) {
				name = getNamingStrategy().getName(handlerMethod, mapping);
				7、添加映射的名称,名称默认是类名+方法名 如TestController的register方法 那么
                                   映射的名称就是TC#register
				addMappingName(name, handlerMethod);
			}

                        8、初始化跨域的配置,对于RequestMappingHandlerMapping来说就是解析 类
                           或者方法上的的@CrossOrigin注解来生成一个CorsConfiguration实例。
			CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
			if (corsConfig != null) {
			        9、添加处理器方法的跨域信息
				this.corsLookup.put(handlerMethod, corsConfig);
			}

                        10、最后使用RequestMappingInfo实例 + handlerMethod实例 + 确认的Url
                            列表 + 映射的名称来构建一个映射的登记实例MappingRegistration,
                            然后将其添加到MappingRegistry映射注册中心的registry的Map中:
                                              key = RequestMappingInfo实例
			                      value=映射的登记实例MappingRegistration。
			this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
		}finally {
			this.readWriteLock.writeLock().unlock();
		}
	}

       2.3、总结:RequestMappingHandlerMapping在初始化的时候的整体步骤
                            1、会解析@Controller、@RequestMapping注解,使用Controller类中的方法实例Method,以及                                                         @RequestMapping注解的属性来生产一个RequestMappingInfo实例。

                            2、通过Controller类在SpringMVC上下文中的BeanName+Controller类中的Method实例来创建一个                                                     HandlerMothed,创建的时候会对方法参数类型进行解析。

                            3、注册RequestMappingInfo实例与HandlerMothed实例的映射关系到MappingRegistry的mappingLookup的                                       Map中。

                            4、通过RequestMappingInfo实例来确认当前映射的路径列表。

                            5、注册当前的映射的路径与RequestMappingInfo实例的映射关系到MappingRegistry的urlLookup的Map中。

                            6、解析当前handlerMethod实例的跨域信息,并注册到handlerMethod实例与跨域配置实例corsConfig的映射                                     关系到MappingRegistry的corsLookup的Map中。

                             7、使用RequestMappingInfo实例 + handlerMethod实例 + 映射的路径列表directUrls + 映射名称name来

                                   构建一个映射登记实例,然后注册RequestMappingInfo实例 与 映射登记实例MappingRegistration的映射                                      关系到MappingRegistry的registry的Map中。

                              8、完成HnadlerMethods的初始化。

  3、RequestMappingHandlerMapping初始化完成后映射的注册信息如下:

                       

                             

                        

 

4、HandlerMapping在DispatcherServlet中的工作流程:

         4.1、我们把HandlerMapping的主要的实现类RequestMappingHandlerMapping的初始化流程做了详细的剖析,接下来我们看看初始化好的RequestMappingHandlerMapping是怎么工作的。

          4.2、在DispatcherServlet的执行方法中存在如下代码片段:

	mappedHandler = getHandler(processedRequest);

                   我们来剖析getHandler方法,源码如下:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                我们配置的RequestMappingHandlerMapping会添加到DispatcherServlet的handlerMappings列表中。
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
                        在此处触发RequestMappingHandlerMapping的getHandler(request)方法。
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

                  我们来到RequestMappingHandlerMapping的getHandler(request)方法中:

                    调用链如下:父类AbstractHandlerMapping-->父类AbstractHandlerMethodMapping

                    我们先看入口AbstractHandlerMapping的getHandler(HttpServletRequest request)方法:

        @Override
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                内部获取handler 根据请求request
		Object handler = getHandlerInternal(request);
		if (handler == null) {
                        如果没有获取到就获取默认的handler
			handler = getDefaultHandler();
		}
		if (handler == null) {
                        既没有内部注册的也没有默认的handler,就返回空
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
                        如果handler 是String类型的就是使用SpringMVC容器进行实例化+初始化。
			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;
	}

                    重点在获取内部的处理器handler,来到AbstractHandlerMethodMapping的getHandlerInternal(HttpServletRequest request)方法:

        @Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
                通过初始化阶段指定的Url获取帮助者通过请求获取到请求路径,这个路径就是Controller的
                类上的路径+方法上的路径,会自动创建“/”进行拼接。
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}

                申请RequestMappingHandlerMapping的mappingRegistry的读锁。
		this.mappingRegistry.acquireReadLock();
		try {
                        使用请求路径+请求实例寻找匹配的唯一的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 + "]");
				}
			}

                         如果handlerMethod实例找到了,就把handlerMethod实例的bean属性通过 
                         beanName从容器中进行实例化+初始化,也就是实例化+初始化Controller类。
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

                    我们重点关注的根据请求路径+请求实例寻找HandlerMethod实例实现如下:

        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
                创建一个类型是Match的空集合,Match包含了RequestMappingInfo + HandlerMethod
		List<Match> matches = new ArrayList<Match>();

                使用请求路径从mappingRegistry实例的urlLookup的Map中获取到匹配的RequestMappingInfo列表。
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);

                如果获取到了,就将其构建成每一个Match实例,添加到创建的空的matches集合中,这一步   
                会根据使用路径从urlLookup中找到的RequestMappingInfo实例作为key去 
                mappingLookup的Map中找到对应的HandlerMethod实例。
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}

		if (matches.isEmpty()) {
                        如果到此步骤matches还是空的,那就从mappingRegistry实例的mappingLookup的Map中把所有的mapping都添加到空的matchs的集合中。
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

                如果matchs不为空
		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
						lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0);
                        如果matches>1 说明一个路径对应了多个HandlerMethod,一般都是一个,多个可以不用关心。
			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 + "}");
				}
			}
 
                        处理匹配关系,就是向请求request中添加一个参数
			handleMatch(bestMatch.mapping, lookupPath, request);

                        返回handlerMethod实例。
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}


	protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
		request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
	}

                   既然根据路径+请求实例匹配到了HandlerMethod实例,那就来构建一个执行链实例HandlerExecutionChain:

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {

                如果handler已经是一个执行链了就直接强转,如果不是就使用HandlerMethod来创建一个HandlerExecutionChain实例。
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

                在此通过请求获取到请求的路径
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

                循环查看RequestMappingHandlerMapping注册的 拦截器是否需要拦截当前请求,就是查看
                拦截器配置的拦截的url是否包含当前的请求路径。
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
                                如果是规则配置类型的拦截器,就判断是否需要拦截当前的请求。
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                                        如果拦截器需要拦截当前请求,就将当前的拦截器添加到
                                        HandlerExecutionChain实例的chain属性中,在执行处理器 
                                        的阶段进行拦截器方法调用。
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
                                将全局的拦截器添加到HandlerExecutionChain实例的chain属性中,
                                在执行处理器的阶段进行拦截器方法调用。
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

                   这个时候是不是就获取到了一个完整的HandlerExecutionChain实例返回到DispatcherServlet的主执行链路中了。

                   接下来就是使用HandlerAdapter去执行这个HandlerExecutionChain实例了,详细的剖析我们在下一篇文章中进行剖                    析。

        4.3、总结:

                        1、使用请求路径path从RequestMappingHandlerMapping的MappingRegistry的urlLoogup的Map中找到                                              RequestMappingInfo实例。

                         2、使用1中找到的RequestMappingInfo实例去MappingRegistry是mappingLookup的Map中找到对应的                                              HandlerMethod实例。

                         3、使用2中找到的HandlerMethod实例来构建一个HandlerExecutionChain实例,这个过程会解析可用的拦截器                                   将设置到HandlerExecutionChain实例的chain属性中,在使用HandlerAdapter去执行这个                                                               HandlerExecutionChain实例的时候处理拦截器的业务流程。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值