前提
前面已经讲过了从Servlet
规范到FrameworkServlet
,以及从FrameworkServlet
规范到DispatcherServlet
。其中在DispatcherServlet
这个类中讲到了doDispatch
方法,其中的第一个重要步骤就是,调用getHandler
方法根据请求对象获取合适的HandlerExecutionChain
。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//判断handlerMappings是否为空,默认不会为空的
if (this.handlerMappings != null) {
//迭代handlerMappings
for (HandlerMapping mapping : this.handlerMappings) {
//调用HandlerMapping的getHandler方法获取handler,这里只获取到第一个合适的HandlerExecutionChain,所以有顺序优先性
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这里有两个关键的东西:
- 包含
HandlerMapping
对象集合的handlerMappings
- 返回的
HandlerExecutionChain
1.HandlerMapping
何时被封装到handlerMappings
handlerMappings
是在容器刷新上下文的时候会调用的,具体的可以看看这个spring什么时候以及何时初始化web应用相关上下文的(FrameworkServlet,DispatcherServlet)。
这里直接看initStrategies
方法中的initHandlerMappings
方法逻辑。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//是否获取容器中的所有的HandlerMapping
if (this.detectAllHandlerMappings) {
//获取容器中的所有的HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
//如果存在HandlerMapping,则进行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//获取上下文中的bean的名字是handlerMapping的类型为HandlerMapping的bean
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.
}
}
//如果容器中没有,则用默认的,一般情况下不会到这一波,关于默认的配置会读取DispatcherServlet.properties配置文件中的配置
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
整体的逻辑还是比较简单的,就是从容器中获取所有的HandlerMapping
类型的bean封装到一个集合中,然后在进行排序。
2. HandlerExecutionChain
对象
HandlerExecutionChain
主要是一个由Object
类型的(==默认情况下是HandlerMethod
对象==)的handler字段跟两个由HandlerInterceptor
对象组成的拦截器数组跟集合组成的
public class HandlerExecutionChain {
//默认情况下这个Object是HandlerMethod类型的
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
}
3 如何获取HandlerExecutionChain
对象的
我们直接进入到DispatcherServlet
类的getHandler
方法中。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//判断handlerMappings是否为空,默认不会为空的
if (this.handlerMappings != null) {
//迭代handlerMappings
for (HandlerMapping mapping : this.handlerMappings) {
//调用HandlerMapping的getHandler方法获取handler,这里只获取到第一个合适的HandlerExecutionChain,所以有顺序优先性
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这里需要注意的一点事在循环遍历HandlerMapping
对象集合的时候,如果找到了第一个合适的就会停下来,直接返回。所以一般自定义的实现类都会实现Ordred
接口或者贴上@Order
注解来指定对应的顺序。
3.1 进入到AbstractHandlerMapping
的getHandler
方法
HandlerMapping
的getHandler
方法由AbstractHandlerMapping
实现的。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler
Object handler = getHandlerInternal(request);
//如果没有找到合适的处理器,就用默认的正常情况下是null
if (handler == null) {
handler = getDefaultHandler();
}
//如果没有handel则直接返回
if (handler == null) {
return null;
}
// Bean name or resolved handler?
//如果对应的HandlerMethod是string类型的则需要确认是不是Bean,并寻找对应的bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//添加符合当前请求路径的拦截器到一个新的HandlerExecutionChain对象并返回
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//这个部分的逻辑不太清楚,大概跟跨域请求相关的,如果handler有跟跨域先关的配置(实现了CorsConfigurationSource接口)
if (hasCorsConfigurationSource(handler)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
这个方法的逻辑就是获取导一个请求处理器对象:
- 调用由子类来实现的
getHandlerInternal
方法,获取一个合适的Object
类型的handler(默认情况下是AbstractHandlerMethodMapping
实现的返回的是HandlerMethod
类型) - 如果没有合适的则用默认的,默认情况下是默认的也是null
- 如果是handler是null,则直接结束当前方法
- 如果hanler是
String
类型的,则可能表示的是一个bean的名称,所以前去上下文中去取 - 根据请求的路径获取handler中的合适的拦截器,然后加入到创建的
HandlerExecutionChain
对象中,并返回 - 关于跨域先关的处理逻辑,这一部分没太理解
3.2.1 获取handler
在上面的步骤中最关键的就是第一步的获取合适请求处理器。第一步的方法getHandlerInternal
是在AbstractHandlerMapping
中定义的由子类来实现的一个方法。
3.2.1.1 getHandlerInternal
方法的实现类
在spring中AbstractHandlerMapping
类的getHandlerInternal
有很多实现比如AbstractUrlHandlerMapping
,AbstractHandlerMethodMapping
等,但是哪一个才是默认的呢。这里直接说。
在Spring中有一个类DelegatingWebMvcConfiguration
,这个类会被容器管理,而这个类的父类WebMvcConfigurationSupport
中会用@Bean
注解注入多个AbstractHandlerMapping
的子类到到容器中比如,RequestMappingHandlerMapping
,BeanNameUrlHandlerMapping
,SimpleUrlHandlerMapping
等。
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping(
......
return mapping;
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
ContentNegotiationManager mvcContentNegotiationManager,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
mapping.setContentNegotiationManager(mvcContentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
......
return mapping;
}
接下来的分析我们选取的是RequestMappingHandlerMapping
这个类进行分析的,其他类的这个方法的目的都是一样的,大同小异,可以自己查看源码
3.2.1.2 寻找内部HandlerMethod
的getHandlerInternal
方法(这里举例的是RequestMappingHandlerMapping
)
在RequestMappingHandlerMapping
中实现的getHandlerInternal
方法回先调用父类AbstractHandlerMethodMapping
的实现,然后执行之际的额外逻辑。所以先看看AbstractHandlerMethodMapping
的逻辑
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取映射查找路径 比如 localhost/one/two 对应的lookupPath就是/one/two
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//将请求的路径加入到请求对象中
request.setAttribute(LOOKUP_PATH, lookupPath);
//获取读锁
this.mappingRegistry.acquireReadLock();
try {
//根据路径查找handlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//如果handlerMethod不是null,并且中的bean是容器中的Bean则需要先获取
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//释放锁
this.mappingRegistry.releaseReadLock();
}
}
主要逻辑就是先获取请求的请求路径,然后先获取一个读锁根据请求的路径去找对应的HandlerMethod
,找到之后就返回,没有找到就返回null,最后释放读锁。
3.2.2 如何根据请求的路径获取HandlerMethod
接下来的逻辑就是如何根据请求的路径来获取HandlerMethod
了。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//Match对象保存了路径跟对应的handlerMethod的包装类
List<Match> matches = new ArrayList<>();
//根据路径查找对应匹配的路径集合,mappingRegistry会在AbstractHandlerMethodMapping初始化之后设置,MappingRegistry是一个注册表对象,它维护所有到处理程序方法的映射
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
//对应的匹配的路径集合不为空,将路径跟请求跟对应的handleMethod保存到Match中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//匹配器为空说明没有合适的mapping跟当前的request匹配,就把所有的mapping跟当前请求进行匹配选择
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//如果找到了合适的就进行下一步处理
if (!matches.isEmpty()) {
//获取一个MatchComparator
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
//进行排序
matches.sort(comparator);
//选出最佳的匹配
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
//如果存在相同的路径则报错
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
//设置匹配的处理方法
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
//从mappingInfo中获取URI模板变量、矩阵变量和可产生的媒体类型
handleMatch(bestMatch.mapping, lookupPath, request);
//返回HandlerMethod
return bestMatch.handlerMethod;
}
else {
//没有就将所有的匹配路径交给子类在遍历一边,如果还是寻找不出来,就根据不同情况抛出对应的异常
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
现在对逻辑进行分析:
- 从处理方法跟路径的注册表中,根据请求的路径获取对应的映射集合
- 如果匹配的路径不为空将对应的路径跟匹配的
HandlerMethod
加入到路径跟处理器的包装类集合matches
中 - 如果
matches
为空,说明没有合适的mapping
匹配的,因此要遍历所有的mapping
进行寻找 - 如果找到了,先获取一个合适
MatchComparator
(用于后面的匹配HandlerMethod
用),然后将matches
中的第一个跟第二个进行比较获取合适的HandlerMethod
。获取到合适的之后将对应的HandlerMethod
设置到request的属性中 - 如果没有找到,就将所有的匹配路径交给子类在遍历一边,如果还是寻找不出来,就根据不同情况抛出对应的异常
3.2.2.1 注册列表mappingRegistry
是何时完成映射的注册的
在上面的方法中一个核心的对象就是MappingRegistry
这个注册列表。这个对象保存了对应的请求跟处理方法和其他配置。
AbstractHandlerMethodMapping
实现了InitializingBean
的afterPropertiesSet
方法,在初始化之后会调用这个方法。
public void afterPropertiesSet() {
//初始化映射的方法
initHandlerMethods();
}
protected void initHandlerMethods() {
//确定应用程序上下文中候选bean的名称
for (String beanName : getCandidateBeanNames()) {
//如果bean的名称不是以scopedTarget开头,这个开头的bean是被代理的有作用域的bean,这种情况下需要排除
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//处理候选bean
processCandidateBean(beanName);
}
}
//初始化handlerMethod
handlerMethodsInitialized(getHandlerMethods());
}
protected String[] getCandidateBeanNames() {
//是object类型的都拿出来
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//获取bean的Class对象
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不为空,并且是符合的类型,其中的isHandler在RequestMappingHandlerMapping实现
if (beanType != null && isHandler(beanType)) {
//对bean进行注册
detectHandlerMethods(beanName);
}
}
protected boolean isHandler(Class<?> beanType) {
//bean上面有Controller注解或者RequestMapping类型注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
上面逻辑中主要就是在上下文中寻找所有的bean,然后对bean进行筛选需要满足以下条件的bean:
- bean是不是被代理出来的有作用域的bean
- bean的Class对象不能为null
- bean的Class对象有
Controller
注解或者RequestMapping
类型注解(注意这里的isHandler
具体是现在RequestMappingHandlerMapping
中)
然后就是对bean的注册处理了,处理的方法就在detectHandlerMethods
方法中
protected void detectHandlerMethods(Object handler) {
//如果对应的handler是String类型的,则从上下文中获取对应的name为handler 的bean 的类型,不是String则直接获取对应的Class对象
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
//如果类型不是null
if (handlerType != null) {
//获取原始Class,因为这个Class可能是经过CGLIB代理过后的
Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取对应的Class中的贴有RequestMapping或者派生于RequestMapping注解的方法,然后处理封装为RequestMappingInfo对象,跟对饮给的处理方法组成一个map对象
//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.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
//对方法进行迭代,然后注册
methods.forEach((method, mapping) -> {
//获取可调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//注册到mappingRegistry对象中
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
这里的逻辑就是从对应的请求处理类的Class对象中获取所有的RequestMapping
注解的方法转化成一个Method
跟RequestMappingInfo
组成的map集合,然后进行处理。具体的获取方法跟封住逻辑可以自行查看,都是对反射的利用。
上面就是对应的MappingRegistry
注册列表的生成逻辑了。
3.2.3 生成HandlerExecutionChain
对象的getHandlerExecutionChain
现在我们要回到AbstractHandlerMapping
的getHandler
方法了。上面的逻辑都是分析的这个方法中的getHandlerInternal
方法的逻辑,现在看下一个关键步骤HandlerExecutionChain
对象的生成方法getHandlerExecutionChain
。
这个其实比较简单就是对interceptor
的一个筛选过程。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//检查handler是不是HandlerExecutionChain子类,不是则需要创建
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//获取请求的路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
//循环对应的HandlerInterceptor
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//如果是MappedInterceptor,需要对路径进行匹配不是的则直接添加
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//检查路径是不是以下请求“/actuator/*”或者“/error”,不是则将对应的MappedInterceptor加入到HandlerExecutionChain中
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
4.总结
整体的逻辑有部分代码省略了,但是逻辑还是有点多的,这里做一个大概的总结:
- 在
DispatchServlet
的initHandlerMappings
方法中,会从容器中获取到所有的HandlerMapping
类型的bean,然后==按照顺序==进行排序 - 在
DispatchServlet
的getHandler
方法中,会调用对应的HandlerMapping
的getHandler
方法获取HandlerExecutionChain
对象
2.1 进入到对应的HandlerMapping
的getHandler
方法(这里spring中的HandlerMapping
的getHandler
方法都是调用的AbstractHandlerMapping
类的),回先调用子类实现的getHandlerInternal
方法获取合适的处理对象(这里举例的是RequestMappingHandlerMapping
)
2.2 获取到了对应的请求处理对象后,用将请求对象封装在一个HandlerExecutionChain
对象中,并将合适的拦截器集合也封装在一起,然后返回一个HandlerExecutionChain
网友评论