承接前文SpringMVC源碼情操陶冶-AbstractHandlerMapping,本文將介紹如何注冊HandlerMethod對象作為handler
類結構瞧一瞧
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean{}
此為抽象方法,並實現了initializingBean
接口,其實主要的注冊操作則是通過afterPropertiesSet()接口方法來調用的
AbstractHandlerMethodMapping#afterPropertiesSet()-初始化HandlerMethod對象
源碼奉上
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
轉而看initHandlerMethods()
,觀察是如何實現加載HandlerMethod
protected void initHandlerMethods() {
//獲取springmvc上下文的所有注冊的bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
//isHandler()是抽象方法,主要供子類需要掃描什么類型的bean
if (beanType != null && isHandler(beanType)) {
//解析其中的HandlerMethod進行注冊
detectHandlerMethods(beanName);
}
}
}
//抽象方法,目前尚無實現
handlerMethodsInitialized(getHandlerMethods());
}
接下來稍微分析springmvc是如何解析bean並獲取其中的HandlerMethod
AbstractHandlerMethodMapping#detectHandlerMethods()解析
源碼奉上
protected void detectHandlerMethods(final Object handler) {
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 {
//模板方法獲取handlerMethod的mapping屬性
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});
//對查找到的HandlerMethod進行注冊,保存至內部類mappingRegistry對象中
for (Map.Entry<Method, T> entry : methods.entrySet()) {
//作下判斷,method是否從屬於userType
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
針對唯一的實現類RequestMappingHandlerMapping
,上述的mapping
屬性指代的是RequestMappingInfo
對象,內部包含@RequestMapping
注解的內部屬性,比如method
、params
、consumes
、produces
、value
以及對應的屬性判斷類
RequestMappingHandlerMapping-唯一實現類
作為HandlerMethod
對象的配置者,我們主要觀察其復寫的幾個方法
RequestMappingHandlerMapping#afterPropertiesSet()
代碼奉上
@Override
public void afterPropertiesSet() {
//config為RequestMappingInfo的創建對象
this.config = new RequestMappingInfo.BuilderConfiguration();
//設置urlPathHelper默認為UrlPathHelper.class
this.config.setUrlPathHelper(getUrlPathHelper());
//默認為AntPathMatcher,路徑匹配校驗器
this.config.setPathMatcher(getPathMatcher());
//是否支持后綴補充,默認為true
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
//是否添加"/"后綴,默認為true
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
//是否采用mediaType匹配模式,比如.json/.xml模式的匹配,默認為false
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
//mediaType處理類
this.config.setContentNegotiationManager(getContentNegotiationManager());
//調用父類進行HandlerMethod的注冊工作
super.afterPropertiesSet();
}
此處如何設置可通過查看博文>>>SpringMVC源碼情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
RequestMappingHandlerMapping#isHandler()-判斷獲取何種類型的Handler
獲取@Controller
/@RequestMapping
注解下的handler
@Override
protected boolean isHandler(Class<?> beanType) {
//優先匹配@Controller
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
RequestMappingHandlerMapping#getMappingForMethod()-獲取HandlerMethod對應的mapping屬性
此處指的是RequestMappingInfo
,主要包含了路徑匹配策略、@RequestMapping
屬性匹配策略,簡單源碼奉上
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//對method以及class類都進行創建RequestMappingInfo
//因為@RequestMapping可以在方法上/類上應用注解
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
}
return info;
}
由代碼可知,其將拼裝Class上的@RequestMapping
和Method上的@RequestMapping
組裝成RequestMappingInfo
對象,其內部的屬性讀者有興趣可自行去分析。
此處對createRequestMappingInfo()方法作下補充
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
return RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.options(this.config)
.build();
}
小結
介紹AbstractHandlerMethodMapping如何創建HandlerMethod,調用者為
RequestMappingHandlerMapping
,其可通過mvc:annotation-driven
注冊,具體可查看>>>SpringMVC源碼情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器其中其唯一實現類RequestMappingHandlerMapping主要是獲取
@Controller
下的@RequestMapping
注解的方法將其注冊為HandlerMethod對象,其余的相關信息則注冊為RequestMappingInfo
對象供對路徑信息匹配其中如何獲取HandlerMethod查看前言中的鏈接即可