HandlerMapping组件

HandlerMapping的作用是根据request找到相应的处理器Handler和Interceptors。

我们看下接口的定义

	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
复制代码

AbstractHandlerMapping

AbstractHandlerMapping是HandlerMapping的抽象实现,很显然用模版模式来设计。子类只需要通过模版方法提供一些初始值或者具体算法。

获取Handler的过程通过模版方法getHandlerInternal交给子类。

AbstractHandlerMapping中保存所用配置的Interceptor,在获取到Handler后会自己根据从request提取的lookupPath将相应的Interceptors装配上去,当然子类也可以通过getHandlerInternal设置自己的Interceptor。

创建AbstractHandlerMapping之器

AbstractHandlerMapping的创建就是在initApplicationContext方法中实现

protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);//模版方法,用于给子类提供一个增加(或修改)Interceptors入口,目前没用到
  //将SpringMVC容器及父容器中所有MapperInterceptor的Bean添加到mappedInterceptors
		detectMappedInterceptors(this.mappedInterceptors);
  //将interceptors里的对象添加到mappedInterceptor或者adaptedInterceptors
		initInterceptors();
	}
复制代码
  • interceptors:用于配置SpringMVC的拦截器,有两种设置方式:1)注册HandlerMapping时通过属性设置。2)通过extendInterceptors钩子方法设置。interceptors并不会直接使用,而是通过initInterceptors方法分配到mappedInterceptor或者adaptedInterceptors
  • mappedInterceptor: 与url进行匹配,成功后放到getHandler的返回值HandlerExecutionChain中。
  • adaptedInterceptors: 这种Interceptor不要匹配,在getHandler中会全部添加到HandlerExecutionChain。只能从interceptors中取。
  • 	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		Object handler = getHandlerInternal(request);//模版方法,子类实现
    		if (handler == null) {
    			handler = getDefaultHandler();//通过set方法注入。可在配置HandlerMapping时配置,也可在子类进行设置
    		}
    		if (handler == null) {
    			return null;
    		}
    		// 
    		if (handler instanceof String) {
    			String handlerName = (String) handler;
    			handler = getApplicationContext().getBean(handlerName);
    		}
    		return getHandlerExecutionChain(handler, request);
    	}
    复制代码
   protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
    				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    		chain.addInterceptors(getAdaptedInterceptors());//设置adapted
    
    		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    		for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
    			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
    				chain.addInterceptor(mappedInterceptor.getInterceptor());
    			}
    		}
    
    		return chain;
    	}
复制代码

这个方法简单,首先使用handler创建HandlerExectionChain类型变量,然后添加拦截器。

AbstractUrlHandlerMapping系列

AbstractUrlHandlerMapping是通过url来进行匹配的。原理是将URL和对应的Handler保存在Map中,在getHandlerInternal中用URL从map中取Handler,在这个类中实现了取handler的过程,而Map的初始化则交给子类完成。

private Object rootHandler;

	private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
//具体获取Handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			Object rawHandler = null;
			if ("/".equals(lookupPath)) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// 
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = getApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
	
		return handler;
	}

//该方法lookupHandler用lookupPath从Map中查找Handler
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// 直接Map中取
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			// 若Sring类型则从容器中取
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}
		// Pattern 匹配,比如使用带*号的模式与URL进行匹配
		List<String> matchingPatterns = new ArrayList<String>();
		for (String registeredPattern : this.handlerMap.keySet()) {
			if (getPathMatcher().match(registeredPattern, urlPath)) {
				matchingPatterns.add(registeredPattern);
			}
		}
		String bestPatternMatch = null;
		Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
		if (!matchingPatterns.isEmpty()) {
			Collections.sort(matchingPatterns, patternComparator);
			if (logger.isDebugEnabled()) {
				logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
			}
			bestPatternMatch = matchingPatterns.get(0);
		}
		if (bestPatternMatch != null) {
			handler = this.handlerMap.get(bestPatternMatch);
			// 
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

			// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
			// for all of them
			Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
			for (String matchingPattern : matchingPatterns) {
				if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
					Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
					Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
					uriTemplateVariables.putAll(decodedVars);
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
			}
			return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
		}
		// No handler found...
		return null;
	}



	protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
			String pathWithinMapping, Map<String, String> uriTemplateVariables) {

		HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
		chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
		if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
			chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
		}
		return chain;
	}
复制代码

buildPathExposingHandler方法用于给查找到的Handler注册两个内部拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor,主要是将与当前URL实际匹配的Pattern、匹配条件和URL模版参数等设置到request的属性里,方便后续直接取。

  • Map的初始化。 它通过registerHandler方法进行,这个方法负责AbstractUrlHandlerMapping的创建工作,registerHandler方法是通过子类调用的。这样不同的子类就可以注册不同的Handler将组件创建出来。

SimpleUrlHandlerMapping

该类定义一个Map 变量(自己定义一个Map两个作用,第一方便配置,第二在注册前做预处理),将所有的url和Handler的对应关系放在里面,最后注册到父类的Map中,而AbstractDetectingUrlHandlerMapping则是将容器中所有bean都拿出来,按一定规则注册到父类Map。

SimpleUrlHandlerMapping通过重写父类initApplicationContext方法调用自己registerHandlers(内部调父类registerHandlers)完成Handler注册。

  	public void initApplicationContext() throws BeansException {
  		super.initApplicationContext();//调父类方法
  		registerHandlers(this.urlMap);
  	}
  protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
  		if (urlMap.isEmpty()) {
  			logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
  		}
  		else {
  			for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
  				String url = entry.getKey();
  				Object handler = entry.getValue();
  				// Prepend with slash if not already present.
  				if (!url.startsWith("/")) {
  					url = "/" + url;
  				}
  				// Remove whitespace from handler bean name.
  				if (handler instanceof String) {
  					handler = ((String) handler).trim();
  				}
  				registerHandler(url, handler);//调父类的方法
  			}
  		}
  	}
复制代码

AbstractDetectingUrlHandlerMapping

该类也是重写initApplicationContext来注册Handler的,里面调用detectHandler方法,找到所有bean的beanName,然后用determineUrlsForHandler方法对每个beanName解析出对应的urls,注册到map(URL,beanName作为Handler),注册方法依然是调用父类的registerHandler方法。

public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}
	
	protected void detectHandlers() throws BeansException {
	//获取容器的所有bean的名字
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		// 对每个beanName解析URL,解析到注册到父类的Map中
		for (String beanName : beanNames) {
			String[] urls = determineUrlsForHandler(beanName);//beanName解析URL,模版方法,子类实现
			if (!ObjectUtils.isEmpty(urls)) {
				// 调父类
				registerHandler(urls, beanName);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				}
			}
		}
	}
复制代码

AbstractDetectingUrlHandlerMapping有三个子类:我们分析BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<String>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = getApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

}
复制代码

主要是检查beanName和alias是不是以"/"开头,如果是则将其作为url。

AbstractControllerUrlHandlerMapping是将实现了Controller接口或者@Controller的bean作为Handler,并且可以设置excludedClass和excludedPackages将不包含的bean或者不包含的包下所有的bean排出在外,determineUrlsForHandler主要负责将符合条件的Handler找出来,而具体用什么URL则使用模版buildUrlsForHandler交给子类去做。

protected String[] determineUrlsForHandler(String beanName) {
		Class<?> beanClass = getApplicationContext().getType(beanName);
		//判断是不是支持的类型
		if (isEligibleForMapping(beanName, beanClass)) {
			return buildUrlsForHandler(beanName, beanClass);//模版方法,子类实现
		}
		else {
			return null;
		}
	}

protected boolean isEligibleForMapping(String beanName, Class<?> beanClass) {
		if (beanClass == null) {
			return false;
		}//排除excludedClass里配置的类
		if (this.excludedClasses.contains(beanClass)) {
			return false;
		}
		String beanClassName = beanClass.getName();
  //排除包下的类
		for (String packageName : this.excludedPackages) {
			if (beanClassName.startsWith(packageName)) {
				return false;
			}
		}//检查是否实现Controller接口或者注释@Controller
		return isControllerType(beanClass);
	}
复制代码

它有两个子类ControllerClassNameHandlerMapping(使用className作为url)、ControllerBeanNameHandlerMapping(使用beanName)作为url。

总结一下:首先在AbstractUrlHandlerMapping中设计整体结构,完成查找Handler的具体逻辑,这用到一个保存url和Handler对应关系的Map,这个Map是给子类初始化的。但是提供了初始化Map的工具方法。registerHandler。

初始化Map分为两种方式,一种通过手工在配置文件注册,另一种是在spring的容器里面找。

AbstractHandlerMethodMapping系列

我们经常使用的@RequestMapping所注释的方法就是这种Handler,也就是Method类型的Handler。

	//handlerMethods保存着匹配条件(RequestCondition)和HandlerMethod的对应关系
//key={[/articles/{articleId}/comment],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}
//value=FollowMeController.doComment() 
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
//保存URL与匹配条件的关系
//key=/articles/{articleId}/comment
//value={[/articles/{articleId}/comment],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}
	private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
//name与HandlerMethod关系
//key=FMC#doComment
//value=FollowMeController.doComment() 
MultiValueMap<String, HandlerMethod> nameMap = new LinkedMultiValueMap<String, HandlerMethod>();
复制代码

该类实现InitializingBean,所以我们很敏感的看到

	public void afterPropertiesSet() {
		initHandlerMethods();
	}
	protected void initHandlerMethods() {
	//首选拿到所有的bean
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (!beanName.startsWith("scopedTarget.") &&
					isHandler(getApplicationContext().getType(beanName))){//然后根据isHandler找出Handler
				detectHandlerMethods(beanName);//handler保存到Map
			}
		}
    //对Handler进行初始化,模版方法
		handlerMethodsInitialized(getHandlerMethods());
	}
复制代码

首选拿到所有的bean,然后根据isHandler找出Handler,最后放到Map。

isHandler是模版方法在RequestMappingHandlerMapping实现。筛选的逻辑是检查类是否有@Controller或者@RequestMapping注释

//RequestMappingHandlerMapping
protected boolean isHandler(Class<?> beanType) {
		return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
				(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
	}



protected void detectHandlerMethods(final Object handler) {
  //获取Handler类型
		Class<?> handlerType =
				(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

		// 保存Handler与匹配条件的关系,用于给registerHandlerMethod传入匹配条件
		final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
  //如果是cglib代理的子对象类型,返回父类型,否则直接返回传入的类型
		final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取当前bean里所有符合Handler要求的Method
		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
			@Override
			public boolean matches(Method method) {
        //模版方法,实现在RequestMappingHandlerMapping
        //根据@RequestMapping找匹配条件
				T mapping = getMappingForMethod(method, userType);
				if (mapping != null) {
					mappings.put(method, mapping);
					return true;
				}
				else {
					return false;
				}
			}
		});
//将符合要求的Method注册起来,也就是保存到三个Map中
		for (Method method : methods) {
			registerHandlerMethod(handler, method, mappings.get(method));
		}
	}
复制代码

detectHandlerMethods方法分两步:首先从传入的处理器中找到符合要求的方法,然后注册到Map中。 说完了怎么找到HandlerMethod,再来看一下怎么将找到的HandlerMethod注册到Map里。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
		HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
		//检查是否已经在handlerMethods中存在,若存在而且不同抛出异常
		if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
			throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
					"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
					oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
		}
//添加到handlerMethods
		this.handlerMethods.put(mapping, newHandlerMethod);
		//添加到urlMap
		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) {
			if (!getPathMatcher().isPattern(pattern)) {
				this.urlMap.add(pattern, mapping);
			}
		}
//添加到nameMap
		if (this.namingStrategy != null) {
			String name = this.namingStrategy.getName(newHandlerMethod, mapping);
			updateNameMap(name, newHandlerMethod);
		}
	}
复制代码

//AbstractHandlerMethodMapping实战

该类中实现的getHandlerInternal方法

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  //获取url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//找handlerMethod
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
	
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {		//Match保存匹配条件和Handler
		List<Match> matches = new ArrayList<Match>();
  //根据lookupPath获取到匹配条件
		List<T> directPathMatches = this.urlMap.get(lookupPath);
		if (directPathMatches != null) {
      //将找到的匹配条件放到matches
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			//如果上面没找到匹配条件,则将所有匹配条件加入matches
			addMatchingMappings(this.handlerMethods.keySet(), matches, request);
		}
//将matches排序
		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				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(handlerMethods.keySet(), lookupPath, request);
		}
	}

//调用链路
  		DispatcherServlet#doService()
  			doDispatch()
  				getHandler()
  					AbstractHandlerMapping#getHandler()
  						AbstractHandlerMethodMapping#getHandlerInternal()
  					AbstractHandlerMapping#getHandlerExecutionChain()
复制代码

那么SpringMVC是如何用HandlerMapping的相应实现类(RequestMappingHandlerMapping)的呢?

通过注解mvc:annotation-driven/,Spring在启动时会解析标签,注册bean。这时RequestMappingHandlerMapping就有了。当我们通过getBean方式获取时,就走到了InitializingBean逻辑。剩下的就是利用RequestMappingHandlerMapping找handler注册Handler的事了。

转载于:https://juejin.im/post/5bd988716fb9a0224a5e423a

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值