SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化
RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller.
@Controller
@RequestMapping(value = "books")
public class BookController { @RequestMapping(value = "/{id}")
@ResponseBody
public String getBook(@PathVariable("id") String id) {
// ...
return id;
}
}
初始化时,3个类的大致分工如下:
AbstractHandlerMethodMapping定义整个算法流程;
RequestMappingInfoHandlerMapping提供匹配条件RequestMappingInfo的解析处理;
RequestMappingHandlerMapping根据@RequestMapping注解生成 RequestMappingInfo,同时提供isHandler实现
整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.
1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object
2. 迭代类,分别判断isHandler判断目标类是否Handler
2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)
3. 对handler解析出所有需要分发的方法detectHandlerMethods
3.1 获取原始的Class<?>
3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类
RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo
3.3 对过滤到的每个method进行注册registerHandlerMethod
a, 使用createHandlerMethod封装处理器为HandlerMethod
b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)
c, 设置匹配条件到handler method的映射关系
d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现
4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做
在讲初始化之前,我们先来聊聊使用到的一些概念
1. 映射关系,url到匹配条件RequestMappingInfo,匹配条件到HandlerMethod
2. 特殊的MultiValueMap,特别在value是个List
3. 使用到注解@Controller,@RequestMapping
4. 封装处理器信息的HandlerMethod
5. 封装各类匹配条件的RequestMappingInfo(诸如pattern,http method,request parameter等)
6. RequestCondition记录匹配条件
1. 进行request分发前,需要在初始化时准备好映射关系,这边AbstractHandlerMethodMapping中有两个属性保存了映射关系
// AbstractHandlerMethodMapping
// 匹配条件到HandlerMethod的映射
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
// url到匹配条件的映射
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
2. 这边的MultiValueMap其实挺简单,就是map的值是个list
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
// ...
}
3. 我们再来看看这边使用到的两个注解:
// @Controller
// org.springframework.stereotype.Controller
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller { /**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any
*/
String value() default ""; }
// @RequestMapping
// org.springframework.web.bind.annotation.RequestMapping
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping { /**
* url路径,如/myPath/*.do
*/
String[] value() default {}; /**
* HTTP request methods 如:GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
*/
RequestMethod[] method() default {}; /**
* requeset parameter 有3种匹配方式,是否包含某个参数,参数值相等,参数值不等于某个值,如myParam!=myValue
*/
String[] params() default {}; /**
* request的header
*/
String[] headers() default {}; /**
* request的content type
*/
String[] consumes() default {}; /**
* 返回内容的content type
*/
String[] produces() default {}; }
}
4. HandlerMethod封装了处理器相关的全部信息,如类Object,方法Method,BeanFactory,参数MethodParameter[],原始方法Method
// HandlerMethod
// org.springframework.web.method.HandlerMethod
private final Object bean;// 因为final不可修改,所以下面每次需要修改信息时,都需要new一个 private final Method method; private final BeanFactory beanFactory; private final MethodParameter[] parameters; private final Method bridgedMethod;
5. 这边匹配条件的范型只有一个实现,RequestMappingInfo.匹配条件里记录的是RequestCondition子类,用于诸如pattern,http method,request parameter等
// RequestMappingInfo
// javax.servlet.http.HttpServletRequest.RequestMappingInfo
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition; private final RequestConditionHolder customConditionHolder;
// ... }
6. 最后再简单看看RequestCondition ,这边定义了3个方法
package org.springframework.web.servlet.mvc.condition;
public interface RequestCondition<T> {
/**
* 拼接条件
*/
T combine(T other); /**
* 查找匹配的条件,并返回
*/
T getMatchingCondition(HttpServletRequest request); /**
* 用于排序
*/
int compareTo(T other, HttpServletRequest request);
}
看看继承体系吧,老套路,定义接口,然后模板方法实现主要逻辑,具体算法留给子类实现,还有正事要做,还是后期再细化吧.
正文
整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.copy一段,省得回去比对看
1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object
2. 迭代类,分别判断isHandler判断目标类是否Handler
2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)
3. 对handler解析出所有需要分发的方法detectHandlerMethods
3.1 获取原始的Class<?>
3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类
RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo
3.3 对过滤到的每个method进行注册registerHandlerMethod
a, 使用createHandlerMethod封装处理器为HandlerMethod
b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)
c, 设置匹配条件到handler method的映射关系
d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现
4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做
// AbstractHandlerMethodMapping
/** 这个方法哪来的,具体看备注的InitializingBean
* Detects handler methods at initialization.
*/
public void afterPropertiesSet() {
initHandlerMethods();
} /**扫描ApplicationContext中的bean,然后筛选handler method 并注册
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler(Class)
* @see #getMappingForMethod(Method, Class)
* @see #handlerMethodsInitialized(Map)
*/
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
} String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) {
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
预留给子类实现的判断handler,实际是由RequestMappingHandlerMapping实现
// AbstractHandlerMethodMapping
/**
* Whether the given type is a handler with handler methods.
* @param beanType the type of the bean being checked
* @return "true" if this a handler type, "false" otherwise.
*/
protected abstract boolean isHandler(Class<?> beanType);
// RequestMappingHandlerMapping
这边判断的逻辑很简单,类上使用Controller或RequestMapping其中至少一个注解就可以.
/**
* {@inheritDoc}
* Expects a handler to have a type-level @{@link Controller} annotation.
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
// AbstractHandlerMethodMapping
/**
* Look for handler methods in a handler.
* @param handler the bean name of a handler or a handler instance
*/
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String) ?
getApplicationContext().getType((String) handler) : handler.getClass(); final Class<?> userType = ClassUtils.getUserClass(handlerType); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
}); for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
registerHandlerMethod(handler, method, mapping);
}
}
// AbstractHandlerMethodMapping
这边具体的实现是由RequestMappingHandlerMapping实现,根据注解生产匹配关系,这边实现类是RequestMappingInfo,就是代码有点多,慢慢看
/**
* Provide the mapping for a handler method. A method for which no
* mapping can be provided is not a handler method.
* @param method the method to provide a mapping for
* @param handlerType the handler type, possibly a sub-type of the method's
* declaring class
* @return the mapping, or {@code null} if the method is not mapped
*/
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
// RequestMappingHandlerMapping
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
*
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
*
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
// 读取方法上的RequestMapping注解信息
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
// 读取自定义的条件,这边没有使用
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
// 根据方法上的RequsetMapping注解和自定义条件,生成匹配条件.这边的匹配条件包括http method,request parameter,request header等
info = createRequestMappingInfo(methodAnnotation, methodCondition);
// 读取类上的RequestMapping注解信息
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
// 生成类上的匹配条件,并合并方法上的
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
// RequestMappingHandlerMapping
/**
* Created a RequestMappingInfo from a RequestMapping annotation.
*/
private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
return new RequestMappingInfo(
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
new RequestMethodsRequestCondition(annotation.method()),
new ParamsRequestCondition(annotation.params()),
new HeadersRequestCondition(annotation.headers()),
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
customCondition);
} /**
* Resolve placeholder values in the given array of patterns.
* @return a new array with updated patterns
*/
protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
if (this.embeddedValueResolver == null) {
return patterns;
}
else {
String[] resolvedPatterns = new String[patterns.length];
for (int i=0; i < patterns.length; i++) {
resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
}
return resolvedPatterns;
}
}
// AbstractHandlerMethodMapping
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
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);// 匹配条件requestMappingInfo 到处理器HandlerMethod
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
} Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);// url到匹配条件RequestMappingInfo
}
}
}
// AbstractHandlerMethodMapping
/**
* Create the HandlerMethod instance.
* @param handler either a bean name or an actual handler instance
* @param method the target method
* @return the created HandlerMethod
*/
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
}
else {
handlerMethod = new HandlerMethod(handler, method);
}
return handlerMethod;
}
// AbstractHandlerMethodMapping
/**
* Extract and return the URL paths contained in a mapping.
*/
protected abstract Set<String> getMappingPathPatterns(T mapping);
RequestMappingInfoHandlerMapping会实现这个模板方法
// RequestMappingInfoHandlerMapping
/**
* Get the URL path patterns associated with this {@link RequestMappingInfo}.
*/
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
备注:
1. 这边的afterPropertiesSet是因为实现了InitializingBean接口
// org.springframework.beans.factory.InitializingBean
/**
* Interface to be implemented by beans that need to react once all their
* properties have been set by a BeanFactory: for example, to perform custom
* initialization, or merely to check that all mandatory properties have been set.
*
* <p>An alternative to implementing InitializingBean is specifying a custom
* init-method, for example in an XML bean definition.
* For a list of all bean lifecycle methods, see the BeanFactory javadocs.
*
* @author Rod Johnson
* @see BeanNameAware
* @see BeanFactoryAware
* @see BeanFactory
* @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
* @see org.springframework.context.ApplicationContextAware
*/
public interface InitializingBean { /**
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception; }
SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化的更多相关文章
- SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发
AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程 RequestMappingInfoHandlerMapping根据RequestM ...
- SpringMVC源码解读 - HandlerMapping - SimpleUrlHandlerMapping初始化
摘要: SimpleUrlHandlerMapping只是参与Handler的注册,请求映射时由AbstractUrlHandlerMapping搞定. 初始化时,通过setMappings(Prop ...
- SpringMVC源码解读 - HandlerMapping
SpringMVC在请求到handler处理器的分发这步是通过HandlerMapping模块解决的.handlerMapping 还处理拦截器. 先看看HandlerMapping的继承树吧 可以大 ...
- SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInterna ...
- SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发
AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现 2. 如果没有获取Handl ...
- SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系
一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest& ...
- SpringMVC源码解读 - RequestMapping注解实现解读
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系 https://www.cnblogs.com/leftthen/p/520840 ...
- SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo
使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition, ...
- springMVC源码分析--HandlerMapping(一)
HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...
随机推荐
- Spring3.2 Contorller单元测试参数问题: java.lang.NoSuchMethodException
使用3.2做单元测试的时候发现这个问题,因为之前都是用3.0中的配置适配器使用AnnotationMethodHandlerAdapter,到3.2中升级为RequestMappingHandlerA ...
- java学习第01天(程序开发体验)
1.基本写法 class Demo{ public static void main(String[] args){ System.out.print("Hello World") ...
- 微信小程序开发教程(七)逻辑层——.js详解
逻辑层,是事务逻辑处理的地方.对于小程序而言,逻辑层就是.js脚本文件的集合.逻辑层将数据进行处理后发送给视图层,同时接收视图层的事件反馈. 微信小程序开发框架的逻辑层是由JavaScript编写.在 ...
- tensorflow中的卷积和池化层(一)
在官方tutorial的帮助下,我们已经使用了最简单的CNN用于Mnist的问题,而其实在这个过程中,主要的问题在于如何设置CNN网络,这和Caffe等框架的原理是一样的,但是tf的设置似乎更加简洁. ...
- c++刷题(15/100)矩阵转置,最深子树
题目一:矩阵转置 给定一个矩阵 A, 返回 A 的转置矩阵. 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引. 示例 1: 输入:[[1,2,3],[4,5,6],[7,8,9]] 输出 ...
- Python练习-time模块
明天的明天的明天,雾草! # 编辑者:闫龙 #显示当前时间三天后是星期几? import time t = time.time()+((24*3600)*3) tl = time.localtime( ...
- C# 安装部署Windows服务脚本
@echo off Installutil.exe 程序目录 F:\test\TestWindows.exe 服务程序目录 @sc start "服务名称" @sc config ...
- ASP.NET 前台Javascript调用后台代码 / 后台调用前台Javascript
1:ASP.NET 前台Javascript调用后台代码 1.1:前台Javascript <script> function CallCs() { var str = "< ...
- 系统学习(javascript)_基础(语法)
1.分类 ECMAScript javascript的标准 DOM Document object Model文档对象模型 BOM Browser object Model 浏览器对象模型 ti ...
- Sysmon + NXlog构建简单的windows安全监控
工具: Sysmon (sysmon 5.0) ,NXlog(nxlog-ce-2.9.1716.msi) . Sysmon监控系统并生成windows event log, NXlog将wind ...