SpringMVC DispatcherServlet源码(2) 扫描Controller创建HandlerMapping流程

29 篇文章 0 订阅
6 篇文章 0 订阅

Spring MVC向容器注册一个RequestMappingInfoHandlerMapping组件,他会扫描容器中的Controller组件,创建RequestMappingInfo并注册HandlerMethod映射关系。

本文将阅读Spring MVC源码分析上述流程。

RequestMappingHandlerMapping组件

Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.

实现HandlerMapping接口

public interface HandlerMapping {

	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

getHandler方法:

  • Return a handler and any interceptors for this request. The choice may be made on request URL, session state, or any factor the implementing class chooses.
  • The returned HandlerExecutionChain contains a handler Object, rather than even a tag interface, so that handlers are not constrained in any way. For example, a HandlerAdapter could be written to allow another framework’s handler objects to be used.
  • Returns null if no match was found. This is not an error. The DispatcherServlet will query all registered HandlerMapping beans to find a match, and only decide there is an error if none can find a handler.

功能就是根据当前请求获取执行处理器,HandlerExecutionChain封装着处理器和拦截器:

public class HandlerExecutionChain {

	private final Object handler;

	private HandlerInterceptor[] interceptors;

	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;
}

后续分析分发请求流程时会介绍这个方法。

实现InitializingBean接口

public void afterPropertiesSet() {
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setUrlPathHelper(getUrlPathHelper());
	this.config.setPathMatcher(getPathMatcher());
	this.config.setSuffixPatternMatch(useSuffixPatternMatch());
	this.config.setTrailingSlashMatch(useTrailingSlashMatch());
	this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
	this.config.setContentNegotiationManager(getContentNegotiationManager());

	super.afterPropertiesSet();
}

AbstractHandlerMethodMapping类afterPropertiesSet方法实现:

public void afterPropertiesSet() {
	initHandlerMethods();
}

AbstractHandlerMethodMapping类是一个抽象类,泛型T需要子类实现去指定,比如RequestMappingHandlerMapping子类指定就是RequestMappingInfo类型,表示其内部管理和注册的是RequestMappingInfo类型对象:

public abstract class AbstractHandlerMethodMapping<T> 
    	extends AbstractHandlerMapping implements InitializingBean {

    // 这个类也支持泛型
    private final MappingRegistry mappingRegistry = new MappingRegistry();
}

扫描Controller创建RequestMappingInfo

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 */
protected void initHandlerMethods() {
    // 首先从spring容器获取所有的beanName集
	for (String beanName : getCandidateBeanNames()) {
        // 过滤掉scoped proxy的beanName
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            // 这里解析bean
			processCandidateBean(beanName);
		}
	}
}

protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
		beanType = obtainApplicationContext().getType(beanName);
	} catch (Throwable ex) {}
    // 这里判断bean被RequestMapping或Controller注解标注
    // 之后被这两个注解标注的bean才进行进一步解析
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

protected void detectHandlerMethods(Object handler) {
    // 这里的handler就是Controller的beanName
    // 所以需要从容器里面获取一下bean的类型
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
        // 获取一下原始类型,如果是cglib类,需要解析父类获取原始类型
		Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 泛型T是RequestMappingInfo类型
        // RequestMappingInfo实现了RequestCondition接口
        // 封装了一系列的RequestCondition用于匹配请求,这个后续再介绍
        // 这个method保存的就是Controller方法 -> RequestMappingInfo对象的映射关系
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
                        // 这里使用Controller方法、Controller类型创建RequestMappingInfo对象
                        // 抽象方法,需要子类实现
                        // RequestMappingHandlerMapping子类创建的是RequestMappingInfo对象
						return getMappingForMethod(method, userType);
					} catch (Throwable ex) {
						throw new IllegalStateException("xx");
					}
				});

        // 注册HandlerMethod
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

// Uses method and type-level @RequestMapping annotations to create the RequestMappingInfo.
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);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, RequestCondition<?> customCondition) {

    // 创建过程就是在创建一系列的RequestCondition
	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

RequestMappingInfo类和RequestCondition接口

封装着Controller处理方法相关的匹配条件,用于匹配进来的请求。

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

	private final String name;

	private final PatternsRequestCondition patternsCondition;

	private final RequestMethodsRequestCondition methodsCondition;

	private final ParamsRequestCondition paramsCondition;

	private final HeadersRequestCondition headersCondition;

	private final ConsumesRequestCondition consumesCondition;

	private final ProducesRequestCondition producesCondition;

	private final RequestConditionHolder customConditionHolder;
}

RequestCondition接口:

/*
 * Contract for request mapping conditions.
 * Request conditions can be combined via combine(Object), matched to a request via 
 * getMatchingCondition(HttpServletRequest), and compared to each other via compareTo(Object, 
 * HttpServletRequest) to determine which is a closer match for a given request.
 */
public interface RequestCondition<T> {

	/**
	 * Combine this condition with another such as conditions from a type-level 
	 * and method-level @RequestMapping annotation.
	 */
	T combine(T other);

	/**
	 * Check if the condition matches the request returning a potentially new instance 
	 * created for the current request. For example a condition with multiple URL patterns 
	 * may return a new instance only with those patterns that match the request.
	 * If a condition cannot be matched to a pre-flight request it should return 
	 * an instance with empty content thus not causing a failure to match.
	 */
	T getMatchingCondition(HttpServletRequest request);

	/**
	 * Compare this condition to another condition in the context of a specific request.
	 * This method assumes both instances have been obtained via 
	 * getMatchingCondition(HttpServletRequest) to ensure they have content 
	 * relevant to current request only.
	 */
	int compareTo(T other, HttpServletRequest request);
}

注册HandlerMethod

// 注册HandlerMethod
methods.forEach((method, mapping) -> {
    // 判断并返回一个可以被调用的Method
    // 1. method不是私有的、不是静态的
    // 2. method所属的Class是userType类型
    // 3. 等等
	Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    // 注册HandlerMethod,三个参数:
    // handler: Controller BeanName
    // invocableMethod: 接口处理方法
    // mapping: 上文创建的RequestMappingInfo对象
	registerHandlerMethod(handler, invocableMethod, mapping);
});

注册HandlerMethod流程:

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

mappingRegistry是MappingRegistry对象:

class MappingRegistry {

	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

    // RequestMappingInfo -> HandlerMethod
	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

    // url -> RequestMappingInfo
	private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

    // RequestMappingName -> HandlerMethod集
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 这个泛型T是RequestMappingInfo
	public void register(T mapping, Object handler, Method method) {
		this.readWriteLock.writeLock().lock();
		try {
            // 创建HandlerMethod
            // 封装接口处理方法信息,包括:
            // Controller实例、Controller类型、接口处理方法、处理方法参数列表等信息
			HandlerMethod handlerMethod = createHandlerMethod(handler, method);
			validateMethodMapping(handlerMethod, mapping);
            // 添加RequestMappingInfo -> HandlerMethod映射
			this.mappingLookup.put(mapping, handlerMethod);

            // 从RequestMappingInfo中查找没有占位符/变量的url集,添加url -> RequestMappingInfo映射
			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);
			}

            // 创建MappingRegistration并添加映射
			this.registry.put(
                mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
		} finally {
			this.readWriteLock.writeLock().unlock();
		}
	}
}

DispatcherServlet初始化

DispatcherServlet初始化入口

DispatcherServlet在启动时从spring容器加载HandlerMapping集和HandlerAdapter集,以及其他MVC组件,此处重点介绍HandlerMapping集和HandlerAdapter集:

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

// Initialize the strategy objects that this servlet uses
protected void initStrategies(ApplicationContext context) {
    // Initialize the MultipartResolver used by this class
	initMultipartResolver(context);
    // Initialize the LocaleResolver used by this class
	initLocaleResolver(context);
    // Initialize the ThemeResolver used by this class
	initThemeResolver(context);
    // 加载HandlerMapping集
	initHandlerMappings(context);
    // 加载HandlerAdapter集
	initHandlerAdapters(context);
    // 加载HandlerExceptionResolver异常处理器集
	initHandlerExceptionResolvers(context);
    // Initialize the RequestToViewNameTranslator used by this servlet instance
	initRequestToViewNameTranslator(context);
    // 加载ViewResolver视图解析器集
	initViewResolvers(context);
	initFlashMapManager(context);
}

加载HandlerMapping集

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 从容器查找所有的HandlerMapping
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
            // 创建HandlerMapping集
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
            // 排序,之前介绍过
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	} else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		} catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

    // 略
	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	if (this.handlerMappings == null) {
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
	}
}

加载HandlerAdapter集

private void initHandlerAdapters(ApplicationContext context) {
	this.handlerAdapters = null;

	if (this.detectAllHandlerAdapters) {
		// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerAdapter> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerAdapters = new ArrayList<>(matchingBeans.values());
			// We keep HandlerAdapters in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerAdapters);
		}
	}
	else {
		try {
			HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
			this.handlerAdapters = Collections.singletonList(ha);
		} catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerAdapter later.
		}
	}

    // 略
	// Ensure we have at least some HandlerAdapters, by registering
	// default HandlerAdapters if no other adapters are found.
	if (this.handlerAdapters == null) {
		this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
	}
}

加载HandlerExceptionResolver异常处理器集

HandlerMapping集

在这里插入图片描述

  1. springfox.documentation.spring.web.PropertySourcedRequestMappingHandlerMapping

  2. org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping

    A custom HandlerMapping that makes web endpoints available over HTTP using Spring MVC.

  3. org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping

    HandlerMapping that exposes @ControllerEndpoint and @RestControllerEndpoint annotated endpoints over Spring WebFlux.

  4. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

    Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.

  5. org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping

    Implementation of the org.springframework.web.servlet.HandlerMapping interface that maps from URLs to beans with names that start with a slash (“/”), similar to how Struts maps URLs to action names.

  6. org.springframework.web.servlet.function.support.RouterFunctionMapping

    HandlerMapping implementation that supports RouterFunctions. If no RouterFunction is provided at construction time, this mapping will detect all router functions in the application context, and consult them in order.

  7. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping

    Implementation of the org.springframework.web.servlet.HandlerMapping interface that maps from URLs to request handler beans. Supports both mapping to bean instances and mapping to bean names; the latter is required for non-singleton handlers.

  8. org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping

    An AbstractUrlHandlerMapping for an application’s welcome page. Supports both static and templated files. If both a static and templated index page are available, the static page is preferred.

HandlerAdapter集

在这里插入图片描述

  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

    Extension of AbstractHandlerMethodAdapter that supports @RequestMapping annotated HandlerMethods

  2. org.springframework.web.servlet.function.support.HandlerFunctionAdapter

    HandlerAdapter implementation that supports HandlerFunctions.

  3. org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter

    Adapter to use the plain HttpRequestHandler interface with the generic org.springframework.web.servlet.DispatcherServlet. Supports handlers that implement the LastModified interface. This is an SPI class, not used directly by application code.

  4. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

    Adapter to use the plain Controller workflow interface with the generic org.springframework.web.servlet.DispatcherServlet. Supports handlers that implement the LastModified interface. This is an SPI class, not used directly by application code.

HandlerExceptionResolver异常处理器集

在这里插入图片描述

Spring MVC注入RequestMappingHandlerMapping组件

@EnableWebMvc注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 导入了DelegatingWebMvcConfiguration配置类
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

DelegatingWebMvcConfiguration配置类

继承WebMvcConfigurationSupport类。

WebMvcConfigurationSupport会向容器里面注入RequestMappingHandlerMapping和RequestMappingHandlerAdapter组件:

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
	mapping.setOrder(0);
	mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
	mapping.setContentNegotiationManager(contentNegotiationManager);
	mapping.setCorsConfigurations(getCorsConfigurations());

	PathMatchConfigurer configurer = getPathMatchConfigurer();

	Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
	if (useSuffixPatternMatch != null) {
		mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
	}
	Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
	if (useRegisteredSuffixPatternMatch != null) {
		mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
	}
	Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
	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);
	}
	Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
	if (pathPrefixes != null) {
		mapping.setPathPrefixes(pathPrefixes);
	}

	return mapping;
}

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcValidator") Validator validator) {

	RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
	adapter.setContentNegotiationManager(contentNegotiationManager);
	adapter.setMessageConverters(getMessageConverters());
	adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
	adapter.setCustomArgumentResolvers(getArgumentResolvers());
	adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

	if (jackson2Present) {
		adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
		adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
	}

	AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
	configureAsyncSupport(configurer);
	if (configurer.getTaskExecutor() != null) {
		adapter.setTaskExecutor(configurer.getTaskExecutor());
	}
	if (configurer.getTimeout() != null) {
		adapter.setAsyncRequestTimeout(configurer.getTimeout());
	}
	adapter.setCallableInterceptors(configurer.getCallableInterceptors());
	adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

	return adapter;
}

Spring Boot注入RequestMappingHandlerMapping组件

WebMvcAutoConfiguration自动装配类

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration内部类继承WebMvcConfigurationSupport类,自动注入RequestMappingHandlerMapping和RequestMappingHandlerAdapter组件。

小结

经过本文的源码分析,我们了解了Spring扫描Controller注册HandlerMethod的过程、以及Spring MVC和Spring Boot环境下注入RequestMappingHandlerMapping组件的过程:

  1. Spring MVC、Spring Boot自动注入RequestMappingHandlerMapping组件
  2. RequestMappingHandlerMapping组件扫描Controller创建RequestMappingInfo对象
  3. 封装注册HandlerMethod

后续我们将继续阅读DispatcherServlet源码,分析请求分发的流程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值