RequestMappingHandlerMapping的作用是在容器啓動後將系統中所有控制器方法的請求條件(RequestMappingInfo)和控制器方法(HandlerMethod)的對應關係註冊到RequestMappingHandlerMapping Bean的內存中,待接口請求系統的時候根據請求條件和內存中存儲的系統接口信息比對,再執行對應的控制器方法。
1.首先分析下HandlerMethod(控制器方法包裝對象)
public class HandlerMethod {
//bean名稱,調試的時候看到是字符串控制器名稱(首字母小寫)
private final Object bean;
//bean工廠類,個人調試傳入的是DefaultListableBeanFactory
@Nullable
private final BeanFactory beanFactory;
//方法所屬類
private final Class<?> beanType;
//控制器方法
private final Method method;
//橋接方法,如果method是原生的,這個屬性就是method
private final Method bridgedMethod;
//封裝方法參數實例
private final MethodParameter[] parameters;
//Http狀態碼
@Nullable
private HttpStatus responseStatus;
//ResponseStatus註解的reason值
@Nullable
private String responseStatusReason;
//使用createWithResolvedBean方法創建的HttpMethod方法對象
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
//getInterfaceParameterAnnotations獲取
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
//類描述,使用initDescription方法解析beanType和method獲得
private final String description;
}
HandlerMethod類用於封裝控制器方法信息,包含類信息、方法Method對象、參數、註解等信息,具體的接口請求是可以根據封裝的信息調用具體的方法來執行業務邏輯;
2.RequestMappingInfo(請求信息)
RequestMappingInfo其實就是將我們熟悉的@RequestMapping註解的信息數據封裝到了RequestMappingInfo POJO對象之中,然後和HandlerMethod做映射關係存入緩存之中;
首先看下@RequestMapping註解,這個註解會將請求映射到控制器方法之上;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
/**
* 請求映射別名
*/
String name() default "";
/**
* url映射路徑,等價於value屬性
* 數組類型,同一個控制器支持多個路由請求
* 支持ant風格和通配符表達式
*/
@AliasFor("path")
String[] value() default {};
/**
* url映射路徑,等價於path屬性
* 數組類型,同一個控制器支持多個路由請求
* 支持ant風格和通配符表達式
*/
@AliasFor("value")
String[] path() default {};
/**
* Http請求方法,支持GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE請求
* 默認請求方法是GET請求,同時可以支持多個請求類型
*/
RequestMethod[] method() default {};
/**
* 此屬性可以指定同一個URL路由由多個控制器來處理,而參數的值不同,每個控制器可以根據
* 不同的參數值來處理請求
*/
String[] params() default {};
/**
* 請求頭中必須包含指定的參數纔可以處理請求
*/
String[] headers() default {};
/**
* 匹配請求Content-Type的媒體類型,示例如下:
* consumes = "text/plain"
* consumes = {"text/plain", "application/*"}
* consumes = MediaType.TEXT_PLAIN_VALUE
* 也可以使用!來表示非
*/
String[] consumes() default {};
/**
* 定義控制器處理程序生成的數據媒體類型,示例如下:
* produces = "text/plain"
* produces = {"text/plain", "application/*"}
* produces = MediaType.TEXT_PLAIN_VALUE
* produces = "text/plain;charset=UTF-8"
* 也可以使用!來表示非
*/
String[] produces() default {};
}
下面看下RequestMappingHandlerMapping類是如何獲取註解@RequestMapping的數據並封裝到RequestMappingInfo對象的方法中去:
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//獲取@RequestMapping註解類對象
RequestMapping requestMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element);
//調用創建RequestMappingInfo對象的方法
return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
}
createRequestMappingInfo方法使用RequestMappingInfo對象將RequestMapping對象中的相關信息 組裝起來,並返回一個RequestMappingInfo對象;
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
Builder builder = RequestMappingInfo.paths(
//解析路徑信息
this.resolveEmbeddedValuesInPatterns(requestMapping.path()))
//請求方法,如GET、POST
.methods(requestMapping.method())
//請求參數數據
.params(requestMapping.params())
//請求頭數據
.headers(requestMapping.headers())
//控制器可以接收的數據媒體類型
.consumes(requestMapping.consumes())
//控制器處理完成後響應的數據類型
.produces(requestMapping.produces())
//參數名稱
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
3.源碼執行邏輯分析
先總結下RequestMappingHandlerMapping的工作流程大致如下:
- 容器將RequestMappingHandlerMapping組件註冊入容器的時候,監測到了InitializingBean接口,註冊完成後會執行afterPropertiesSet方法;
- afterPropertiesSet方法會調用父類AbstractHandlerMethodMapping的afterPropertiesSet方法,然後調用initHandlerMethods方法,此方法會首先獲取容器中所有bean的beanName,然後循環調用processCandidateBean方法;
- processCandidateBean方法首先會獲取bean的Class類型,然後調用isHandler方法判定是否是控制器類,如果是則調用detectHandlerMethods方法;
- detectHandlerMethods方法首先會通過MethodIntrospector.selectMethods方法獲取bean中所有控制器方法的Method和RequestMappingInfo對應關係,然後循環調用registerHandlerMethod註冊方法;
- 在registerHandlerMethod方法中會將對應關係分別註冊入多個不同的Map關係映射中,其中mappingLookup集合Map是我們外部獲取系統中所有URL的入口地址,urlLookup是前端發送request請求時根據url獲取RequestMappingInfo信息的入口集合;
上面的類圖顯示RequestMappingHandlerMapping實現了InitializingBean接口,容器會在初始化RequestMappingHandlerMapping完成後調用afterPropertiesSet方法;
public void afterPropertiesSet() {
...
super.afterPropertiesSet();
}
afterPropertiesSet方法會調用父類AbstractHandlerMethodMapping的方法:
public void afterPropertiesSet() {
this.initHandlerMethods();
}
protected void initHandlerMethods() {
//獲取容器中所有的beanName
String[] var1 = this.getCandidateBeanNames();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
if (!beanName.startsWith("scopedTarget.")) {
this.processCandidateBean(beanName);
}
}
this.handlerMethodsInitialized(this.getHandlerMethods());
}
//掃描ApplicationContext容器中的Bean,判定並註冊爲HandlerMethods
protected String[] getCandidateBeanNames() {
return this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class);
}
//判定當前類是否是控制器類
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
/**
* 根據beanName獲取類類型,根據類類型判定是否是控制器
**/
protected void processCandidateBean(String beanName) {
Class beanType = null;
try {
beanType = this.obtainApplicationContext().getType(beanName);
} catch (Throwable var4) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
}
}
//判定bean的類型是控制器類
if (beanType != null && this.isHandler(beanType)) {
this.detectHandlerMethods(beanName);
}
}
//獲取控制器類中方法對象和RequestMappingInfo的對應關係
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
//獲取Method和RequestMappingInfo的對應關係
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
//獲取控制器handler的Method和RequestMappingInfo對應關係
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
}
//將控制器方法對應關係註冊進入內存對象之中
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
將@RequestMapping註解信息封裝成RequestMappingInfo信息
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//針對控制器方法也就是使用了@RequestMapping註解信息,創建一個RequestMappingInfo對象
//如果是一個控制器的普通方法,則返回的info對象是null
RequestMappingInfo info = this.createRequestMappingInfo(method);
if (info != null) {
//針對控制器類上註解信息,創建一個RequestMappingInfo信息
RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//合併控制器類和控制器方法上的RequestMappingInfo信息
info = typeInfo.combine(info);
}
//獲取自定義的控制器路由前綴信息
String prefix = this.getPathPrefix(handlerType);
if (prefix != null) {
//如果定義了路徑前綴信息,考慮合併前綴信息
info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);
}
}
return info;
}
對應關係要注入的Map對象
private final Map<T, AbstractHandlerMethodMapping.MappingRegistration<T>> registry = new HashMap();
//容器可以通過此對象獲取系統之中的所有URL
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap();
//存儲URL和HandlerMethod的對應關係
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
執行註冊對應關係的最終方法:
public void register(T mapping, Object handler, Method method) {
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0 && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
}
this.readWriteLock.writeLock().lock();
try {
//獲取控制器方法對應的HandlerMethod方法
HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
this.validateMethodMapping(handlerMethod, mapping);
//將RequestMappingInfo和HandlerMethod對應關係存入Map集合
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = this.getDirectUrls(mapping);
Iterator var6 = directUrls.iterator();
while(var6.hasNext()) {
String url = (String)var6.next();
//將url和RequestMappingInfo信息存入集合
this.urlLookup.add(url, mapping);
}
String name = null;
if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
this.addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name));
} finally {
this.readWriteLock.writeLock().unlock();
}
}
GitHub地址:https://github.com/mingyang66/spring-parent/tree/master/doc/base