HandlerMapping
该类会根据请求来返回对应的Controller方法实例给DispatcherServlet,因此相关的映射便是在HandlerMapping中实现的。
由于DispatcherServlet会使用HandlerMapping的能力,所以在DispatcherServlet中会去对HandlerMapping进行初始化。
HandlerMapping的初始化:
DispatcherServlet中的initHandlerMappings()方法会去对HandlerMapping进行初始化。该方法调用在该类的initStrategies方法中(initStrategies()方法是在onRefresh方法中调用的)
进入到DispatcherServlet中的initHandlerMappings()方法。
(1)将类中存放HandlerMapping的handlerMappings成员变量置为空值
(2)判断是否需要从容器里扫描已注册的HandlerMapping实现类并排序好存入handlerMappings成员变量中。(默认是true)
True:
(1)寻找IOC容器中HandlerMapping类型的Bean
这里获取到的Bean有:RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、RouterFunctionMapping
(2)获取到的Bean集合不是空的话对其进行排序后存入handlerMappings成员变量中
RequestMappingHandlerMapping:
负责处理RequestMapping标签的
该类会先在容器中给创建出来,后续才能在DispatcherServlet的initHandlerMappings()方法中注入实例进去。
在RequestMappingHandlerMapping创建的时候会去调用到父类(AbstractHandlerMethodMapping)的afterPropertiesSet()方法去调用initHandlerMethods() :去初始化映射关系
RequestMappingHandlerMapping的关系图:
该类实现了ApplicationContextAware和ServletContextAware,使之能获取到容器的能力,可以操作容器内部的bean。
该类实现类InitializingBean接口,该接口的afterPropertiesSet()方法是在bean初始化的时候执行的。所以在该类中可以对标记有RequestMapping的bean进行处理。
进入到AbstractHandlerMethodMapping类中,该类实现InitializingBean接口的afterPropertiesSet()方法。方法中调用了initHandlerMethods()方法。该方法主要就是用来初始化处理方法实例的
protected void initHandlerMethods() {
//遍历容器里所有的BeanName
for (String beanName : getCandidateBeanNames()) {//***
//忽略掉scopedTarget.打头的bean(session application request之类的作用域内的代理类)
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {//对bean进行筛选,
processCandidateBean(beanName);//如果类上有Controller注解或RequestMapping注解:提取url与Controller映射关系
}
}
handlerMethodsInitialized(getHandlerMethods());
}
首先我们进入到getCandidateBeanNames()方法,该方法会从容器中去获取所有的bean。
protected String[] getCandidateBeanNames() {
//从root容器以及子容器里,或者仅从子容器里获取所有的Bean
//在前期如果root容器里有bean被标记RequestMapping,下面的变量就会为true。
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));//只从子容器(servletContext)中查找所有Bean实例的名字
}
然后我们会对获取到的所有BeanName进行筛选(忽略过标了scopeTarget属性作用域的代理类),然后对筛选过后的BeanName用processCandidateBean()方法进行提取url与Controller映射关系。
processCandidateBean():该方法会去获取到bean的Class然后提取其url与Controller映射关系
(1)根据bean名字获取Bean的Class对象
(2)beanType !=null &&isHandler(beanType) = (Bean的Class不是空的 && Class上有@Controller或@RequestMapping注解)
条件成立的话会调用detectHandlerMethods(beanName)方法提取其(目标Bean的)url与Controller映射关系,为后续提取并建立Controller方法和请求的映射做准备。
(A)获取到对应的Bean的Class对象 (如果没有获取到对应的Class对象则不执行后面的逻辑)
(B)获取目标Class对象的真正被代理类(因为Class对象可能是CGLIB创建的代理类,我们要获取到真正的代理对象)
(C)MethodIntrospector.selectMethods()方法 :会返回一个(Map<Method, T> methods)存储容器以供下面去注册
寻找方法上有@RequestMapping注解的Method实例。然后建立起(Key:)Controller方法实例与(Value:)RequestMappingInfo的映射关系
//寻找方法上有@RequestMapping注解的Method实例
//key:方法实例 值:RequestMappingInfo实例
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
//在执行完selectMethods()方法之后就能建立起Controller方法实例与RequestMappingInfo的映射关系,
//并将相关的映射保存到methods这个map里,key:方法实例 值:RequestMappingInfo实例
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//返回的RequestMappingInfo
return getMappingForMethod(method, userType);//*** //该方法会将目标方法上的RequestMappingInfo和目标类上的RequestMappingInfo合并成一个RequestMappingInfo然后配置上前缀返回(通常不需要配置前缀)
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);
}
});
(D)for循环将获取到的Method对象依次注册到HandlerMapping中{
(1)获取到被AOP代理包装后的真实的方法实例
(2)将目标类、真实执行的方法、RequestMappingInfo实例传递 到registerHandlerMethod()方法中去注册(注册进HandlerMethod中)
}
//将获取到的Method对象依次注册到HandlerMapping中去
methods.forEach((method, mapping) -> {
//由于可能获取到的是没包装过的被代理类的方法,所以重新获取到被AOP代理包装后的方法实例
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);//获取被AOP代理包装后的方法实例
//handler:目标类 invocableMethod:真正执行的方法 mapping:RequestMappingInfo实例
registerHandlerMethod(handler, invocableMethod, mapping);//注册进HandlerMethod中 //***
});
registerHandlerMethod()方法:会去调用成员变量(mappingRegistry).register()去进行注册
我们跟进到this.mappingRegistry.register(mapping, handler, method)方法中:
(1)上锁
(2)校验要注册的方法的唯一性,即先前是否已经注册过同样的映射(有则抛异常)
(3)注册RequestMappingInfo 和 HandlerMethod(往mappingLookup(成员变量)中存放进去)
(4)注册请求路径与对应RequestMappingInfo(获取到mapping中所有的请求路径,然后for循环将url与mapping一 一对应的存入到urlLookup(成员变量)中)
(5)
(6)注册HandlerMethod与跨域信息
(7)创建及注册MappingRegistation信息