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
关系映射。