Controller查找在上图中对应的步骤1至2的过程
SpringMVC初始化过程
理解初始化过程之前,先认识两个类
RequestMappingInfo类,对RequestMapping注解封装。里面包含http请求头的相关信息。如uri、method、params、header等参数。一个对象对应一个RequestMapping注解
HandlerMethod类,是对Controller的处理请求方法的封装。里面包含了该方法所属的bean对象、该方法对应的method对象、该方法的参数等。
上图是RequestMappingHandlerMapping的继承关系。在SpringMVC初始化的时候,首先执行RequestMappingHandlerMapping中的afterPropertiesSet方法,然后会进入AbstractHandlerMethodMapping的afterPropertiesSet方法(line:93),这个方法会进入当前类的initHandlerMethods方法(line:103)。这个方法的职责便是从applicationContext中扫描beans,然后从bean中查找并注册处理器方法,代码如下。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
//获取applicationContext中所有的bean name
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//遍历beanName数组
for (String beanName : beanNames) {
//isHandler会根据bean来判断bean定义中是否带有Controller注解或RequestMapping注解
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
isHandler方法其实很简单,如下
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
就是判断当前bean定义是否带有Controlller注解或RequestMapping注解,看了这里逻辑可能会想如果只有RequestMapping会生效吗?答案是不会的,因为在这种情况下Spring初始化的时候不会把该类注册为Spring bean,遍历beanNames时不会遍历到这个类,所以这里把Controller换成Compoent注解也是可以,不过一般不会这么做。当确定bean为handlers后,便会从该bean中查找出具体的handler方法(也就是我们通常定义的Controller类下的具体定义的请求处理方法),查找代码如下
protected void detectHandlerMethods(final Object handler) {
//获取到当前Controller bean的class对象
Class<?> handlerType = (handler instanceof String) ?
getApplicationContext().getType((String) handler) : handler.getClass();
//同上,也是该Controller bean的class对象
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取当前bean的所有handler method。这里查找的依据便是根据method定义是否带有RequestMapping注解。如果有根据注解创建RequestMappingInfo对象
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
});
//遍历并注册当前bean的所有handler method
for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
//注册handler method,进入以下方法
registerHandlerMethod(handler, method, mapping);
}
}
以上代码有两个地方有调用了getMappingForMethod方法
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
//获取method的@RequestMapping注解
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
//获取method所属bean的@RequtestMapping注解
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
//合并两个@RequestMapping注解
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
这个方法的作用就是根据handler method方法创建RequestMappingInfo对象。首先判断该mehtod是否含有RequestMpping注解。如果有则直接根据该注解的内容创建RequestMappingInfo对象。创建以后判断当前method所属的bean是否也含有RequestMapping注解。如果含有该注解则会根据该类上的注解创建一个RequestMappingInfo对象。然后在合并method上的RequestMappingInfo对象,最后返回合并后的对象。现在回过去看detectHandlerMethods方法,有两处调用了getMappingForMethod方法,个人觉得这里是可以优化的,在第一处判断method时否为handler时,创建的RequestMappingInfo对象可以保存起来,直接拿来后面使用,就少了一次创建RequestMappingInfo对象的过程。然后紧接着进入registerHandlerMehtod方法,如下
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//创建HandlerMethod
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
//检查配置是否存在歧义性
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
+ "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
//获取@RequestMapping注解的value,然后添加value->RequestMappingInfo映射记录至urlMap中
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}