前言
这篇文章里:Spring MVC容器启动时,web九大组件初始化详解,已经大概介绍过web九大组件;
在这篇文章里:springMVC前端控制器组件HandlerMapping的调用过程 中,我们已经了解HandlerMapping的调用过程
本文将聚焦于HandlerMapping的初始化展开讨论
AbstractHandlerMapping中的初始化
HandlerMapping接口是负责处理其调用的逻辑,故本文就不在讨论其getHandler方法
HandlerMapping有一个直系子类AbstractHandlerMapping。他定义了HandlerMapping初始化模版
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, ... {
//默认的Handler,这边使用的Obejct,子类实现的时候,使用HandlerMethod,HandlerExecutionChain等
private Object defaultHandler;
// url路径计算的辅助类、工具类
private UrlPathHelper urlPathHelper = new UrlPathHelper();
// Ant风格的Path匹配模式~ 解决如/books/{id}场景
private PathMatcher pathMatcher = new AntPathMatcher();
// 保存着拦截器们~~~
private final List<Object> interceptors = new ArrayList<>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
// 跨域相关的配置~
private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
@Nullable
private String beanName;
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
其类图如下:
可以看出其继承了回调接口ApplicationContextAware。利用其setApplicationContext进行初始化。AbstractHandlerMapping的父类ApplicationObjectSupport
底层对setApplicationContext方法进行了包装,底层核心方法是initApplicationContext
ServletContextAware和BeanNameAware接口与HandlerMapping的初始化无关,这里就忽略了
public abstract class ApplicationObjectSupport implements ApplicationContextAware {
@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
// Initialize with passed-in context.
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException("");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
// Ignore reinitialization if same context passed in.
if (this.applicationContext != context) {
throw new ApplicationContextException("");
}
}
}
protected void initApplicationContext(ApplicationContext context) throws BeansException {
initApplicationContext();
}
protected void initApplicationContext() throws BeansException {
}
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware {
//重写父类的initApplicationContext(ApplicationContext context)方法
protected void initApplicationContext(ApplicationContext context) {
//调用父类的initApplicationContext(ApplicationContext context)方法
super.initApplicationContext(context);
if (this.servletContext == null && context instanceof WebApplicationContext) {
this.servletContext = ((WebApplicationContext) context).getServletContext();
if (this.servletContext != null) {
initServletContext(this.servletContext);
}
}
}
AbstractHandlerMapping子类可以通过重写父类的initApplicationContext进行初始化,但是注意其子类只有AbstractUrlHandlerMapping系列通过initApplicationContext进行初始化,而AbstractHandlerMethodMapping系列则是通过bean的初始化方法进行初始化的,他们没有重写initApplicationContext方法。
AbstractHandlerMethodMapping还有一个直系子类RouterFunctionMapping,这是WebFlux中的,本文暂且忽略
AbstractUrlHandlerMapping系列的初始化
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
// 根路径 / 的处理器~
@Nullable
private Object rootHandler;
// 是否使用斜线/匹配 如果为true 那么`/users`它也会匹配上`/users/` 默认是false的
private boolean useTrailingSlashMatch = false;
// 设置是否延迟初始化handler。仅适用于单实例handler 默认是false表示立即实例化
private boolean lazyInitHandlers = false;
// 这个Map就是缓存下,URL对应的bean对象(注意这里不是chain),在容器初始化时进行设置
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
...
// =========该抽象类提供的这个方法就特别重要了:向handlerMap里面put值的唯一入口~~~ 可以批量urls
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// 如果是beanName,并且它是立马加载的~~~~
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
// 并且还需要是单例的,那就立马实例化吧~~~~
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
// 先尝试从Map中去获取
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
// 这个异常错误信息,相信我们在开发中经常碰到吧:简单就是说就是一个URL只能映射到一个Handler上(但是一个Handler是可以处理多个URL的,这个需要注意)
// 这个校验必不可少啊~~~~
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("");
}
} else {
// 如果你的handler处理的路径是根路径,那太好了 你的这个处理器就很特殊啊~~~~
if (urlPath.equals("/")) {
setRootHandler(resolvedHandler);
}
// 这个路径相当于处理所有 优先级是最低的 所以当作默认的处理器来使用~~~~
else if (urlPath.equals("/*")) {
setDefaultHandler(resolvedHandler);
}
// 正常的路径了~~~
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
}
该抽象类提供了一个Map,缓存着了URL和它对应的Handler(bean),这是个非常重要的缓存。它提供了registerHandler()允许子类调用,向缓存里注册url和handler的对应关系~
AbstractDetectingUrlHandlerMapping
这又是个抽象类,继承自AbstractUrlHandlerMapping。它就越来越具有功能化了:Detecting表明它是有检测URL的功能的~
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
// 这是个抽象方法由子类去实现。 它的作用就是看看url和bean怎么才算是匹配呢?也就是说这个handler到底能够处理哪些URL呢?
// 注意:此处还是类级别(Bean),相当于一个类就是一个Handler哦~~~~
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
//使用父类的
registerHandler(urls, beanName);
}
}
}
protected abstract String[] determineUrlsForHandler(String beanName);
AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发看看到底是交给哪个Handler进行处理~,最后真的是得看它实现类的时候了
BeanNameUrlHandlerMapping
它是AbstractDetectingUrlHandlerMapping的唯一实现类
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
// 意思就是必须以/开头才行~~~~~~这算是一种约定吧~~~
// 这种方式和@WebServlet方式一毛一样~~~~~
if (beanName.startsWith("/")) {
urls.add(beanName);
}
// 当然别名也是可以的
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
BeanNameUrlHandlerMapping初始化是,会查询容器中所有以"/"开头的bean名字。把他们直接形成对应的映射关系,注册到handlerMap中
AbstractHandlerMethodMapping的初始化
AbstractHandlerMethodMapping系列的初始化不是使用ApplicationContextAware完成,而是利用InitializingBean完成
AbstractHandlerMethodMapping
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
//用于储存HandlerMethod的注册表,下面有说明
private final MappingRegistry mappingRegistry = new MappingRegistry();
//bean的初始化方法
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//遍历应用程序上下文中bean的名称
for (String beanName : getCandidateBeanNames()) {
//如果beanName不是以scopedTarget开头
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//会在每个Bean里面找处理方法 HandlerMethod,然后注册进去到mappingRegistry中
processCandidateBean(beanName);
}
}
//这是输出日志方面,忽略
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//获取对应bean的class对象
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// 一个无法解析的bean类型,可能来自一个懒惰的bean——让我们忽略它。
if (logger.isTraceEnabled()) {
logger.trace(" ");
}
}
//如果获取的class对象不空,且是否是hanlder类,子类实现
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
//如果当前handler是CGLIB代理对象,那么获取其父类,否则原对象返回
Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取当前对象的方法
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//获取当前类的方法信息,子类实现,
//5.1版本其子类只有一个RequestMappingHandlerMapping,所以就是把处理器的匹配规则封装成RequestMappingInfo
//RequestMappingInfo是对@RequestMapping信息的封装对象
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException(" ");
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
//把方法注册到mappingRegistry中
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//把处理器和对应的匹配规则储存到注册表mappingRegistry中
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
//5.1版本其子类只有一个RequestMappingHandlerMapping,T就是RequestMappingInfo
//handler一般情况下是处理器方法从属bean的名字
//method是处理器方法
public void register(T mapping, Object handler, Method method) {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
//决定哪些方法才是HandlerMathod
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
//决定哪些Bean是Handler类
protected abstract boolean isHandler(Class<?> beanType);
该抽象类完成了所有的Handler以及handler里面所有的HandlerMethod的模版操作,但是决定哪些Bean是Handler类和哪些方法才是HandlerMathod,这些逻辑都是交给子类自己去实现,所以这层抽象可谓也是非常的灵活,并没有把Handler的实现方式定死,允许不同
这里面有个核心内容:那就是注册handlerMethod,是交给AbstractHandlerMethodMapping的一个内部类MappingRegistry去完成的,用来专门维持所有的映射关系,并提供方法去查找方法去提供当前url映射的方法。
AbstractHandlerMethodMapping.MappingRegistry:内部类注册中心
维护几个Map(键值对),用来存储映射的信息, 还有一个MappingRegistration专门保存注册信息
这个注册中心,核心是保存了多个Map映射关系,相当于缓存下来。在请求过来时需要查找的时候,可以迅速定位到处理器
class MappingRegistry {
//对于RequestMappingHandlerMapping来说
//保存着RequestMappingInfo和MappingRegistration的对应关系~
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// 对于保存着mapping和HandlerMethod的对应关系~
//对于RequestMappingHandlerMapping来说
//保存着RequestMappingInfo和HandlerMethod的对应关系~
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// 这里的Map不是普通的Map,而是MultiValueMap,它是个多值Map。其实它的value是一个list类型的值
// 至于为何是多值?有这么一种情况 URL都是/api/v1/hello 但是有的是get post delete等方法 所以有可能是会匹配到多个MappingInfo的
//对于RequestMappingHandlerMapping来说,保存着URL和RequestMappingInfo的关系~
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
//对于RequestMappingHandlerMapping来说,保存着URL和HandlerMethod的关系~
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>()
// 这两个就不用解释了
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
// 读写锁~~~ 读写分离 提高启动效率
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
RequestMappingHandlerMapping:决定哪些Bean是Handler类和哪些方法才是HandlerMathod
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware {
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//RequestMappingInfo是对@RequestMapping的解析
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
//查询标注@RequestMapping注解的方法,把他封装成RequestMappingInfo对象
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
//标注注解@Controller或者@RequestMapping的类是handler
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
RequestMappingHandlerMapping在解析@RequestMapping标注的控制器方法时,会把他解析成RequestMappingInfo其类如下:
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
@Nullable
private final String name;
//储存url储存规则
private final PatternsRequestCondition patternsCondition;
//储存@RequestMapping的method属性
private final RequestMethodsRequestCondition methodsCondition;
//储存@RequestMapping的params属性
private final ParamsRequestCondition paramsCondition;
//储存@RequestMapping的headers属性
private final HeadersRequestCondition headersCondition;
//储存@RequestMapping的consumes属性
private final ConsumesRequestCondition consumesCondition;
//储存@RequestMapping的produces属性
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
总结:RequestMappingHandlerMapping在初始化时,会搜集并分析每个注解@Controller或者@RequestMapping的类,从中提取对应标注
@RequestMapping注解的类,获取对应的"<请求匹配条件,控制器方法>“映射关系,并注册到映射关系表中。请求匹配条件"通过RequestMappingInfo包装,而"控制器方法"则通过HandlerMethod来包装和表示