讀完第一篇文章 總覺得這一塊哪還有缺失。所以還是繼續把上一篇文章缺失部分補全。我們接着上一遍文章講的SimpleUrlHandlerMapping 接下來講一下他的孿生兄弟 BeanNameUrlHandlerMapping和他倆的堂兄弟RequestMappingHandlerMapping(我們經常使用的一個HandlerMapping)
1,BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping處理器映射器會根據請求的url與springIoc容器中定義的處理器bean的name屬性進行匹配
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
通過代碼我們可以清楚的發現 只有當處理器的名字以/開頭才能將該bean映射成 映射處理器。
我們進一步找到調用這個方法地方它位於抽象父類的detectHandlers()方法中。而detectHandlers方法會被SpringIoc容器刷新結束調用。
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
determineUrlsForHandler 這個方法是一個抽象方法 由實現它的子類去實現 spring慣用的伎倆模板方法 該模式幾乎在spring源碼裏面隨處可見。因此 我們可以自己定義映射規則。
2,RequestMappingHandlerMapping
這個處理映射器 是我們經常使用的一個映射器 主要是將url和方法映射。我們debug去查看一下 它如何將Controller裏面的method映射成 處理器的。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().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);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
上述代碼可以描述爲 從SpringIoc容器中拿出所有的實例 獲取當前實例的類型 如果該實例複合一定的規則 就將進行detectHandlerMethods(beanName);處理。具體什麼規則 主要由我們的子類RequestMappingHandlerMapping實現 。當然我們也可以自定義實現。我們進入子類的isHandler(beanType)方法。
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
如果這個實例上面有Controller註解 或者RequestMapping註解其中的一個 RequestMappingHandlerMapping就認爲是合法的可以進行下一步處理。我們再看父類的處理流程
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
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.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
首先找出這個類的所有方法映射成Map<Method,RequestMappingInfo> 具體實現放在子類實現 有興趣可以看一下子類的getMappingForMethod 這個方法。然後循環向mappingRegistry註冊。