介绍
一个@RequestMapping请求的流程如下
- 通过RequestMappingHandlerMapping返回HandlerMethod这种类型的handler
- 找到能处理HandlerMethod的RequestMappingHandlerAdapter
- RequestMappingHandlerAdapter执行业务逻辑返回ModelAndView
Spring容器启动的时候,会将@RequestMapping中的信息和要执行的方法存放在AbstractHandlerMethodMapping的内部类MappingRegistry中的5个成员变量中,其中的T为RequestMappingInfo,是对@RequestMapping注解信息的封装
class MappingRegistry {
// MappingRegistration包含
// 1. 映射信息
// 2. 映射信息中的直接路径(非模式化路径)
// 3. 映射名
// 4. 对应的处理器方法
// 分别为下面4个map的key
// 这个只是对4个key的封装,只在unregister方法中会使用
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// RequestMappingInfo -> HandlerMethod
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// 不包含通配符的url -> List<RequestMappingInfo>
// 这里为什么是一个List呢?因为一个url有可能对应多个方法,即这些方法的@RequestMapping注解path属性一样,但是其他属性不一样
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// 映射名 -> HandlerMethod
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
// HandlerMethod -> 跨域配置
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
}
其中最主要的是如下2个map,其余的本节关联不大,不再分析
urlLookup:保存了如下映射关系
key=不包含通配符的url
value=List<RequestMappingInfo>
mappingLookup:
key=RequestMappingInfo
value=HandlerMethod
为什么需要这2个map呢?先留着这个问题,我们继续往后看
RequestMappingHandlerMapping的查找过程
RequestMappingHandlerMapping的继承关系如下
HandlerMapping接口一个getHandler方法,用来根据请求的url找到对应的HandlerExecutionChain对象。
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
而HandlerExecutionChain对象就是简单的将url对应的Handler和拦截器封装了一下
public class HandlerExecutionChain {
private final Object handler;
@Nullable
private List<HandlerInterceptor> interceptorList;
}
通过debug发现,整个查找过程只是依次调用了如下3个方法
- AbstractHandlerMapping#getHandler
- AbstractHandlerMethodMapping#getHandlerInternal
- AbstractHandlerMethodMapping#lookupHandlerMethod
AbstractHandlerMapping#getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// getHandlerInternal 是模板方法,留给子类去实现
Object handler = getHandlerInternal(request);
if (handler == null) {
// 没有获取到使用默认的handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// 如果handler是string类型,则以它为名从容器中查找相应的bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 拿到handler,和我们配置的拦截器,封装成HandlerExecutionChain对象返回
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 跨域配置
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
可以看到找handler的过程是交由子类去实现的。并将对这个请求适用的拦截器,跨域配置,handler封装成HandlerExecutionChain返回给DispatcherServlet
AbstractHandlerMethodMapping#getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取请求路径,作为查找路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
// 获取读锁
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
// handlerMethod就是要执行的controller方法的封装
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
这一步做的事情也不是很多,就是根据url找到对应的HandlerMethod
AbstractHandlerMethodMapping#lookupHandlerMethod
兜兜转转终于来到根据请求获取相应的HandlerMethod对象了
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 匹配结果列表
List<Match> matches = new ArrayList<>();
// 直接匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
// 如果有匹配的,就添加进匹配列表中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// 还没有匹配,就遍历所有的处理方法去找
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// 匹配结果不为空
if (!matches.isEmpty()) {
// 匹配结果比较器,直接使用匹配信息进行比较
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 对多个匹配结果进行排序
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
// 最佳匹配
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// 第一个元素和第二个元素进行比较
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
// 结果为0,至少有2个最佳匹配
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
// 多个最佳匹配,抛出异常
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回最佳匹配对应的处理器方法
return bestMatch.handlerMethod;
}
else {
// 没有匹配结果,执行无处理器匹配方法,可执行一些特殊逻辑
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
这个方法就涉及到我们最开始提到的那2个map。
urlLookup
key=不包含通配符的url
value=List<RequestMappingInfo>
mappingLookup
key=RequestMappingInfo
value=HandlerMethod
- 先根据url从urlLookup中查找对应的RequestMappingInfo,如果找到的List<RequestMappingInfo>不为空,则判断其他匹配条件是否符合
- 如果其他条件也有符合的,则不再遍历所有的RequestMappingInfo,否则遍历所有的RequestMappingInfo,因为考虑到有通配符形式的url必须遍历所有的RequestMappingInfo才能找出来符合条件的
- 如果最终找到的RequestMappingInfo有多个,则按照特定的规则找出一个最匹配的,并返回其对应的HandlerMethod
可能有小伙伴有很多疑惑,为什么一个请求请求地址会有多个实现。我给你举一个例子,有3个相同的handler方法,@RequestMapping中的其他属性都相同,只是method不同,这样就有可能根据一个url找到3个RequestMappingInfo,这样就有选择一个最优RequestMappingInfo的过程
当然还有其他情况,比如一个url同时匹配2个通配符路径。
可以看出来查找的过程还是挺简单的,接着我们就来分析一下注册的过程
RequestMappingHandlerMapping的初始化过程
从上面的分析中我们可以得出结论,在Spring容器启动后,RequestMappingInfo和HandlerMethod的映射关系已经被保存到MappingRegistry对象中,那么它是多会保存的以及以何种形式保存的?
通过查看调用关系法相,MappingRegistry中各种映射关系的初始化在register()方法,所以我们只需要在其register()方法上加一个断点,查看其调用链路即可
- RequestMappingHandlerMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#initHandlerMethods
- AbstractHandlerMethodMapping#detectHandlerMethods
- AbstractHandlerMethodMapping#registerHandlerMethod
从RequestMappingInfoHandlerMapping可以看出AbstractHandlerMethodMapping中的泛型类为RequestMappingInfo
RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo>
接着仔细分析这些方法
RequestMappingHandlerMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
// 创建映射信息构造器配置,用于构造映射信息RequestMappingInfo
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());
// 执行处理器方法的初始化逻辑
super.afterPropertiesSet();
}
主要做了一些初始化设置
AbstractHandlerMethodMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
单纯调用initHandlerMethods方法
AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
// 获取ApplicationContext中所有BeanName
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
// 排除Scoped目标类型Bean
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
// 如果获取的BeanName类型不为空,且是一个处理器类型的Bean
// 如果beanType上有Controller注解或者RequestMapping注解则是handler
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
从容器中拿到所有的beanName,排除Scope类型的bean,根据beanName得到beanType,如果一个beanType是一个handler,则调用AbstractHandlerMethodMapping#detectHandlerMethods方法
AbstractHandlerMethodMapping#detectHandlerMethods
注册的重头戏就在这里了
protected void detectHandlerMethods(Object handler) {
// 如果传入的handler是String类型,则表示是一个BeanName,从上下文获取
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// Method -> Method上对应的RequestMappingInfo
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
methods.forEach((method, mapping) -> {
// 获取真实的可执行的方法,因为上述查找逻辑在特殊情况下查找到的方法可能存在于代理上
// 需要获取非代理方法作为可执行方法调用
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
就是获取Handler上的Method,以及根据Method上@RequestMapping注解封装的RequestMappingInfo,
最后根据Handler,Method,RequestMappingInfo将映射关系注册到map中
AbstractHandlerMethodMapping#registerHandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
AbstractHandlerMethodMapping#MappingRegistry#register
主要就是初始化本文最开始说的MappingRegistry中的5个成员变量
public void register(T mapping, Object handler, Method method) {
// 获取写锁
this.readWriteLock.writeLock().lock();
try {
// 把类和方法封装为HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 保证方法映射唯一
// 如果一个相同的url对应多个handlerMethod则会抛出异常
assertUniqueMethodMapping(handlerMethod, mapping);
// 向映射查找表中添加 映射信息->对应的处理器方法
this.mappingLookup.put(mapping, handlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
// 存储 不带统配符的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);
// 注册到nameLookup
addMappingName(name, handlerMethod);
}
// 获取HandlerMethod对应的CORS配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 注册跨域配置
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
至此整个注册过程分析完毕,总结起来就是初始化Map和从Map中取值的过程
欢迎关注
参考博客
[1]https://www.cnblogs.com/Java-Starter/p/10315964.html
[2]https://cloud.tencent.com/developer/article/1497621