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集
-
springfox.documentation.spring.web.PropertySourcedRequestMappingHandlerMapping
-
org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping
A custom HandlerMapping that makes web endpoints available over HTTP using Spring MVC.
-
org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping
HandlerMapping that exposes @ControllerEndpoint and @RestControllerEndpoint annotated endpoints over Spring WebFlux.
-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.
-
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.
-
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.
-
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.
-
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集
-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
Extension of AbstractHandlerMethodAdapter that supports @RequestMapping annotated HandlerMethods
-
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
HandlerAdapter implementation that supports HandlerFunctions.
-
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.
-
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组件的过程:
- Spring MVC、Spring Boot自动注入RequestMappingHandlerMapping组件
- RequestMappingHandlerMapping组件扫描Controller创建RequestMappingInfo对象
- 封装注册HandlerMethod
后续我们将继续阅读DispatcherServlet源码,分析请求分发的流程。