一个人可以走的很快,但一群人可以走的更远 ,新的一年我想遇到优秀的你
1. 回顾2. 精华总结3. 注册Handler3.1 isHandler-判3.2 detectHandlerMethods3.2.1 寻找3.2.2 登记(缓存):4. 获取handler4.1 匹配4.2 包装5. 总结
1. 回顾
上节,说了Handler的4种定义方式,以及Handler与HandlerMapping的关系。
@RequestMapping方式是我们最常用的定义handler的方式。
RequestMappingHandlerMapping 负责把@RequestMapping方式定义handler登记在册。
这一节从源码角度来看看,@RequestMapping注解如何让一个方法变成一个handler。
2. 精华总结
@RequestMapping注解背后的做的工作其实挺多,但是我们可以抓住几个关键点:
- 处理时机:Bean初始化
- 处理方式:遍历所有Bean,判断handler类,找到类中handler
- 处理结果:关系映射缓存
3. 注册Handler
@RequestMapping注解的解析逻辑,是伴随着RequestMappingHandlerMapping 初始化过程完成的
。
一切从RequestMappingHandlerMapping.afterPropertiesSet方法开始。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
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());
super.afterPropertiesSet();
}
}
主要做了RequestMappingInfo的Builder助手的配置操作,调用父类的afterPropertiesSet()方法
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
public void afterPropertiesSet() {
initHandlerMethods();
}
//初始化HandlerMethod
protected void initHandlerMethods() {
//获取所有bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//遍历判断
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
// 如果是handler,
if (beanType != null && isHandler(beanType)) {
//识别出Hadnler中的HandlerMethod
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
}
这里的逻辑还是比较清晰的。遍历BeanFactory仓库中Bean,挨个检查类是否是Handler,如果是Handler就去Handler查找HandlerMethod
也就是说:一个Bean在此会经历两个重点方法,isHandler方法与detectHandlerMethods方法
3.1 isHandler-判
判断当前Bean是否是一个handler,方法实现在RequestMappingHandlerMapping 类中。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
}
类上有@Controller注解或者@RequestMapping 此处就看做是一个Handler
//@RequestMapping("/user")
@Controller
public class UserController {
}
虽然@Controller注解的本质也是表示一个@Component
@Component
public @interface Controller {
}
但是,我不用@Component 来表示一个代表Controller类。
其原因就是@Controller 会在RequestMappingHandlerMapping 中按@Controller注解判定为handler。
所以说:@Controller 是表示这是一个跟web有关的Bean
3.2 detectHandlerMethods
判定为Bean是一个handler 之后,下面就是把类中能处理请求的方法找到,登记在册。也就是@RequestMapping注解的方法的解析过程
。
两个动作:找到,登记
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//找:方法搜索器,搜索当前bean继承体系上的所有方法中,符合我们需要的method
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method 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);
}
//注册,
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}}
3.2.1 寻找
MethodIntrospector
能够搜索到当前类 所有关联的方法(包括,接口,父类,同时还处理参数化方法以及接口和基于类代理的常见场景)中,符合我们要求的方法,返回与之关联的元数据。
MethodIntrospector.MetadataLookup
用于定义条件,匹配当前类所有关联的方法中符合我们要求的方法。
getMappingForMethod就是这个条件。getMappingForMethod是一个抽象方法,在中定义RequestMappingHandlerMapping 中
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
//获取RequestMappingInfo
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//查找method是是否有RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
//如果方法上有
if (info != null) {
//获取类上的RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//合并方法与类上的注解信息。 这也是为啥。方法的访问路径是类路径+方法路径。
info = typeInfo.combine(info);
}
}
return info;
}
// 根据方法或者类上的注解信息,创建RequestMappingInfo
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//获取当前element上的RequestMapping 注解元信息 (包括直接@RequestMapping注解,或者间接RequestMapping 如@GetMapping)
// 将查询出的多个annotationType类型注解属性合并到查询的第一个注解中
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);
}
//创建RequestMappingInfo
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
return RequestMappingInfo
//解析请求中注解对应的
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.options(this.config)
.build();
}
}
简单来说:找的过程,就是遍历
当前Bean相关的所有方法。解析
方法上的@RequestMapping注解与类上的@RequestMapping注解,合并
两者信息,创建出一个RequestMappingInfo
实例来表示当前方法的@RequestMapping注解元信息
合并这点其实就是解释了 为啥请求处理方法的Url路径= 类路径+方法路径
最终找的结果:得到一个 以method为K ,以RequestMappingInfo
为Value的Map methods。也就是解析到了方法与方法对应RequestMappingInfo
关系映射。
3.2.2 登记(缓存):
登记动作发生在registerHandlerMethod 方法中。此时就是把各种关系缓存起来。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//找:方法搜索器,搜索当前bean继承体系上的所有方法中,符合我们需要的method
//....
//注册,
for (Map.Entry<Method, T> entry : methods.entrySet()) {
//找到可调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
//注册
registerHandlerMethod(handler, invocableMethod, mapping);
}}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
class MappingRegistry {
//三个参数直接信息, handler类,hangler方法
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();//线程安全
try {
//创建一个HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//检查路径是否被定义过,确保一个路径只能找到一个method(路径定义重复的错误就是在这里抛出的)
assertUniqueMethodMapping(handlerMethod, mapping);
//日志,启动时可以看到
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
// mappingLookup: 缓存Mapping和handlerMethod的关系
this.mappingLookup.put(mapping, handlerMethod);
//获取注解里的路径信息。
List<String> directUrls = getDirectUrls(mapping);
//缓存路径与mapping的关系
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// 保存name和handlerMethod的关系 同样也是一对多
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
//初始化跨域配置,并缓存handlerMethod与跨域配置的
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 注册mapping和MappingRegistration的关系
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
}
}
总结注册过程做了啥:
- 根据handler,method创建一个HandlerMethod实例
- 缓存mapping和handlerMethod的关系(
mappingLookup
) - 缓存路径url与mapping的关系()
- 缓存name和handlerMethod的关系
- 根据mapping,handlerMethod,urls,name创建一个MappingRegistration相关实例
- 缓存mapping和MappingRegistration的关系
Bean初始化完成后,我们定义的接口方法也就以各种形态,各种关系缓存起来了。
4. 获取handler
当请求来到DispatcherServlet#doDispatch 方法时。遍历所有的handlerMapping, 并尝试从其实例中获取Handler, 如果获取到就返回。
mappedHandler = getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
对于我们以@RequestMapping("/xxxx")
注解定义的handler。RequestMappingHandlerMapping的父类AbstractHandlerMapping 提供对getHandler的实现。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
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
- 包装
4.1 匹配
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求的URI 的path
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
//根据url从多个缓存关系中选择HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//根据url从关系缓存:urlLookup获取对应的匹配条件集
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//如果没有找到匹配条件,从整个关系缓存mappingLookup中寻找
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));
//按匹配度进行排序
Collections.sort(matches, 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) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
//找到匹配后,在Attributes中加入一些相关信息。
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
//返回最优匹配的handlerMehod
return bestMatch.handlerMethod;
}
else {
//没有找到。交给子类去处理没有找到的情况
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
4.2 包装
找到handler后,并不是直接返回handler . 为了 体现框架设计的开闭原则,SpringMVC把拦截与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;
5. 总结
因为篇幅的原因,很多东西看源码理解的更快,本文提供观看思路,加入个人的理解。
框架千千万万,我们不需要把每一个细节看到非常透,抓住主干,理解原理。我们已经成功了一步。
如果觉得文章有用, 求点赞👍 求关注❤️ 求分享👥
如果本文任何错误,请批评指教,不胜感激 !
微信公众号:[
源码行动
],加我认识认识呗
我把自己写文章的定位是个人理解日记。因为每当我写文章时,我会集中注意力在知识点的理解与阐述上,这样会加深,加强对知识点的理解。
新的一年,我享读读源码,不求技术飞速提高,但求心里舒服。