04在spring配置文件中声名HandlerMapping并注册Handler

简介: Spring中bean创建之后执行自定义初始化获取当前所有的bean并过滤得到是处理器的bean为业务Handler创建映射关系

内容概览


  • Spring中bean创建之后执行自定义初始化
  • 获取当前所有的bean并过滤得到是处理器的bean
  • 为业务Handler创建映射关系


spring-beans的InitializingBean


InitializingBean这个接口是spring-beans模块内的接口。该接口定义中就一个方法:


void afterPropertiesSet() throws Exception;


该方法在BeanFactory创建bean之后会被调用,因此可以通过实现该接口做自定义的初始化,或者校验必要的参数是否被设置。


创建Http请求和处理器Handler的映射关系的切入口


Http请求和处理器Handler的映射关系由处理器映射器HandlerMapping来维护,RequestMappingHandlerMapping是处理器映射器的一种实现,该类做了如下两件事情


  1. 创建了Http请求和处理器Handler的映射
  2. 当有Http请求时,可以通过Http请求获取到处理该请求的处理器


由于本篇只描述映射关系的创建,所以下面类关系图中没有体现RequestMappingHandlerMapping对HandlerMapping的实现:



  1. RequestMappingHandlerMapping继承了AbstractHandlerMethodMapping,而AbstractHandlerMethodMapping就实现了InitializingBean接口
  2. 所以当RequestMappingHandlerMapping这个Bean在被创建之后实现了InitializingBean接口中的方法afterPropertiesSet就会被调用


RequestMappingHandlerMapping实例化之后的创建映射关系的执行流程


映射关系创建的时序图



主要代码


AbstractHandlerMethodMapping实现的afterPropertiesSet


   @Override

   public void afterPropertiesSet() throws Exception {

       initHandlerMethods();

   }


AbstractHandlerMethodMapping实现了InitializingBean的afterPropertiesSet方法,当被回调之后调用自己的初始化方法。


AbstractHandlerMethodMapping的initHandlerMethods


   protected void initHandlerMethods() {

       for (String beanName : getCandidateBeanNames()) {

           if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {

               processCandidateBean(beanName);

           }

       }

   }


获取所有的候选Bean,对候选的Bean进行循环遍历并简单的排除


AbstractHandlerMethodMapping的processCandidateBean


   protected void processCandidateBean(String beanName) {

       Class<?> beanType = null;


       try {

           beanType = obtainApplicationContext().getType(beanName);

       } catch (Throwable ex) {

           if (logger.isTraceEnabled()) {

               logger.trace("Could not resolve type for bean '" + beanName + "'", ex);

           }

       }

       if (beanType != null && isHandler(beanType)) {

           detectHandlerMethods(beanName);

       }

   }


对排除之后的候选Bean进行处理,这里会调用isHandler判断当前的Bean是否是处理器类型的Bean。如果是则会处理这个类中的方法。


RequestMappingHandlerMapping的isHandler


   @Override

   protected boolean isHandler(Class<?> beanType) {

       return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||

               AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);

   }


这个方法的逻辑还是很明了的,判断当前Bean的类型是否有Controller注解或者RequestMapping注解。这里就知道为什么业务Handler使用这两个注解标识之后就可以用来处理Http请求了。


AbstractHandlerMethodMapping的detectHandlerMethods


   protected void detectHandlerMethods(Object handler) {

       Class<?> handlerType = (handler instanceof String) ? obtainApplicationContext().getType(((String) handler)) : handler.getClass();


       if (null != handlerType) {

           Class<?> userType = ClassUtils.getUserClass(handlerType);

           Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>)

                   method -> getMappingForMethod(method, handlerType));


           methods.forEach((method, mapping) -> {

               Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);

               registerHandlerMethod(handler, invocableMethod, mapping);

           });

       }

   }


对于是处理器的类,则获取该类的所有映射方法getMappingForMethod。得到所有方法之后进行遍历循环进行注册


获取RequestMappingInfo


 @Override

   @Nullable

   protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {

       RequestMappingInfo info = createRequestMappingInfo(method);

       if (null != info) {

           RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);

           if (null != typeInfo) {

               info = typeInfo.combine(info);

           }

       }

       return info;

   }


   @Nullable

   private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {

       RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);

       RequestCondition<?> condition = element instanceof Class ?

               getCustomTypeCondition((Class<?>) element) :

               getCustomMethodCondition((Method) element);

       return null != requestMapping ? createRequestMappingInfo(requestMapping, condition) : null;

   }


首先根据方法获取RequestMapping信息封装成RequestMappingInfo,然后再获取当前类上的RequestMapping信息,因为RequestMapping既可以作用在方法上又可以作用在类上,当两者上面都有时,需要将两者合并起来。


其他


注册的流程虽然很简单,但是注册的过程需要注意的地方确实很多。特别是RequestMappingInfo的构造,这里先认识下它,在后面根据Http请求查找映射的处理器时还会用到它。

相关文章
|
2月前
|
Java Spring
Spring boot 运行服务jar外配置配置文件方式总结
Spring boot 运行服务jar外配置配置文件方式总结
397 0
|
1月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
19天前
|
Java Spring 传感器
AI 浪潮席卷,Spring 框架配置文件管理与环境感知,为软件稳定护航,你还在等什么?
【8月更文挑战第31天】在软件开发中,配置文件管理至关重要。Spring框架提供强大支持,便于应对不同环境需求,如电商项目的开发、测试与生产环境。它支持多种格式的配置文件(如properties和YAML),并能根据环境加载不同配置,如数据库连接信息。通过`@Profile`注解可指定特定环境下的配置生效,同时支持通过命令行参数或环境变量覆盖配置值,确保应用稳定性和可靠性。
34 0
|
2月前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
61 3
|
1月前
|
XML Java 数据库连接
深入解析 Spring 配置文件:从基础到高级
【8月更文挑战第3天】Spring配置文件是构建与管理Spring应用的核心,它涵盖了从基础到高级的各种配置技巧。基础配置采用`.xml`格式定义Bean及其依赖;中级配置包括设置Bean作用域及引入属性文件;高级配置则涉及AOP、事务管理和与其他框架的整合。熟练掌握这些配置能帮助开发者构建出更为灵活且易维护的应用系统。
|
2月前
|
存储 安全 Java
Spring Boot中的配置文件加密
Spring Boot中的配置文件加密
|
2月前
|
负载均衡 安全 Java
Spring Cloud中的服务发现与注册
Spring Cloud中的服务发现与注册
|
2月前
|
负载均衡 Java 微服务
深入理解Spring Cloud中的服务发现与注册
深入理解Spring Cloud中的服务发现与注册
|
2月前
|
Java Spring
解析Spring Boot中的配置文件与外部化配置
解析Spring Boot中的配置文件与外部化配置
|
2月前
|
Java API 数据中心
Spring Cloud中的服务注册与发现实现方法
Spring Cloud中的服务注册与发现实现方法