享读SpringMVC源码2-@RequestMapping注解源码(上)

简介: 享读SpringMVC源码2-@RequestMapping注解源码(上)

1. 回顾


上节,说了Handler的4种定义方式,以及Handler与HandlerMapping的关系。

@RequestMapping方式是我们最常用的定义handler的方式。

RequestMappingHandlerMapping 负责把@RequestMapping方式定义handler登记在册。

这一节从源码角度来看看,@RequestMapping注解如何让一个方法变成一个handler。


2. 精华总结


@RequestMapping注解背后的做的工作其实挺多,但是我们可以抓住几个关键点:

  • 处理时机:Bean初始化
  • 处理方式:遍历所有Bean,判断handler类,找到类中handler
  • 处理结果:关系映射缓存


3. 注册Handler


@RequestMapping注解的解析逻辑,是伴随着RequestMappingHandlerMapping 初始化过程完成的

一切从RequestMappingHandlerMapping.afterPropertiesSet方法开始。

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {
public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
        this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
        this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
        this.config.setContentNegotiationManager(getContentNegotiationManager());
        super.afterPropertiesSet();
    }
}

主要做了RequestMappingInfo的Builder助手的配置操作,调用父类的afterPropertiesSet()方法

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
   public void afterPropertiesSet() {
           initHandlerMethods();
       }
       //初始化HandlerMethod
   protected void initHandlerMethods() {
           //获取所有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) {
                       if (logger.isDebugEnabled()) {
                           logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                       }
                   }
                   // 如果是handler, 
                   if (beanType != null && isHandler(beanType)) {
                       //识别出Hadnler中的HandlerMethod
                       detectHandlerMethods(beanName);
                   }
               }
           }
           handlerMethodsInitialized(getHandlerMethods());
       }
}

这里的逻辑还是比较清晰的。遍历BeanFactory仓库中Bean,挨个检查类是否是Handler,如果是Handler就去Handler查找HandlerMethod

也就是说:一个Bean在此会经历两个重点方法,isHandler方法与detectHandlerMethods方法


3.1 isHandler-判

判断当前Bean是否是一个handler,方法实现在RequestMappingHandlerMapping 类中。

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {
protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
}

类上有@Controller注解或者@RequestMapping 此处就看做是一个Handler

//@RequestMapping("/user")
@Controller
public class UserController {
}

虽然@Controller注解的本质也是表示一个@Component

@Component

public @interface Controller {

}

但是,我不用@Component 来表示一个代表Controller类。

其原因就是@Controller 会在RequestMappingHandlerMapping 中按@Controller注解判定为handler。

所以说:@Controller 是表示这是一个跟web有关的Bean


3.2 detectHandlerMethods

判定为Bean是一个handler 之后,下面就是把类中能处理请求的方法找到,登记在册。也就是@RequestMapping注解的方法的解析过程

两个动作:找到,登记

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    protected void detectHandlerMethods(final Object handler) {
            Class<?> handlerType = (handler instanceof String ?
                    getApplicationContext().getType((String) handler) : handler.getClass());
            final Class<?> userType = ClassUtils.getUserClass(handlerType);
            //找:方法搜索器,搜索当前bean继承体系上的所有方法中,符合我们需要的method
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    new MethodIntrospector.MetadataLookup<T>() {
                        @Override
                        public T inspect(Method 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);
            }
            //注册,
            for (Map.Entry<Method, T> entry : methods.entrySet()) {
                Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                T mapping = entry.getValue();
                registerHandlerMethod(handler, invocableMethod, mapping);
            }
}}


3.2.1 寻找

MethodIntrospector 能够搜索到当前类 所有关联的方法(包括,接口,父类,同时还处理参数化方法以及接口和基于类代理的常见场景)中,符合我们要求的方法,返回与之关联的元数据。

MethodIntrospector.MetadataLookup 用于定义条件,匹配当前类所有关联的方法中符合我们要求的方法。

getMappingForMethod就是这个条件。getMappingForMethod是一个抽象方法,在中定义RequestMappingHandlerMapping 中

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {
    //获取RequestMappingInfo
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        //查找method是是否有RequestMappingInfo
        RequestMappingInfo info = createRequestMappingInfo(method);
        //如果方法上有
        if (info != null) {
            //获取类上的RequestMappingInfo
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                //合并方法与类上的注解信息。 这也是为啥。方法的访问路径是类路径+方法路径。
                info = typeInfo.combine(info);
            }
        }
        return info;
    }    
    // 根据方法或者类上的注解信息,创建RequestMappingInfo
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        //获取当前element上的RequestMapping 注解元信息 (包括直接@RequestMapping注解,或者间接RequestMapping 如@GetMapping)
        // 将查询出的多个annotationType类型注解属性合并到查询的第一个注解中
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        //获取自定义的条件。
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        //根据注解信息,与自定义条件。创建一个RequestMappingInfo
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }
    //创建RequestMappingInfo
    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();
        }
}

简单来说:找的过程,就是遍历当前Bean相关的所有方法。解析方法上的@RequestMapping注解与类上的@RequestMapping注解,合并两者信息,创建出一个RequestMappingInfo 实例来表示当前方法的@RequestMapping注解元信息

合并这点其实就是解释了 为啥请求处理方法的Url路径= 类路径+方法路径

最终找的结果:得到一个 以method为K ,以RequestMappingInfo 为Value的Map methods。也就是解析到了方法与方法对应RequestMappingInfo 关系映射。

相关文章
|
5月前
|
JSON 前端开发 Java
Spring MVC入门必读:注解、参数传递、返回值和页面跳转(下)
Spring MVC入门必读:注解、参数传递、返回值和页面跳转(下)
61 0
|
7月前
|
Java
【SpringMVC】注解、参数传递、返回值和页面跳转的关键步骤(三)
【SpringMVC】注解、参数传递、返回值和页面跳转的关键步骤(三)
41 0
|
2月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
42 0
|
5月前
|
缓存 前端开发 Java
【SpringMVC】JSR 303与拦截器注解使用
【SpringMVC】JSR 303与拦截器注解使用
42 0
|
5月前
|
前端开发 Java Spring
Spring MVC拦截器+注解方式实现防止表单重复提交
Spring MVC拦截器+注解方式实现防止表单重复提交
|
2月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
36 0
|
3月前
|
前端开发 Java 应用服务中间件
SpringMVC之@RequestMapping注解
SpringMVC之@RequestMapping注解
|
4月前
|
设计模式 前端开发 Java
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
|
4月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
64 0
|
4月前
|
Java
springmvc之自定义注解-->自定义注解简介,基本案例和aop自定义注解
springmvc之自定义注解-->自定义注解简介,基本案例和aop自定义注解
32 0