Spring MVC框架中使用HandlerMapping来进行请求到处理器的映射处理。
那么就会有一个问题:那么框架到底是如何从Url获取到一个处理器的呢?
而回到这个问题,一般要思考两个问题:
1、框架如何初始化HandlerMapping接口.
2、 框架如何使用HandlerMapping接口进行请求到处理器的映射处理。
那么就会有一个问题:那么框架到底是如何从Url获取到一个处理器的呢?
而回到这个问题,一般要思考两个问题:
1、框架如何初始化HandlerMapping接口.
2、 框架如何使用HandlerMapping接口进行请求到处理器的映射处理。
现在,先来看看第一个问题。我们以@RequestMapping为例,是如何处理的。
其中SpringMVC容器提供了
RequestMappingHandlerMapping实现类来处理这个注解.
debug源码,发现是在初始化容器的时候会注册RequestMappingHandlerMappingBean:
步骤如下:
1:在初始化容器的时候,有一个步骤:
AbstractAutowireCapableBeanFactory.initializeBeaninvoke
在这方法中有一个invokeInitMethods, 在这个代码中会执行bean.afterPropertiesSet()方法
2:afterPropertiesSet()方法
该方法是
InitializingBean的一个接口,所有实现了这个接口,都要实现 afterPropertiesSet()方法。
在初始化bean的时候调用。
AbstractHandlerMethodMapping.afterPropertiesSet()
AbstractHandlerMethodMapping实现了这个接口,也就是实现了afterPropertiesSet()方法,
在初始化bean的时候会调用 AbstractHandlerMethodMapping.afterPropertiesSet()
3: initHandlerMethods()
在afterPropertiesSet()方法中调用了initHandlerMethods()方法.
这个方法用来从所有的bean中检测出符合规则的类和方法,
然后生成一个调用
RequestMappingHandlerMapping.getMappingForMethod() 产生一个 RequestMappingInfo对象,
在
registerHandlerMethod注册过程中:根据
RequestMappingInfo对象生成一个HandlerMethod对象。
4: 注册到AbstractHandlerMethodMapping的handlerMethods和urlMap中.
这个是变量的声明
Map<T, HandlerMethod> handlerMethods;
MultiValueMap<String, T> urlMap;
会把相关的对象保存到上面的Map中.
涉及到的相关类:
InitializingBean、AbstractHandlerMethodMapping、RequestMappingHandlerMapping、R equestMappingInfo、HandlerMethod.
代码分析。。。:
一:
AbstractHandlerMethodMapping.initHandlerMethods()
这个方法用来扫描容器的bean,监测出所以的映射方法(Handler Methods),并且注册到映射处理器中(HandlerMapping)。
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler(Class)
* @see #getMappingForMethod(Method, Class)
* @see #handlerMethodsInitialized(Map)
*/
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
这个方法步骤如下:
1 :从容器中获得所有bean的名字
2:遍历容器中的bean,判断Bean
是否属于Handler,如果是则检测出所以的HandlerMethod方法并且注册到容器中.
3:handlerMethodsInitialized这个方法里的方法体为空,没有做任何的处理
注意:在上面的代码中,isHandler()方法是个抽象方法,由子类来提供具体的实现。
比如
RequestMappingHandlerMapping提供了实现,代码如下:
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
在这里来判断当前的bean是否有Controller或者RequestMapping注解。
再来看下detectHandlerMethods的代码
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMatchingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}
大概步骤如下:
1 : 通过HandlerMethodSelector.selectMethods()方法来从当前的处理器中匹配出具有映射处理的方法。
2:取出映射的方法后,则调用
registerHandlerMethod()注册到处理器中。
注意:
在上面代码中,getMappingForMethod(method, userType)是个抽象方法,具体由子类来实现。
比如
RequestMappingHandlerMapping中提供了如下的实现:
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
具体步骤:
1:从当前类中取出
RequestMapping 注解。
2:如果注解不为空的话,则进行一系列处理,生成一个RequestMappingInfo对象。
再来看看registerHandlerMethod这个方法的实现,看看是是把HandlerMethod注册到哪里的?
代码:
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}
从上面代码可以看出是 注册到下面两个变量中。
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
一看定义就知道,
urlMap 的key是个String类型,表示的是请求url,而T应该是RequestMappingInfo.
handlerMethods 的key是RequestMappingInfo。
也就是请求的时候,会先根据请求url从urlMap中查找对应的
RequestMappingInfoBean,
然后再从handlerMethods 方法中找到对应的HandlerMethod方法,这个HandlerMethod就是实际的业务处理逻辑。
总结:
这篇文章分析SpringMVC容器在初始化的时候,是如何初始化映射处理器的。
1:容器初始化的时候是在哪个入口 进入 初始化映射处理器的。
2: 初始化映射处理器的流程是如何检测出所有符合规则的 Handler类和HandlerMethod方法,并且是如何注册到容器中。
在这个整个流程中涉及到了InitializingBean、RequestMappingHandlerMapping、RequestMappingHandlerMapping、RequestMappingInfo、HandlerMethod.等类.