背景介绍
RequestMappingHandlerMapping
是Spring MVC HandlerMapping
的一个实现,主要用于针对控制器类(带有注解@Controller
)中类级别或者方法级别的注解@RequestMapping
创建RequestMappingInfo
并管理。
RequestMappingHandlerMapping
工作原理大致是这样的 :
- 应用启动阶段
RequestMappingHandlerMapping
实现了接口InitializingBean
,它的初始化方法会设置各种工作参数,并检测容器中所有的控制器方法并将它们登记管理起来。针对每个使用注解@RequestMapping
的控制器方法所生成的请求匹配条件是一个RequestMappingInfo
对象,最终所被管理的是一个HandlerMethod
对象。
- 运行时
- 当一个请求到达时,
RequestMappingHandlerMapping
会被DispatcherServlet
用于匹配一个HandlerMethod
并最终调用它处理该请求。匹配的依据是当前请求中的路径信息和RequestMappingHandlerMapping
所管理的每个控制器方法的RequestMappingInfo
请求匹配条件。
- 当一个请求到达时,
关于
RequestMappingHandlerMapping
的源代码分析,可以参考:Spring MVC HandlerMapping : RequestMappingHandlerMapping 源代码解析
本文将着重介绍应用启动阶段搜集所有控制器方法并对其进行管理的过程。
1. RequestMappingHandlerMapping
组件引入
Spring MVC
配置过程中定义了bean
组件RequestMappingHandlerMapping requestMappingHandlerMapping
,定义代码如下所示 :
// Spring MVC 配置类 WebMvcConfigurationSupport 代码片段
/**
* Return a RequestMappingHandlerMapping ordered at 0 for mapping
* requests to annotated controllers.
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 设置所配置的 HandlerInterceptor 到该 RequestMappingHandlerMapping 组件
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
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;
}
/**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerMapping}.
* @since 4.0
*/
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* Provide access to the shared handler interceptors used to configure
* HandlerMapping instances with.
* This method cannot be overridden; use #addInterceptors instead.
*/
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
// 添加开发人员提供的 HandlerInterceptor,该方法缺省实现为空,开发人员可以提供自己的实现
// 以添加自己的 HandlerInterceptor
addInterceptors(registry);
// 添加 Spring MVC 内置 HandlerInterceptor
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
在容器启动时,该配置被应用,bean
组件RequestMappingHandlerMapping requestMappingHandlerMapping
被注册到Spring IoC
容器。
2.RequestMappingHandlerMapping
组件初始化
RequestMappingHandlerMapping
隐含地实现了接口InitializingBean
(参考 Spring MVC HandlerMapping : RequestMappingHandlerMapping 源代码解析中RequestMappingHandlerMapping
的继承关系图),所以在它被容器创建时,方法#afterPropertiesSet
会被调用。该方法实现如下 :
// RequestMappingHandlerMapping 代码片段
@Override
public void afterPropertiesSet() {
// 各种配置
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 调用基类的初始化方法,其实现逻辑在基类 AbstractHandlerMethodMapping
super.afterPropertiesSet();
}
// 基类 AbstractHandlerMethodMapping 代码片段
/**
* Detects handler methods at initialization.
* @see #initHandlerMethods
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
接下来我们看initHandlerMethods
的具体实现 :
// 基类 AbstractHandlerMethodMapping 代码片段
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
*/
protected void initHandlerMethods() {
// #getCandidateBeanNames 方法其实是获取容器中所有 bean 的名称
// 该for循环遍历容器中所有那些名称不以 scopedTarget. 开头的 bean,
// 然后检测该 bean 组件中可能存在的控制器方法
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 检测 beanName 对应的 bean 组件中可能存在的控制器方法
processCandidateBean(beanName);
}
}
// 所有控制器方法检测完成之后,调用此方法做调试级别日志输出该信息
handlerMethodsInitialized(getHandlerMethods());
}
processCandidateBean
方法实现如下 :
// 基类 AbstractHandlerMethodMapping 代码片段
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 获取该 bean 的类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
// #isHandler 方法从 bean 类型判断这是否是一个控制器类,
// AbstractHandlerMethodMapping 类中 isHandler 是一个抽象方法,需要由实现子类提供实现,
// 在 RequestMappingHandlerMapping 类中,isHandler 是通过检测该 bean 类上的是否使用
// 注解 @Controller 或者 @RequestMapping 来判断的。
detectHandlerMethods(beanName);
}
}
processCandidateBean
方法主要是检测指定bean
类是否使用了注解@Controller
或者@RequestMapping
,如果使用了,则认为这是目标类,会调用方法detectHandlerMethods
从中检测控制器方法。而方法detectHandlerMethods
实现如下:
// 基类 AbstractHandlerMethodMapping 代码片段
protected void detectHandlerMethods(Object handler) {
// handler 其实就是控制器类对应的单例bean组件,这里获取其类型到 handlerType
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 从当前组件bean类上获取所有控制器方法的信息保存在 methods
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 从方法 method 上尝试构建 映射信息
// getMappingForMethod 在 AbstractHandlerMethodMapping 中
// 是一个抽象方法,在 RequestMappingHandlerMapping 中有实现,
// 该实现尝试从方法 method 上构建 RequestMappingInfo,如果该方法
// 不是一个控制器方法,构造的 RequestMappingInfo 为 null,而
// 返回 null RequestMappingInfo 的 method 不会被 methods 记录
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
//
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册登记每个控制器方法
// 对于 RequestMappingHandlerMapping 来讲,这里 mapping 类型为 RequestMappingInfo
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
这里RequestMappingHandlerMapping
对抽象方法getMappingForMethod
的实现指的一提,因为它是从一个控制器类的方法分析控制器方法信息的RequestMappingInfo
关键。RequestMappingHandlerMapping
类中,此方法实现如下 :
// RequestMappingHandlerMapping 类代码片段
/**
* Uses method and type-level @RequestMapping annotations to create
* the RequestMappingInfo.
* 结合使用方法和类级别的注解 @RequestMapping 创建 RequestMappingInfo
* 如果综合考虑之后,该方法没有使用注解 @RequestMapping,则该方法返回 null,
* 否则该方法返回一个 RequestMappingInfo 对象
* @return the created RequestMappingInfo, or null if the method
* does not have a @RequestMapping annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 尝试获取方法级别的注解 @RequestMapping 并基于此构建 RequestMappingInfo info
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 尝试获取类级别的注解 @RequestMapping 并基于此构建 RequestMappingInfo typeInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 对类级别和方法级别的 RequestMappingInfo 进行合并,最终都合并到 info 对象
info = typeInfo.combine(info);
}
// 根据控制类上的路径前缀信息做 RequestMappingInfo 路径前缀的调整
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
// 其他辅助方法
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
@Nullable
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
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();
}
从以上代码分析可见 ,RequestMappingHandlerMapping
初始化搜集所有控制器方法的过程有如下要点 :
- 所实现接口
InitializingBean
约定的方法afterPropertiesSet
在该组件bean
创建时触发了扫描所有控制器方法的逻辑调用; AbstractHandlerMethodMapping#processCandidateBean
方法扫描所有使用了注解@Controller
或者@RequestMapping
的目标类:控制器类,并扫描其中的控制器方法;RequestMappingHandlerMapping#isHandler
用于检测一个类是否是控制器类:是否使用了注解@Controller
或者@RequestMapping
;AbstractHandlerMethodMapping#detectHandlerMethods
检测一个控制器类中的所有控制器方法,获取每个控制器方法上的RequestMappingInfo
信息并注册登记;RequestMappingHandlerMapping#getMappingForMethod
用于从控制器类的一个方法上尝试根据类级别和方法级别的@RequestMapping
信息构建相应的RequestMappingInfo
对象,供#detectHandlerMethods
调用;
经过以上这些逻辑,开发人员定义的所有控制器方法都被注册登记到RequestMappingHandlerMapping
之中了,这些信息供随后DispatcherServlet
在处理用户请求时使用。