⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://blog.csdn.net/u012410733/article/details/51912375 「carl.zhao」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

要想了解决Spring MVC是怎么把@RequestMapping注解了的方法以及类解析的,首先我们需要关注AbstractHandlerMethodMapping#afterPropertiesSet这个方法,而这个方法是Spring的init方法,是Spring在进行初始化bean之前在DI注入之后调用到的方法。所以当Spring容器初始化之后,@RequestMapping就会被解析成Spring容器的bean管理。通过这个方法的说明也能验证之前所说的。我们首先来关注一下@RequestMapping的时序图: 这里写图片描述

下面就入口方法。

/**
* Detects handler methods at initialization.
*/
public void afterPropertiesSet() {
initHandlerMethods();
}

1、初始化处理client页面request的方法

下面我们就要重点关注AbstractHandlerMethodMapping#initHandlerMethods这个方法了。

/**
* 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());
}

// 读取Spring容器中的所有beanNames
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));

// 遍历这些bean,检测出处理方法
for (String beanName : beanNames) {
// 判断这个bean是不是处理类(根据这个类是否使用了@Controller或@RequestMapping)
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
// 处理方法后期处理,Spring空实现,方便以后扩展
handlerMethodsInitialized(getHandlerMethods());
}

那么我们的关注点就需要放在detectHandlerMethods这个方法上了。

2、detectHandlerMethods

下面我们再来看一下AbstractHandlerMethodMapping#detectHandlerMethods这个方法。

protected void detectHandlerMethods(final Object handler) {
// 从Spring容器中获取这个类
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);

// 从处理类(@Controller)中获取处理方法(@RequestMapping)
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));
}
}

3、getMappingForMethod

Spring MVC通过RequestMappingHandlerMapping#getMappingForMethod(Method method, Class

/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
// 这个方法是否使用@RequestMapping信息
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
// 空实现,方便以后扩展
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
// 创建RequestMappingInfo比较简单,如果感兴趣可以自己去看看
info = createRequestMappingInfo(methodAnnotation, methodCondition);
// 查询这个Controller上面使用@RequestMapping信息
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
// 空实现,方便以后扩展
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
// 把Class上面的@RequestMapping信息与方法上的@RequestMapping信息合并起来
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}

return info;
}

返回info主要是包含@RequestMapping的标签信息,我们可以看看info中的信息: 这里写图片描述

4、registerHandlerMethod

然后我们就关注一下。第二步中的detectHandlerMethods方法中的registerHandlerMethod方法。

/**
* 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);
// 存在URL与RequestMappingInfo的映射
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}

现在我们来看看这个解析之后这个类之中属性。 这里写图片描述 我们主要把关注点停留到urlMap与handlerMethods上。 1、urlMap 这里写图片描述 我们可以看到urlMap属性中保存的是请求路径与RequestMappingInfo的映射信息。 2、handerMethods 这里写图片描述 我们可以看到handerMethods属性中保存的是RequestMappingInfo与HanderMethod的映射。HanderMethod主要保存这个类与处理请求具体的方法。

当页面有url请求过来.首先通过urlMap查询RequestMappingInfo的信息,然后再通过handerMethods查询到HanderMethod的信息。这样url请求是不是与Controller中的方法结合了起来。具体是怎么调用的,可以看看我写的Blog – Spring MVC DispatcherServlet

666. 彩蛋

如果你对 Java 感兴趣,欢迎加入我的知识星球一起交流。

知识星球

文章目录
  1. 1. 1、初始化处理client页面request的方法
  2. 2. 2、detectHandlerMethods
  3. 3. 3、getMappingForMethod
  4. 4. 4、registerHandlerMethod
  5. 5. 666. 彩蛋