Spring MVC学习笔记之Spring MVC组件HandlerMapping(一)

1、HandlerMapping简介

  HandlerMapping组件是Spring MVC核心组件,用来根据请求的request查找对应的Handler。在Spring MVC中,有各式各样的Web请求,每个请求都需要一个对应的Handler来处理,具体接收到一个request请求,应该有那个Handler处理呢?这就是HandlerMapping组件的作用。

  在Spring MVC框架中,HandlerMapping组件及其实现类的如下所示:

Spring MVC学习笔记之Spring MVC组件HandlerMapping(一)_第1张图片
  在HandlerMapping类的层级结构图中,MatchableHandlerMapping接口是一个从Spring4.3.1开始新增的一个接口,用来判断给定的请求是否符合请求条件。除此之外,HandlerMapping接口有一个公共的抽象类AbstractHandlerMapping,所有子孙实现类都需要继承。该抽象类下有三个直接子类,分别是AbstractHandlerMethodMapping、AbstractUrlHandlerMapping和RouterFunctionMapping,其中RouterFunctionMapping是从Spring MVC5.2开始引入的,主要用于WebFlux处理中;而另外两个直接实现类,代表了两大类实现方式:AbstractUrlHandlerMapping表示根据url获取对应的handler;AbstractHandlerMethodMapping表示基于方法的映射方式,这也是我们在实际工作中使用较多的一种方式。

2、AbstractHandlerMapping抽象类

Spring MVC学习笔记之Spring MVC组件HandlerMapping(一)_第2张图片

  AbstractHandlerMapping是HandlerMapping的抽象类,所有子类都是继承于该抽象类。该抽象类采用了模板方法,定义了HandlerMapping的核心逻辑。在抽象类AbstractHandlerMapping中,通过继承WebApplicationObjectSupport类(间接实现了ApplicationContextAware接口),实现了拦截器相关信息的初始化,然后实现了接口中的getHandler()方法,通过调用抽象方法getHandlerInternal()获取Handler,然后把Handler封装成HandlerExecutionChain对象(该对象包括了拦截器信息和跨域访问相关信息),并进行返回。

拦截器初始化:

  首先分析,AbstractHandlerMapping抽象类如何初始化拦截器的。因为AbstractHandlerMapping抽象类间接继承了ApplicationContextAware接口,所以容器初始化是会自动调用setApplicationContext()方法,该setApplicationContext()方法经过ApplicationObjectSupport、WebApplicationObjectSupport层级,最终调用了AbstractHandlerMapping抽象类的initApplicationContext()方法,拦截器的初始化工作就是在这个方法中实现的,代码如下:

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

  其中,extendInterceptors()方法是模板方法,供子类重写,提供添加或修改拦截器的入口;detectMappedInterceptors()方法,用于将容器(包括父级容器)中所有注册的MappedInterceptor类型的Bean添加到adaptedInterceptors属性中;initInterceptors()方法用于初始化拦截器。

  1. detectMappedInterceptors()方法
    通过BeanFactoryUtils.beansOfTypeIncludingAncestors()方法查询所有的MappedInterceptor类型的实例,并添加到adaptedInterceptors属性中。
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
     
	mappedInterceptors.addAll(
			BeanFactoryUtils.beansOfTypeIncludingAncestors(
					obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
  1. initInterceptors()方法
    在初始化方法中,主要实现了把interceptors属性中的拦截器根据拦截器类型,进行适配,然后放到adaptedInterceptors变量中。在Spring4.2之前的版本,有一个变量mappedInterceptors ,所以MappedInterceptor类型的拦截器会放到该变量中,但是再Spring4.2及之后的版本,该变量被移除,所以interceptors属性中的MappedInterceptor类型(HandlerInterceptor的子类)的实例,进行适配时按照HandlerInterceptor类型进行,并统一保存到了adaptedInterceptors变量,不再进行区分。
protected void initInterceptors() {
     
	if (!this.interceptors.isEmpty()) {
     
		for (int i = 0; i < this.interceptors.size(); i++) {
     
			Object interceptor = this.interceptors.get(i);
			if (interceptor == null) {
     
				throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
			}
			this.adaptedInterceptors.add(adaptInterceptor(interceptor));
		}
	}
}


protected HandlerInterceptor adaptInterceptor(Object interceptor) {
     
	if (interceptor instanceof HandlerInterceptor) {
     
		return (HandlerInterceptor) interceptor;
	}
	else if (interceptor instanceof WebRequestInterceptor) {
     
		return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
	}
	else {
     
		throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
	}
}

getHandler()方法实现
  在HandlerMapping接口中,通过getHandler()方法获取request对应的处理器Handler和拦截器Interceptor的,在AbstractHandlerMapping抽象类的实现如下:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     
	//调用模板方法,获取request对应的处理器Handler
	Object handler = getHandlerInternal(request);
	if (handler == null) {
     //如果handler为空,获取默认的handler
		handler = getDefaultHandler();
	}
	if (handler == null) {
     
		return null;
	}
	// 如果handler是实例的名称,需要从容器中获取对应的Bean实例
	if (handler instanceof String) {
     
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}
	//根据Handler获取对应的executionChain对象,该过程会把对应的拦截器添加到executionChain对象中
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
     
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
     
		logger.debug("Mapped to " + executionChain.getHandler());
	}
	//处理跨域问题
	if (hasCorsConfigurationSource(handler)) {
     
		//初始化时如果有跨域配置,则获取config 对象
		CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
		//如果当前handler中有跨域配置,获取handlerConfig对象
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		//合并配置
		config = (config != null ? config.combine(handlerConfig) : handlerConfig);
		//把跨域处理的拦截器,添加到HandlerExecutionChain对象中
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

  在getHandler()方法中,主要做了以下几件事:

  1. 调用模板方法getHandlerInternal(),获取request对应的handler。模板方法由子类实现。
  2. 当获取的handler为空时,获取默认的handler,即获取对应的defaultHandler属性
  3. 判断handler是否时Bean的名称,如果是,获取对应的Bean实例
  4. 获取对应的HandlerExecutionChain对象,封装了初始化时的拦截器
  5. 处理跨域问题,处理跨域问题涉及到了内部类PreFlightHandler、CorsInterceptor和属性CorsProcessor对应的实现类,这里暂不深入分析。
3、AbstractUrlHandlerMapping类

  AbstractUrlHandlerMapping系列的类都继承自AbstractUrlHandlerMapping抽象类,主要用来通过URL进行匹配。思路如下:把URL与Handler的对应关系存到一个Map中,然后在getHandlerInternal方法中,根据URL去获取对应的Handler对象,在AbstractUrlHandlerMapping抽象类中,主要实现了根据url获取对应Handler的方法,如何初始化这个Map对象,交由子类进行实现。

3.1、定义的属性

//根处理器,处理“/”的处理器
@Nullable
private Object rootHandler;
//是否匹配尾部的“/”,比如:如果设置为ture,则"/users"的匹配模式,也会匹配"/users/"
private boolean useTrailingSlashMatch = false;
//设置是否延迟加载,只对单例的处理器有效
private boolean lazyInitHandlers = false;
//保存request和Handler对应关系的变量
private final Map<String, Object> handlerMap = new LinkedHashMap<>();

3.2、getHandlerInternal()方法

  实现了父类中的抽象方法,根据request获取对应的handler,实际上还有由定义的lookupHandler()方法实现。如果没有获取对应的handler,就会尝试获取根处理器或默认处理器。

@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
     
	//获取lookupPath,并保存到request属性中
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	request.setAttribute(LOOKUP_PATH, lookupPath);
	//获取lookupPath 对应的handler
	Object handler = lookupHandler(lookupPath, request);
	if (handler == null) {
     //如果没有获取到对应的handler,则进行下面处理
		// We need to care for the default handler directly, since we need to
		// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
		Object rawHandler = null;
		if ("/".equals(lookupPath)) {
     //如果时根路径,则获取根处理器,即属性rootHandler中保存的处理器
			rawHandler = getRootHandler();
		}
		if (rawHandler == null) {
     //获取默认处理器,在父类中定义,即父类中的defaultHandler属性
			rawHandler = getDefaultHandler();
		}
		if (rawHandler != null) {
     
			// Bean name or resolved handler?
			if (rawHandler instanceof String) {
     //获取对应的Bean实例
				String handlerName = (String) rawHandler;
				rawHandler = obtainApplicationContext().getBean(handlerName);
			}
			//模板方法,校验处理器,交由子类实现或扩展
			validateHandler(rawHandler, request);
			//根据原始的handler构建实际的handler,主要实现构建HandlerExecutionChain对象,并在request添加对应的参数,后续在详细分析
			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
		}
	}
	return handler;
}

  在代码上,添加了注释,不再分析处理逻辑,我们下面详细分析其中的lookupHandler()方法和buildPathExposingHandler()方法。

3.2.1、lookupHandler()方法

  因为从Map对象获取对应的Handler,不是简单的Map.get(),因为还涉及到了正则匹配等问题,所以在专门的方法中进行处理,在该方法中,主要实现了根据lookupPath获取对应的handler,然后处理当获取多个匹配handler后,如何获取最佳匹配的Handler等。 代码如下:

@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
     
	// urlPath正好对应Map的key,则直接获取,并进行处理
	Object handler = this.handlerMap.get(urlPath);
	if (handler != null) {
     
		// Bean name or resolved handler?
		if (handler instanceof String) {
     
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		return buildPathExposingHandler(handler, urlPath, urlPath, null);
	}

	// 模式匹配,获取handlerMap中对应的所有可能匹配的模式
	List<String> matchingPatterns = new ArrayList<>();
	for (String registeredPattern : this.handlerMap.keySet()) {
     
		if (getPathMatcher().match(registeredPattern, urlPath)) {
     
			matchingPatterns.add(registeredPattern);
		}
		else if (useTrailingSlashMatch()) {
     
			if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
     
				matchingPatterns.add(registeredPattern + "/");
			}
		}
	}

	String bestMatch = null;
	//定义最佳匹配的比较器,为了获取最佳的处理器Handler
	Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
	if (!matchingPatterns.isEmpty()) {
     
		//根据比较器进行排序,最佳handler排在第一个
		matchingPatterns.sort(patternComparator);
		if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
     
			logger.trace("Matching patterns " + matchingPatterns);
		}
		//获取最佳的handler
		bestMatch = matchingPatterns.get(0);
	}
	if (bestMatch != null) {
     //获取最佳处理器对应的key,然后进一步处理Handler
		//获取key对应的Handler
		handler = this.handlerMap.get(bestMatch);
		if (handler == null) {
     //如果没有获取到,则移除尾部的斜杆继续尝试
			if (bestMatch.endsWith("/")) {
     
				handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
			}
			if (handler == null) {
     //还是获取不到,直接抛出异常
				throw new IllegalStateException(
						"Could not find handler for best pattern match [" + bestMatch + "]");
			}
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
     //如果时bean的名称,则获取对应的实例
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

		//最佳匹配可能存在多个,比如:/book/{id}和/book/{name},那就再添加一个UriTemplateVariablesHandlerInterceptor拦截器,并在request设置一个key为 org.springframework.web.servlet.HandlerMapping.uriTemplateVariables的变量。
		Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
		for (String matchingPattern : matchingPatterns) {
     
			if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
     
				Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
				Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
				uriTemplateVariables.putAll(decodedVars);
			}
		}
		if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
     
			logger.trace("URI variables " + uriTemplateVariables);
		}
		return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
	}

	// No handler found...
	return null;
}

3.2.2、buildPathExposingHandler()方法

  在Map对象中映射的是真正的handler对象,在buildPathExposingHandler()方法中,实现了把rawHandler封装成HandlerExecutionChain对象,然后添加内部拦截器类PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
	String pathWithinMapping, @Nullable 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;
}

其中,PathExposingHandlerInterceptor拦截器主要设置了request中的 “xxx.bestMatchingHandler”、".introspectTypeLevelMapping"(一直为false)、.bestMatchingPattern"和".pathWithinHandlerMapping"属性,UriTemplateVariablesHandlerInterceptor拦截器,主要是当uriTemplateVariables不为空时设置,主要添加了"xxx.uriTemplateVariables"属性,值就是变量uriTemplateVariables。其中,xxx表示HandlerMapping.class.getName(),即为“org.springframework.web.servlet.HandlerMapping”。

3.3、handlerMap的初始化

  在AbstractUrlHandlerMapping抽象类中,handlerMap(request和handler映射关系)的初始化主要由registerHandler()方法来实现的。而registerHandler()方法,一般在子类中进行调用,从而实现不同的子类就可以通过注册不同的Handler将组件创建出来。

//第一个方法:该重载方法,循环调用第二种实现真正的处理器注册
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;

	// 加载处理器(当处理器是非延时加载时进行)
	if (!this.lazyInitHandlers && handler instanceof String) {
     
		String handlerName = (String) handler;
		ApplicationContext applicationContext = obtainApplicationContext();
		if (applicationContext.isSingleton(handlerName)) {
     
			resolvedHandler = applicationContext.getBean(handlerName);
		}
	}

	Object mappedHandler = this.handlerMap.get(urlPath);
	if (mappedHandler != null) {
     //如果Map中存在处理器,且和当前的不一样,就直接抛出异常。
		if (mappedHandler != resolvedHandler) {
     
			throw new IllegalStateException(
					"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
					"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
		}
	}
	else {
     //当前Map不存在处理器
		if (urlPath.equals("/")) {
     //设置根处理器
			if (logger.isTraceEnabled()) {
     
				logger.trace("Root mapping to " + getHandlerDescription(handler));
			}
			setRootHandler(resolvedHandler);
		}
		else if (urlPath.equals("/*")) {
     //设置默认处理器
			if (logger.isTraceEnabled()) {
     
				logger.trace("Default mapping to " + getHandlerDescription(handler));
			}
			setDefaultHandler(resolvedHandler);
		}
		else {
     //在Map中添加urlPath和处理器对应的关系
			this.handlerMap.put(urlPath, resolvedHandler);
			if (logger.isTraceEnabled()) {
     
				logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
			}
		}
	}
}
4、BeanNameUrlHandlerMapping类

4.1、使用方法

1、定义一个Controller

public class TestController implements Controller {
     
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)   {
     
        ModelAndView mav = new ModelAndView("test");
        mav.addObject("context", “test”);
        return mav;
    }
}

2、TestController 注入到Spring容器

@Configuration
public class ControllerBeanConfig {
     
    /**
     * 注意 :
     * 1. 该 bean 实现了接口 org.springframework.web.servlet.mvc.Controller,
     * 2. 该 bean 没有使用注解 @Controller,
     * (如果使用了注解@Controller,就会被RequestMappingHandlerMapping接管,而不是由BeanNameUrlHandlerMapping处理)
     * 3. 映射到匹配 /test/* 的url
     * @return
     */
    @Bean(name = "/test/*")
    public TestController beanTestController() {
     
        return new TestController();
    }
}

  通过上述配置,在初始化时,会采用BeanNameUrlHandlerMapping类,进行获取request对应de 处理器。在这个过程中,BeanNameUrlHandlerMapping类是如何产生作用的呢?下面我们详细的分析。

4.2、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping原理

  BeanNameUrlHandlerMapping类继承了抽象类AbstractDetectingUrlHandlerMapping,然后又继承了AbstractUrlHandlerMapping抽象类,AbstractUrlHandlerMapping抽象类在前面已经分析过了。在前面介绍抽象类AbstractUrlHandlerMapping的时候,我们知道会在spring初始化的时候,执行initApplicationContext()方法,我们阅读AbstractDetectingUrlHandlerMapping类的代码时,发现它又重写了initApplicationContext()方法,所以在spring初始化的时候,会执行该方法。代码如下:

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

  其中,super.initApplicationContext()方法调用了父类中的方法,前面已经介绍过了。同时,又调用了detectHandlers()方法,该方法主要实现了检测容器中注册的所有的handler。根据detectHandlersInAncestorContexts参数的配置,可以检查当前容器或者当前容器及其祖先容器中注册的handler。代码如下:

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) {
     
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
     
			// URL paths found: Let's consider it a handler.
			registerHandler(urls, beanName);
		}
	}

	if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
     
		logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
	}
}

  在detectHandlers()方法中,通过处理器实例,可以获取对应的url,该逻辑由determineUrlsForHandler()方法实现,该方法由子类BeanNameUrlHandlerMapping中实现。然后再把urls和对应的handler通过父类AbstractUrlHandlerMapping中的registerHandler()方法,注册到Map对象中。

  最后,determineUrlsForHandler()方法的实现,这里主要通过判断对应实例的name或aliases 来判断,只有以“/”开头的才是有效的url,代码如下:

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
     

	@Override
	protected String[] determineUrlsForHandler(String beanName) {
     
		List<String> urls = new ArrayList<>();
		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);
	}

}

  通过上面的分析,我们知道:在initApplicationContext()方法中实现初始化工作,其中AbstractDetectingUrlHandlerMapping类主要实现了处理器的检测,BeanNameUrlHandlerMapping类实现handler对应url的判断。

5、其他

  通过前面的分析,我们知道HandlerMapping有两个分支的实现,在这篇中我们已经分析了AbstractUrlHandlerMapping系列的实现,由于篇幅原因,AbstractHandlerMethodMapping系列的分析,我们在《Spring MVC组件HandlerMapping(二)》中进行。

你可能感兴趣的:(Spring,HandlerMapping,Spring,MVC,源码学习)