1. HandlerMapping 概述
HandlerMapping 的存在其实就是解决 HttpServletRequest 与 Handler 之间映射关系!对应的子类主要分为基于 BeanName, ClassName, 注解信息来获取对应的映射的请求! 其子类如下:
1. HandlerMapping: 这个接口中定义了通过HttpServletRequest 来获取对应的 HandlerExecutionChain(PS: HandlerExecutionChain 中定义了请求的拦截器+最终调用的 Handler), 其中还有一个非常重要的变量 URI_TEMPLATE_VARIABLES_ATTRIBUTE, 这个key对应的value其实就是 uri template 变量(@PathVariable 解析属性时获取数据的来源)
2. AbstractHandlerMapping: 这个类继承了 WebApplicationObjectSupport, 这就让其具有通过 ApplicationContextAware 接口来触发初始化的功能, 当然其也在初始化方法中获取了 MappedInterceptor, 并且定义了通过 HttpServletRequest 获取 HandlerExecutionChain 的主逻辑 <-- 其也留下了获取 Handler 的模版方法 getHandlerInternal
3. AbstractHandlerMethodMapping: 通过 HttpServletRequest 来获取 HandlerMethod; 其通过 InitializingBean.afterPropertiesSet 来搜索获取所有 HandlerMethod <-- 这部分就是在HandlerMapping初始化时获取所有HandlerMethod; 并且留下了 getMappingForMethod 等获取 RequestMappingInfo 的模版方法
4. AbstractUrlHandlerMapping: 与 AbstractHandlerMethodMapping 不同, AbstractUrlHandlerMapping.getHandlerInternal 返回的是个 Object(PS: 这里的Object有可能是 ApplicationContext 中的 BeanName, 或直接是个 Bean), 而且其中完成了 URI 与 handler 的注册流程 registerHandler(urlPath, beanName)
5. RequestMappingHandlerMapping: 基于 HandlerMethod 的HandlerMapping, 主要实现了getMappingForMethod(基于Method,handlerType获取RequestMappingInfo), 而 RequestMappingInfoHandlerMapping 中主要是在查找 HandlerMethod 时对 HttpServletRequest 中的一些操作, 比如设置 uri template variable
6. SimpleUrlHandlerMapping: 在配置 uri 与handler 之间映射关系的 HandlerMapping (PS: 这里的 handler 可以是任意类型, 方正可以有对应的 HandlerAdapter 来进行激活它)
7. AbstractDetectingUrlHandlerMapping: 从类名中我们就可以获知, 这是一个自动获取 Handler 已经 url 的类, 这个方法的触发操作是 ApplicationContextAware.setApplicationContext
8. BeanNameUrlHandlerMapping: 以 BeanName 为 uri 的 HandlerMapping <-- 其中 BeanName 必需以 "/" 开头
9. ControllerBeanNameHandlerMapping: 通过 BeanName 构成 uri 的 HandlerMapping (PS: 这个类已经过期)
10. ControllerClassNameHandlerMapping: 基于 className 生成 uri 的 HandlerMapping(PS: 这个类已经过期)
11. DefaultAnnotationHandlerMapping: 基于 @RequestMapping 的 HandlerMapping, 这里面会出现 多个 urls 对应一个 Handler (PS: 这个类已经过期)
2. HandlerMapping 抽象类 AbstractHandlerMapping
AbstractHandlerMapping 中定义了获取 Handler 的主逻辑; 其主要有一下属性
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
// 默认请求处理器, 在找不到 Handler时返回的默认值
private Object defaultHandler;
// 获取 url 的工具类
private UrlPathHelper urlPathHelper = new UrlPathHelper();
// 路径匹配器 <- 进行正则之类匹配的操作
private PathMatcher pathMatcher = new AntPathMatcher();
// Handler 处理拦截器 <- 最后还是会将拦截器放入 adaptedInterceptors 中
private final List<Object> interceptors = new ArrayList<Object>();
// Handler 处理拦截器, 这个与 interceptors不同, 在封装 Chain 时其实使用的是 adaptedInterceptors
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
// 跨域资源共享(Cross-origin resource sharing)
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
从上面属性我们发现了 HandlerInterceptor, 这是一个作用于 handler 之前与之后的拦截器, 对应的获取操作是在 ApplicationContextAware.setApplicationContext 中, 方法也很简单, 直接通过ApplicationContext获取其中的所有 HandlerInterceptor 类型的 Bean
// 通过 initApplicationContext 方法进行初始化, 其一般是由父类执行 ApplicationContextAware#setApplicationContext() 方法间接调用
// 主要的目的是获取 springMVC 上下文中的拦截器集合, 特指 MappedInterceptor
@Override
protected void initApplicationContext() throws BeansException {
// 供子类扩展添加拦截器, 目前 spring 没有自行实现
extendInterceptors(this.interceptors);
// 搜索 springMVC 中的 MappedInterceptors 保存至 adaptorInterceptors 集合
detectMappedInterceptors(this.adaptedInterceptors);
// 将 interceptors 集合添加至 adaptedInterceptors 集合
initInterceptors();
}
而获取 HandlerExecutionChain 的主逻辑也在这里进行类实现, 当然留下了另外一个模版方法 getHandlerInternal(request)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取到的 handler 对象一般为 bean/HandlerMathod, 它是方法为抽象方法, 供子类实现
Object handler = getHandlerInternal(request); // 正真调用的是 AbstractHandlerMethodMapping#getHandlerInternal()
// 上述找不到则使用默认的处理类, 没有设定则返回 null, 则会返回前台 404 错误
if (handler == null) handler = getDefaultHandler();
// 如果也没有提供默认的 handler, 则无法继续处理返回 null
if (handler == null) return null;
// 若 handler 是 beanName, 则 通过名称取出对应的 Handler bean
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 创建处理链对象 <-- 其中就是封装 HandlerInterceptor 与 Handler
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
3. AbstractHandlerMapping 的子类 AbstractHandlerMethodMapping
相对于AbstractHandlerMapping, AbstractHandlerMethodMapping 主要完成了Handler的获取操作, 对应代码在getHandlerInternal中, 通过 uri 与 HttpServletRequest 获取 HandlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取访问的路径, 一般类似于 request.getServletPath() 返回不含 contextPath 的访问路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 获取读锁
this.mappingRegistry.acquireReadLock(); // 获取读写所的读锁
try {
// 获取 HandlerMethod 作为 handler 对象, 这里涉及到路径匹配的优先级
// 优先级: 精确匹配 > 最长路径匹配 > 扩展名匹配
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// handlerMethod 内部含有 bean 对象, 其实指的是对应的 Controller
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// 释放 度锁
this.mappingRegistry.releaseReadLock();
}
}
上面是对应的获取逻辑, 与之对应的还有HandlerMethod 的注册逻辑, 注册的入口在 InitializingBean.afterPropertiesSet, 大体流程如下:
1. 获取 Spring 中所有注册的 Bean 的Name, 并且一个一个获取对应 BeanType
2. 判断BeanType 是否被注解 @Controller 或 @RequestMapping 注解, 若是的话, 则进行解析这个 BeanType
3. 筛选 BeanType 中被 @RequestMapping 的方法
对应的代码如下:
protected void initHandlerMethods() {
// 获取 springMVC 上下文的所有注册的 Bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) { // 循环遍历 ApplicationContext 中的 所有 beanName
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {}
if (beanType != null && isHandler(beanType)) { // isHandler() 是抽象方法, 主要供子类 需要扫描什么类型的 bean <-- 判断是否是 Handler
// 解析其中的 handlerMethod 进行注册 <-- 其中注册中会注册到好几个 Map 中 RequestMappingInfo <-> HandlerMethod, URI <-> HandlerMethod, name <-> HandlerMethod, RequestMappingInfo <-> MappingRegistration
detectHandlerMethods(beanName);
}
}
}
}
protected void detectHandlerMethods(final Object handler) {
// 获取 handler 的类型 <- 若 handler 是 String, 则通过 ApplicationContext 直接获取 真实的 Bean
Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// 因为有些是 CGLIB 代理生成的, 获取真实类
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
return getMappingForMethod(method, userType); // 模板方法获取 handlerMethod 的 mapping 属性
}
catch (Throwable ex) {}
}
});
for (Map.Entry<Method, T> entry : methods.entrySet()) { // 对查找到的 HandlerMethod 进行注册, 默认就是 被@RequestMapping 注解修饰的,保存到内部类mappingRegistry 对象中
// 做下判断, method 是否从属于 userType
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue(); // 获取 RequestMappingInfo 的信息
registerHandlerMethod(handler, invocableMethod, mapping); // 将 RequestMappingInfo <-> HandlerMethod, URI <-> HandlerMethod, name <-> HandlerMethod, RequestMappingInfo <-> MappingRegistration
}
}
其上只是获取 Method 的操作, 而对应正真注册的工作交给 MappingRegistry 来完成, 而 MappingRegistry 中有如下属性:
1. registry: RequestMappingInfo <--> MappingRegistration 的映射关系(MappingRegistration 是包含 HandlerMethod 与 RequestMappingInfo 的包装类)
2. mappingLookup: RequestMappingInfo <--> HandlerMethod 的映射关系
3. urlLookup: uri <--> HandlerMethod 的映射关系
4. nameLookup: name <--> HandlerMethod 的映射关系
5. readWriteLock: 读写锁, 在多线程环境中对共享资源(映射关系), 读写权限的控制
对应的注册逻辑如下:
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock(); // 获取写锁
try { // 封装 HandlerMethod <-- 其中有 Method, Bean, 以及对应的 MethodParameter
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping); // 断言 mapping 没有注册过
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
this.mappingLookup.put(mapping, handlerMethod); // 将 RequestMethodInfo 与 HandlerMethod 放入 mappingLookup 中
List<String> directUrls = getDirectUrls(mapping); // 获取 @RequestMapping 中 value | path 中的信息 <-- 并且不能含有 *|? (PS: 一个 RequestMapping 具有多个 path|value)
for (String url : directUrls) {
this.urlLookup.add(url, mapping); // 将 @RequestMapping 中的 value|path 注册到 urlLookup 中
}
String name = null; // 获取 @RequestMapping 中的 name 值
if (getNamingStrategy() != null) { // nameStrategy 其实就是 RequestMappingInfoHandlerMethodMappingNamingStrategy
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod); // 将 name <--> handlerMethod 加入 nameLookup
}
// Cors 跨域访问的处理
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 将 RequestMappingInfo 与 MappingRegistration 注册到 registry 中
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
4. AbstractHandlerMethodMapping 的子类 RequestMappingHandlerMapping
RequestMappingHandlerMapping 是一个收集被@RequestMapping修饰的方法的 HandlerMapping, 相对其父类其主要完成了一下功能:
1. 根据 Method, handlerType, 收集@RequestMapping, 并以此创建创建 RequestMappingInfo, 其中涉及到 RequestMappingInfo中的建造者模式(建造RequestMappingInfo), 策略模式(针对 @RequestMapping 中数据的不同条件判断器 RequestCondition)
2. 根据 beanType 上是否被 @Controller | @RequestMapping注解修饰来判断 beanType 是否是符合的 Handler
主要代码如下:
protected boolean isHandler(Class<?> beanType) { // class 是否是 Handler
// 通过 @Controller @RequestMapping 来进行匹配
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
在这里还提到了将 @RequestMapping 解析后得到的对象 RequestMappingInfo, 这个类中包含了 @RequestMapping 注解中各个属性的条件匹配器(匹配器主要有 combine: 组合两个匹配器, getMatchingCondition: 得到两个RequestCondition都匹配的 RequestCondition), 下面是其主要属性:
// @RequestMapping 中的信息
private final String name;
// @RequestMapping 中 value|path 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final PatternsRequestCondition patternsCondition;
// @RequestMapping 中 RequestMethod 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final RequestMethodsRequestCondition methodsCondition;
// @RequestMapping 中 param 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final ParamsRequestCondition paramsCondition;
// @RequestMapping 中 headers 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final HeadersRequestCondition headersCondition;
// @RequestMapping 中 consumes 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final ConsumesRequestCondition consumesCondition;
// @RequestMapping 中 produces 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并进行匹配
private final ProducesRequestCondition producesCondition;
// RequestCondition 的 holder
private final RequestConditionHolder customConditionHolder;
PS: 在进行查找HttpServletRequest对应的HandlerMethod 时, 会通过 RequestMappingInfo.getMatchingCondition 来获取与 HttpServletRequest & @RequestMapping 都匹配的 RequestMappingInfo, 有的话表示找到了需要的Handler
5. AbstractHandlerMapping 的子类 AbstractUrlHandlerMapping
相较于 AbstractHandlerMapping, AbstractUrlHandlerMapping中完成了
1. getHandlerInternal方法, Handler 的获取(PS: 这里handler 可以是任意类型);
2. lookupHandler方法, 通过 urlPath, HttpServletRequest 获取对应的 Handler, 这里也包含了 "URI 模版变量" 的获取(PS: 通过 AntPathMatcher.extractUriTemplateVariables)
3. registerHandler方法, 将 urlPath, handler 注册到 handlerMap 中
getHandlerInternal中定义了获取 Handler 的主逻辑, 若获取不到且uri是"/"则使用 rootHandler, 否则使用 defaultHandler; 代码如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 从 Request 中得到 请求的 URL 路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 将得到的 URL 路径与 Handler 进行匹配, 得到对应的 Handler, 如果没有对应的 Handler, 返回 null, 这样默认的 Handler 会被使用
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
// 如果请求的路径仅仅是 "/", 那么使用 RootHandler 进行处理
rawHandler = getRootHandler();
}
if (rawHandler == null) {
// 无法找到 Handler, 则使用默认的 Handler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// 根据 beanName 找到对应的 bean
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
// 模板方法 校验 hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校验类上面是否有 @RequestMapping 注解
validateHandler(rawHandler, request);
// 将 rawHandler HandlerInterceptor 包装到 chain 中 <- 其中涉及到 暴露 URI 模版变量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其实就是 @PathVariable 这个注解解析时用到的值
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
但真正的通过 urlPath 来进行直接匹配或通配符匹配是在 lookupHandler 的方法中, 并且方法中还有提取 uri 模版变量的步骤, 提取好后就直接存储在 HttpServletRequest 中
// lookupHandler 根据 URL 路径启动在 handlerMap 中对 handler 的检索, 并最终返回 handler对象
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配情况的处理
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) { // 若 handler 是 string 类型, 则将 handler 当作类名, 直接从 BeanFactory 中获取 Bean
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 模板方法 校验 hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校验类上面是否有 @RequestMapping 注解
validateHandler(handler, request);
// 将 rawHandler HandlerInterceptor 包装到 chain 中 <- 其中涉及到 暴露 URI 模版变量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其实就是 @PathVariable 这个注解解析时用到的值
return buildPathExposingHandler(handler, urlPath, urlPath, null); // <-- 最后一个参数是 null, 则 URI 模版参数就没有了
}
// 通配符匹配的处理
// Pattern match?
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) { // 通过 AntPathMatcher 来进行匹配 <-- 正则匹配
matchingPatterns.add(registeredPattern); // 匹配成功, 加入 matchingPatterns <-- matchingPatterns 里面可能有多个值
}
else if (useTrailingSlashMatch()) { // 是否使用尾部反斜杆进行匹配, 若是的话, 则直接在尾部加上 "/" 再进行匹配
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/"); // 匹配成功, 加入 matchingPatterns <-- matchingPatterns 里面可能有多个值
}
}
}
String bestMatch = null; // 获取 AntPatternComparator, 主要是处理多个 urlPath 的排序
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) { // 通过 AntPatternComparator 进行排序, 获取排序的第一个值
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0); // 获取匹配成功的第一个路径
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch); // 从 handlerMap 从获取 bestMatch 匹配的 handler
if (handler == null) {
if (bestMatch.endsWith("/")) { // 若获取不到, 但 bestMatch 又是以 "/" 结尾的, 则去除 "/", 再从 handlerMap 里面获取一次
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) throw new IllegalStateException("Could not find handler for best pattern match [" + bestMatch + "]");
}
// Bean name or resolved handler?
if (handler instanceof String) { // 若从 handlerMap 里面获取出的 handler 是 String, 则再从 ApplicationContext 里面获取对应的 Bean
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request); // 模板方法 校验 hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校验类上面是否有 @RequestMapping 注解
// 获取 正则表达式中 *, ? 所代表的真实字符串
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them // 获取 URI 模版变量的值
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
// 将 rawHandler HandlerInterceptor 包装到 chain 中 <- 其中涉及到 暴露 URI 模版变量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其实就是 @PathVariable 这个注解解析时用到的值
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
接着就是registerHandler, 将 url <--> handler 注册到映射容器 handlerMap 中, 整个流程比较简单, 若 urlPath 是 "/", 则设置为 rootHandler, 若 urlPath 是 "/*", 则设置为 defaultHandler, 其他的则直接设置到 handlerMap(PS: 从中我们也可以看出这个 HandlerMapping 其实不是发安全的, 从 handlerMap 是个 HashMap 可以看出)
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Object resolvedHandler = handler;
// 若 handler 是 String (BeanName), 是否需要将其初始化成 Bean
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) { // 从 ApplicationContext 拿出 Handler
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
// 是否已存在对应的 handler, 若存在, 且里面存储的 handler 与现在将注入进去的不同, 则抛出异常
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else { // 不存在 handler
// 处理 URL 是 "/" 地映射, 把这个 "/" 映射地 controller 设置到 rootHandler 中
if (urlPath.equals("/")) {
// "/" --> 设置为 rootHandler
setRootHandler(resolvedHandler);
}
// 处理 URL 是 "/*" 地映射, 把这个 "/*" 映射地 controller 设置到 defaultHandler 中
else if (urlPath.equals("/*")) {
// 对 "/*" 的匹配 设置默认的 handler
setDefaultHandler(resolvedHandler);
}
// 处理正常地 URL 映射, 设置 handlerMap 的 key 和 value, 分别对应 URL 和 映射的 controller
else {
// 其余 的路径绑定关系则存入 handlerMap
this.handlerMap.put(urlPath, resolvedHandler);
}
}
}
6. AbstractUrlHandlerMapping 的子类 SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 就像其名字一样, 一个简单的 HandlerMapping, 一般都是在xml文件中配置一个, 配置的内容主要是 mappings <-- 这里包含了 urlPath 与 handler 的映射关系, 下面是一个简单的配置文件内容
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
// 处理 /* 的handler
<property name="defaultHandler"><ref local="starController"/></property>
// 处理 / 的 handler
<property name="rootHandler"><ref local="mainController"/></property>
// 是否使用尾部斜杆匹配url
<property name="useTrailingSlashMatch" value="true"/>
<property name="urlMap">
<map>
<!-- 处理 /welcome 的是 welcomeController -->
<entry key="/welcome"><ref local="welcomeController"/></entry>
</map>
</property>
</bean>
7. AbstractUrlHandlerMapping 的子类AbstractDetectingUrlHandlerMapping
AbstractDetectingUrlHandlerMapping 从其名字中我们也可以看出, 这是一个自动获取 URL 的HandlerMapping, 同样对应的触发还是通过 ApplicationContextAware.setApplicationContext 方法!
// 注册所有在 ApplicationContext 中的 Handler
protected void detectHandlers() throws BeansException {
// 获取 ApplicationContext 中所有的 beanNames
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 根据 beanName 获取所有的 urls, 并注册到 AbstractUrlHandlerMapping.handlerMap 中
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName); // 通过 beanName 获取 url, 比如 BeanNameUrlHandlerMapping 是以 beanName 为 url <-- beanName 必需以 "/" 开头
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
}
8. AbstractDetectingUrlHandlerMapping 的子类
1. BeanNameUrlHandlerMapping: 一个把beanName当作urlPath的HandlerMapping(PS: BeanName必需以 "/" 开头)
2. ControllerClassNameHandlerMapping: 基于 beanName 生成 uri 的 HandlerMapping(PS: 此类已经废弃)
3. ControllerBeanNameHandlerMapping: 基于 className 生成 uri 的 HandlerMapping(PS: 此类已经废弃)
4. DefaultAnnotationHandlerMapping: 扫描在 Bean 上注解 @RequestMapping|@Controller 的 类, 收集这个类中所有被 @RequetsMapping 注解修饰的方法, 并将 urlPaths <---> handlerType 注册到AbstractUrlHandlerMapping.handlerMap中(PS: 不过此类也已经废弃)
9. HandlerMapping 中的优秀设计
1. 模版模式: 在Spring中这是一个非常常见的设计模式, 比如 AbstractDetectingUrlHandlerMapping.detectHandlers, 其中定义了获取handler的主逻辑, 但留下了获取 url 的模版方法, 在其子类中进行类相应的实现, 子类中有根据 beanClass|beanName, 也有根据子类方法上的 @RequestMapping 注解
2. 建造者模式: HandlerMapping中主要体现在创建RequestMappingInfo的过程, 通过建造者 RequestMappingInfo.DefaultBuilder 来创建, 当创建一个对象时需要很多属性设置时就可以用 建造者模式
3. 策略模式: 这里的策略模式体现在 RequestCondition上(AbstractRequestCondition: 主要是将@RequestMapping上属性与 HttpServletRequest进行条件匹配, 最终找出两者都符合的 RequestMappingInfo)
4. 组合模式: CompositeRequestCondition 将所有 RequestCondition组合起来, 构成一个 RequestCondition来进行匹配操作
5. 责任链模式: 这个主要体现在 HandlerExecutionChain上, 链条的里面都是 interceptor, 最后执行的才是 Handler
10. 总结
整个 HandlerMapping 体系的设计融合了常见的设计模式以及Spring IOC中的扩展接口 ApplicationContextAware, 完成了 urlPath 与 handler 之间映射的注册, 搜索工作!
11. 参考资料
SpringMVC源码分析系列
Spring MVC源码剖析
Spring源码情操陶冶
Spring 揭秘
Spring 技术内幕
Spring 源码深度分析
看透 Spring MVC 源代码分析与实践