读完第一篇文章 总觉得这一块哪还有缺失。所以还是继续把上一篇文章缺失部分补全。我们接着上一遍文章讲的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注册。