Spring 註解面面通 之 @RequestMapping 註冊處理方法原始碼解析
技術標籤:Spring 全面解析SpringRequestMapping註解註冊
@RequestMapping
原始碼解析主要分為兩個階段:
① @RequestMapping
註釋的方法掃描註冊。
② 請求匹配@RequestMapping
註釋的方法。
本文針對第①
階段從原始碼角度進行解析,關於第②
階段請參照《Spring 註解面面通 之 @RequestMapping 請求匹配方法原始碼解析》。
注意:@RequestMapping
註釋方法掃描註冊的起點是RequestMappingHandlerMapping.afterPropertiesSet()
。
<annotation-driven />
為何要先說<annotation-driven />
配置呢?
@RequestMapping
註釋方法掃描註冊,自RequestMappingHandlerMapping.afterPropertiesSet()
開始,RequestMappingHandlerMapping
實現了InitializingBean
介面,重寫方法afterPropertiesSet()
。
RequestMappingHandlerMapping
正是在<annotation-driven />
配置解析時註冊到BeanFactory
中。
/**
* 解析<annotation-driven />配置.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
XmlReaderContext readerContext = parserContext.getReaderContext();
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition (element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
// 建立ContentNegotiationManager.
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
// 建立RequestMappingHandlerMapping的Bean定義.
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 設定排序序號.
handlerMappingDef.getPropertyValues().add("order", 0);
// 設定ContentNegotiationManager.
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
// 矩陣變數enable-matrix-variables屬性解析.
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
// 呼叫configurePathMatchingProperties(...)配置RequestMappingHandlerMapping屬性.
// 處理屬性包括:useSuffixPatternMatch、useTrailingSlashMatch、useRegisteredSuffixPatternMatch、urlPathHelper、pathMatcher.
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
// 設定CORS相關配置.
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
......
// 註冊RequestMappingHandlerMapping定義.
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
......
return null;
}
@RequestMapping
註釋方法掃描註冊
@RequestMapping
註釋方法掃描註冊流程:
1) RequestMappingHandlerMapping.afterPropertiesSet()
方法。
① 建立RequestMappingInfo
構建配置,設定到RequestMappingHandlerMapping
的config
屬性中。
② 設定config
的urlPathHelper
、pathMatcher
、suffixPatternMatch
、trailingSlashMatch
、risteredSuffixPatternMatch
、contentNegotiationManager
屬性。
③ 呼叫AbstractHandlerMethodMapping.afterPropertiesSet()
繼續處理。
/**
* Bean設定屬性後,初始化呼叫.
*/
@Override
public void afterPropertiesSet() {
// 建立RequestMappingInfo構建配置.
this.config = new RequestMappingInfo.BuilderConfiguration();
// 設定UrlPathHelper.
this.config.setUrlPathHelper(getUrlPathHelper());
// 設定PathMatcher.
this.config.setPathMatcher(getPathMatcher());
// 設定SuffixPatternMatch.
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
// 設定TrailingSlashMatch.
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
// 設定RegisteredSuffixPatternMatch.
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
// 設定ContentNegotiationManager.
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 呼叫AbstractHandlerMethodMapping.afterPropertiesSet().
super.afterPropertiesSet();
}
2) AbstractHandlerMethodMapping.initHandlerMethods()
方法。
① 從ApplicationContext
總取得所有以註冊的Bean
名稱陣列。
② 遍歷從ApplicationContext
總取得所有以註冊的Bean
名稱陣列。
③ 若Bean
的名稱以scopedTarget.
開頭,不予處理此類Bean
。
④ 根據當前Bean
名稱獲取對應的Bean
型別。
⑤ 若當前Bean
型別不是由@Cotroller
和@RequestMapping
註釋,不予處理此類Bean
。
⑥ 從處理器中查詢處理方法,並註冊到AbstractHandlerMethodMapping
的mappingRegistry
屬性中。
/**
* 掃描ApplicationContext中的Bean,檢測並註冊處理程式方法.
*/
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
// 取得ApplicationContext中註冊的所有Bean.
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
// 遍歷ApplicationContext中註冊的所有Bean.
for (String beanName : beanNames) {
// 不以"scopedTarget."開頭.
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// 一個無法解析的bean型別,可能來自一個延遲載入的Bean - 讓我們忽略它.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
// beanType對應的Bean包含@Cotroller或@RequestMapping註解.
if (beanType != null && isHandler(beanType)) {
// 從處理器中查詢處理方法,並註冊到mappingRegistry屬性中.
detectHandlerMethods(beanName);
}
}
}
//在檢測到所有處理程式方法後呼叫.
handlerMethodsInitialized(getHandlerMethods());
}
3) RequestMappingHandlerMapping.isHandler(...)
方法。
① 給定Bean
是否由@Controller
或@RequestMapping
註釋。
/**
* 判斷給定Bean是否包含@Controller或@RequestMapping.
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
4) AbstractHandlerMethodMapping.detectHandlerMethods()
方法。
① 獲取處理器handler
的Bean
型別。
② 遍歷處理器handler
的方法,收集符合條件的方法到methods
.
③ 遍歷符合條件的方法,將其轉換並註冊到AbstractHandlerMethodMapping
的mappingRegistry
屬性中。
/**
* 從處理器中查詢處理方法,並註冊到mappingRegistry屬性中.
* @param handler 處理程式或處理程式例項的Bean名稱.
*/
protected void detectHandlerMethods(final Object handler) {
// 獲取Bean型別.
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
// 遍歷Bean類的方法,收集符合條件的methods.
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 獲取對映的方法.
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
// 進行符合條件methods的註冊.
methods.forEach((method, mapping) -> {
// 在目標型別上選擇一個呼叫方法.
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 註冊HandlerMethod.
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
5) RequestMappingHandlerMapping.getMappingForMethod(...)
方法。
① 通過createRequestMappingInfo
建立方法級別對映。
② 通過createRequestMappingInfo
建立類級別對映。
③ 合併方法級別和類級別createRequestMappingInfo
對映。
/**
* 使用方法級別和型別級別RequestMapping註釋建立RequestMappingInfo.
* @return 建立的RequestMappingInfo,或者null(如果該方法沒有@RequestMapping註釋).
*/
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 解析方法級別對映.
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 解析類級別對映.
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 合併方法和類級別對映.
info = typeInfo.combine(info);
}
}
return info;
}
6) RequestMappingHandlerMapping.createRequestMappingInfo(...)
方法。
① RequestMappingHandlerMapping.createRequestMappingInfo(...)
存在過載方法。
② 在處理器方法級別或類級別查詢@RequestMapping
註解。
③ 通過getCustomTypeCondition(...)
載入自定義類級別匹配條件,通過getCustomMethodCondition(...)
載入自定義方法級別匹配條件。
④ 呼叫createRequestMappingInfo(...)
過載方法建立對映。
⑤ 綜合@RequestMapping
註解指定條件和③
中自定義條件建立RequestMappingInfo
例項。
/**
* 委託給createRequestMappingInfo(RequestMapping, RequestCondition),
* 根據提供的annotatedElement是類或方法來提供適當的自定義RequestCondition.
*/
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 在方法級別或類級別查詢@RequestMapping註解.
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);
}
/**
* 從提供的@RequestMapping註解建立RequestMappingInfo,
* 它可以是直接宣告的註解、元註解,也可以是在註解層次結構中合併註解屬性的合成結果.
*/
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.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();
}
7) AbstractHandlerMethodMapping.registerHandlerMethod(...)
方法。
主要負責將給定handler
、method
、mapping
註冊到mappingRegistry
屬性中。
/**
* 註冊處理程式方法及其唯一對映.
* 在啟動時為每個檢測到的處理程式方法呼叫.
* @param handler 處理程式或處理程式例項的Bean名稱.
* @param method 註冊的方法.
* @param mapping 與處理程式方法關聯的對映條件.
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
8) AbstractHandlerMethodMapping.handlerMethodsInitialized()
方法。
檢測所有處理程式方法後呼叫。
/**
* 檢測所有處理程式方法後呼叫.
*/
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
}
總結
本文對@RequestMapping
註冊的處理器的對映解析,瞭解這部分內容有助於更深入的應用@RequestMapping
註解。
原始碼解析基於spring-framework-5.0.5.RELEASE
版本原始碼。
若文中存在錯誤和不足,歡迎指正!