在开发中我们只需要在控制器中编写好handler方法,并配置好映射,Spring就会把请求自动导向我们写好的方法。Spring在收到请求时肯定是需要根据请求的URL来匹配handler,这是肯定需要知道有哪些handler。本文来分析Spring是怎么知道有哪些handler的,以及是怎么注册的?
一般我们会在控制器类中定义处理器方法,比如:
@Controller
public class UserController {
@RequestMapping(value="/login")
@ResponseBody
public LoginResult login(User user) {
// ...
}
}
控制器类被@Controller注解修饰,处理器方法被@RequestMapping修饰。处理器方法的扫描工作是在RequestMappingHandlerMapping类中的afterPropertiesSet方法触发的,这是Spring的bean生命周期中的方法,所以先来介绍一下RequestMappingHandlerMappingbean的创建。
RequestMappingHandlerMappingbean的创建
Spring Boot提供了一个WebMvcAutoConfigurationAdapter,在该类中存在一个叫作EnableWebMvcConfiguration的内部类,
/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
// 默认是null
if (this.mvcRegistrations != null) {
RequestMappingHandlerMapping mapping = this.mvcRegistrations.getRequestMappingHandlerMapping();
if (mapping != null) {
return mapping;
}
}
return super.createRequestMappingHandlerMapping();
}
}
注意该类继承了DelegatingWebMvcConfiguration,而后者又继承了WebMvcConfigurationSupport类。在WebMvcConfigurationSupport中会创建并注册RequestMappingHandlerMapping类型的bean。
/**
* Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
* requests to annotated controllers.
*/
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// 创建RequestMappingHandlerMapping
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 设置拦截器
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
// 设置内容协商管理器
mapping.setContentNegotiationManager(contentNegotiationManager);
// 设置CORS配置
mapping.setCorsConfigurations(getCorsConfigurations());
// 获取路径匹配配置器
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.getPatternParser() != null) {
// 设置模式解析器
mapping.setPatternParser(pathConfig.getPatternParser());
}
else {
mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
// 设置路径匹配器
mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
// 是否使用后缀模式匹配
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
// 是否使用带“/”结尾的匹配
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
// 设置路径前缀
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
return mapping;
}
/**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerMapping}.
* @since 4.0
*/
// 子类可以重写该方法
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
注意,EnableWebMvcConfiguration类中重写了createRequestMappingHandlerMapping方法。不过这都不重要,只要知道这里对RequestMappingHandlerMapping类型的bean进行了注册就可以了,后续Spring的bean工厂会创建该bean。
在WebMvcConfigurationSupport类中,还注册了RequestMappingHandlerAdapter、ContentNegotiationManager等类型的bean。
扫描RequestMappingInfo
由于在分析代码时没有看清楚包名,导致下面的代码都是reactive包下的。不过,和servlet的实现都差不多,逻辑上都是一样的,代码细节上有些许差别。
RequestMappingHandlerMapping重写了afterPropertiesSet方法,在该方法中会触发RequestMapping处理器的扫描。
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setPatternParser(getPathPatternParser());
this.config.setContentTypeResolver(getContentTypeResolver());
// 调用父类的方法,会触发RequestMapping处理器的扫描
super.afterPropertiesSet();
}
其实真正的扫描工作是在父类AbstractHandlerMethodMapping中完成的。
@Override
public void afterPropertiesSet() {
// 从所有的bean中筛选handler,并从中获取handlerMethod
initHandlerMethods();
// Total includes detected mappings + explicit registrations via registerMapping
// 获取处理器方法的数量并打印日志
int total = getHandlerMethods().size();
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
logger.debug(total + " mappings in " + formatMappingName());
}
}
核心实现在initHandlerMethods方法中。
protected void initHandlerMethods() {
// 获取bean工厂中的所有bean的名称
String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);
// 遍历所有的bean
for (String beanName : beanNames) {
// 筛选掉名称以“scopedTarget.”开头的
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
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);
}
}
// 判断该bean的类型是不是handler
if (beanType != null && isHandler(beanType)) {
// 扫描处理器方法
detectHandlerMethods(beanName);
}
}
}
// 默认是空操作
handlerMethodsInitialized(getHandlerMethods());
}
其中遍历所有的bean,并调用isHandler方法判断其类型属不属于是handler,这是一个模板方法,由子类来实现。
@Override
protected boolean isHandler(Class<?> beanType) {
// 只要类被这两个注解修饰就被认为是Handler
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
可以看到,这里判断了类上是否被@Controller或者@RequestMapping注解修饰,如果是,则认为是handler。如果是handler, 则会调用detectHandlerMethods方法来扫描handler方法。
protected void detectHandlerMethods(final Object handler) {
// 获取处理器类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
// 获取类中的方法
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
// getMappingForMethod方法用于获取处理器方法的映射,这是一个模板方法,由子类实现
(MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
// 遍历方法
methods.forEach((method, mapping) -> {
// 对方法进行封装一下
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册处理器方法
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
该方法中主要完成了两件事情:
- 获取处理器方法;
- 注册处理器方法;
获取处理器方法
这个步骤借助MethodIntrospector工具类来完成。
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(targetType)) { // 如果不是JDK代理类
// 获取类本身或CGLIB代理类的父类
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
/*
* 获取所有实现的接口。
* 为什么这里没有添加targetType本身?
* 注意上面if代码块中,获取用户类就可以获取targetType。
*/
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
// 遍历所有类型
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// 检查方法是否是handler方法
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 如果存在桥接方法,这里再次检查一下桥接方法是否是handler
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
// 添加方法和请求映射信息的映射关系
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
// 用于筛选掉桥接,合成方法,以及Object类中的方法
public static final MethodFilter USER_DECLARED_METHODS =
(method -> !method.isBridge() && !method.isSynthetic() && (method.getDeclaringClass() != Object.class));
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
if (mf == USER_DECLARED_METHODS && clazz == Object.class) {
// nothing to introspect
return;
}
// 获取类中声明的所有方法
Method[] methods = getDeclaredMethods(clazz, false);
// 遍历方法列表
for (Method method : methods) {
// 过滤掉不匹配的方法
if (mf != null && !mf.matches(method)) {
continue;
}
try {
// 处理该方法
mc.doWith(method);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
// 向上递归父类或父接口
// Keep backing up the inheritance hierarchy.
if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}
else if (clazz.isInterface()) {
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}
}
这个方法中主要对当前类及其父类和所有父接口中的方法进行扫描、筛选处理。扫描和基本筛选是在doWithMethods方法中进行的,而判断方法是不是handler方法则是通过MetadataLookup来实现的,如果返回的不是null则认为是handler方法。 MetadataLookup是一个函数式接口,AbstractHandlerMethodMapping传递过来的lambda函数中调用的是getMappingForMethod方法,这是一个模板方法,所以接下来参考一下RequestMappingHandlerMapping中的实现。
@Override
@Nullable
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);
}
for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
if (entry.getValue().test(handlerType)) {
String prefix = entry.getKey();
if (this.embeddedValueResolver != null) {
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
}
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
break;
}
}
}
return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取方法上的@RequesMapping注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 根据注解信息,创建RequestMappingInfo对象
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 将@RequestMapping注解中的属性封装为RequestMappingInfo
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();
}
其实可以发现,这里就是在获取方法上的@RequestMapping注解信息,如果获取到了,就认为是handler方法。
注册处理器方法
private final MappingRegistry mappingRegistry = new MappingRegistry();
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
MappingRegistry是AbstractHandlerMethodMapping中的内部类,负责mapping的注册。
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
// 创建处理器方法对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 验证方法映射
validateMethodMapping(handlerMethod, mapping);
// 获取路径
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
// 添加路径到mapping的映射
this.pathLookup.add(path, mapping);
}
// 初始化CORS配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
// 添加mapping到handler的映射
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
总结
本文先分析了RequestMappingHandlerMapping这个bean是怎么创建的,然后分析了是怎么扫描并注册RequestMappingInfo的。Spring在处理请求时,需要根据请求路径查已注册的handler,这个过程还会涉及到本文的分析的内容的。