【小家Spring】Spring向容器注册Bean的高级应用:@Import、DeferredImportSelector、ImportBeanDefinitionRegistrar的使用(下)

简介: 【小家Spring】Spring向容器注册Bean的高级应用:@Import、DeferredImportSelector、ImportBeanDefinitionRegistrar的使用(下)

ImportBeanDefinitionRegistrar


该接口功能非常强大,能够实现快速的、批量的、扫描式的注册。比如我们熟悉的ServletComponentScanRegistrar就是去解析注解@ServletComponentScan实现批量注册Bean定义

MapperScannerRegistrar就是MyBatis用来解析@MapperScan注解,来扫描的 等等还有很多类似的设计方式


先看一个最简单的效果吧:


@Configuration
@Import({Parent.class, AntPathMatcher.class, MyImportSelector.class, MyDeferredImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class RootConfig {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    // 同样的,这种注入都是不好使的(相同的,那些感知接口是可以实现的,从而注入对应组件)
    @Autowired
    private HelloService helloService;
    /**
     * 实现了该接口让我们的这个类成为了拥有注册bean的能力
     * 也可以让我们实现动态注入(根据条件、逻辑进行动态注入)
     *
     * @param importingClassMetadata 注解信息和本类信息
     * @param registry               注册器,我们可以向容器里面注册[Bean定义信息]
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("this MyImportBeanDefinitionRegistrar");
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(GenericBean.class);
        registry.registerBeanDefinition("genericBean", beanDefinition);
    }
}


输出:(genericBean成功被注入容器了)


rootConfig
helloServiceImpl
com.fsx.bean.Parent
org.springframework.util.AntPathMatcher
com.fsx.bean.Child
genericBean


从另外一个日志的打印来看:MyImportBeanDefinitionRegistrar是更加滞后执行的。那么下面我们就要看看它到底啥时候执行的呢?


this MyImportSelector...
this MyDeferredImportSelector...
this MyImportBeanDefinitionRegistrar


有了上面的源码分析我们知道了,实现了ImportBeanDefinitionRegistrar接口的,最后是这么一句话给缓存下来了(还木有执行):


// registrar是已经被实例化了的当前类
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());


下面重点看看,该接口到底什么时候执行的呢?根据断点,跟踪到它的加载时机是这句代码里:this.reader.loadBeanDefinitions(configClasses);


  public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    // 依次解析每一个配置类,这里面注意了configClass 比如我们的RootConfig这个对象里有个字段
    //importBeanDefinitionRegistrars是记录着了我们前面add进去的ImportBeanDefinitionRegistrar的,因此它会在此处开始执行了
    for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
  }
  // 这个方法很重要:它处理了多种方式(@Bean、实现接口类注册等等)完成向容器里注册Bean定义信息
  private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    // 如果这咯Config不需要被解析,做一些清理、移除的操作~~~~
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
      String beanName = configClass.getBeanName();
      if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
        this.registry.removeBeanDefinition(beanName);
      }
      this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
      return;
    }
///稍微注意一下Spring处理这些Bean定义的顺序,在某些判断逻辑中或许能用到///
    // 如果是被单纯@Import进来的,这个值是true的,默认值是false哦
    if (configClass.isImported()) {
      // 这个处理源码这里不分析了,比较简单。支持@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description等等一些通用的基本属性
      registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //处理方法上的@Bean 解析@Bean上面各种属性值。也处理上面提到的那些通用注解@Lazy等等吧
    //这里面只说一个内部比较重要的方法isOverriddenByExistingDefinition(beanMethod, beanName)  
    // 该方法目的主要是去重。其实如果是@Configuration里面Bean重名了,IDEA类似工具发现,但是无法判断xml是否也存在(注意,发现归发现,但并不是编译报错哦~~~)
    // 它的处理策略为:若来自同一个@Configuration配置类,那就保留之前的。若来自不同配置类,那就覆盖
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //处理@ImportResource,里面解析xml就是上面说到的解析xml的XmlBeanDefinitionReader
    //所以,咱们@Configuration和xml是可以并行使用的
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 最后,这里就是咱们今天的主菜了:解析咱们的ImportBeanDefinitionRegistrars
    // configClass.getImportBeanDefinitionRegistrars():就是我们上面异步add进去的那些注册器们
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
  }


loadBeanDefinitionsFromRegistrars完整解析:


// 没什么多余的代码  所有的注册逻辑(哪些Bean需要注册,哪些不需要之类的,全部交给子类去实现)
// 用处:上面有提到,比如@MapperScan这种批量扫描的===
  private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
        registrar.registerBeanDefinitions(metadata, this.registry));
  }

应用场景分析

根据各个接口的特点,有各自的应用场景。因为直接@Import普通类的场景相对较少,这里主要说说实现接口的方式的场景:


ImportSelector接口应用场景

AdviceModeImportSelector:它是个抽象类。(实现类有出名的AsyncConfigurationSelector、CachingConfigurationSelector等,因为都是基于代理来做的,所以都继承了此抽象)。

它拿到泛型类型(比如@EnableAsync或者@EnableCaching),然后解析注解为AnnotationAttributes,最后由子类去实现select逻辑(具体要向容器注入的Class全类名),比如注入ProxyAsyncConfiguration,或者其它的。。。

总之,向这种还不能决定去注入哪个处理器(如果你能决定,那就直接@Import那个类好了,没必要实现接口了嘛),然后可以实现此接口,写出一些判断逻辑,不同的配置情况注入不同的处理类。


DeferredImportSelector接口应用场景

它和上面只是执行的时机不同。在Spring内部没有应用,但是在Spring Boot中却有大量的应用,比如:

AutoConfigurationImportSelector、EnableCircuitBreakerImportSelector等等(在SpirngBoot章节中还会重点分析)


实现这个接口的基本思想是:默认处理(以用户配置的为准,若用户没管,那就执行我的默认配置呗)。执行生效的一个先后顺序的简单控制


ImportBeanDefinitionRegistrar接口应用场景


它的应用场景特别的有用,因此也是最常使用的。因为她直接可以向工厂里注册Bean的定义信息(当然也可以拿出来Bean定义信息,做出对应的修改)~


下面两个实现,都喝@EnableAspectJAutoProxy注解相关:


  • AspectJAutoProxyRegistrar:它能解析注解的时候,从BeanFactory拿出指定的Bean,设置一些参数值等等
  • AutoProxyRegistrar:自动代理的注册器。它和上面的区别在于它和代理的类型无关(它可以指定mode类型),而上面是表示就是用AspectJ来做切面代理。


实现它的基本思想是:当自己需要操作BeanFactory里面的Bean的时候,那就必须只有它才能做到了。而且它还有个方便的地方,那就是做包扫描的时候,比如@MapperScan类似这种的时候,用它处理更为方便(因为扫描到了直接注册即可)


备注:@Mapper的扫描依赖于ClassPathMapperScanner,它由mybatis-spring提供。它继承与ClassPathBeanDefinitionScanner由Spring底层提供


总结


注意,完成了所有的这些注解、配置文件的解析,Bean们都还只是被加载了,还没有加入到Bean的定义信息里面,更别谈实例化了。要加入到Bean的定义信息里面存储好,还得这一步:


//configClasses就是parser.parse(candidates);上面解析完成了的配置类
//根据这些已经解析好了的配置类,由这个ConfigurationClassBeanDefinitionReader去加载Bean定义信息
this.reader.loadBeanDefinitions(configClasses);


然后,上面已经从源码上分析了Spring处理的一个流程,希望各位小伙伴能更宏观的明晰这个执行的时序图(流程图),最好是能画出来(我这里就不画了哈,若哪位小伙伴有心,欢迎留言贡献一份,不胜感激~)

相关文章
|
11月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1484 3
|
10月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
903 0
|
9月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
789 8
|
11月前
|
Java 应用服务中间件 开发者
Spring Boot 技术详解与应用实践
本文档旨在全面介绍 Spring Boot 这一广泛应用于现代企业级应用开发的框架。内容将涵盖 Spring Boot 的核心概念、核心特性、项目自动生成与结构解析、基础功能实现(如 RESTful API、数据访问)、配置管理以及最终的构建与部署。通过本文档,读者将能够理解 Spring Boot 如何简化 Spring 应用的初始搭建和开发过程,并掌握其基本使用方法。
755 2
|
11月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
11月前
|
安全 算法 Java
在Spring Boot中应用Jasypt以加密配置信息。
通过以上步骤,可以在Spring Boot应用中有效地利用Jasypt对配置信息进行加密,这样即使配置文件被泄露,其中的敏感信息也不会直接暴露给攻击者。这是一种在不牺牲操作复杂度的情况下提升应用安全性的简便方法。
1564 10
|
Java Linux 网络安全
Linux云端服务器上部署Spring Boot应用的教程。
此流程涉及Linux命令行操作、系统服务管理及网络安全知识,需要管理员权限以进行配置和服务管理。务必在一个测试环境中验证所有步骤,确保一切配置正确无误后,再将应用部署到生产环境中。也可以使用如Ansible、Chef等配置管理工具来自动化部署过程,提升效率和可靠性。
1104 13
|
12月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
829 2
|
12月前
|
安全 Java Nacos
0代码改动实现Spring应用数据库帐密自动轮转
Nacos作为国内被广泛使用的配置中心,已经成为应用侧的基础设施产品,近年来安全问题被更多关注,这是中国国内软件行业逐渐迈向成熟的标志,也是必经之路,Nacos提供配置加密存储-运行时轮转的核心安全能力,将在应用安全领域承担更多职责。
|
Java 数据库 开发者
Spring Boot 框架超级详细总结及长尾关键词应用解析
本文深入讲解Spring Boot框架的核心概念、功能特性及实际应用,涵盖自动配置、独立运行、starter依赖等优势。通过Web开发、微服务架构、批处理等适用场景分析,结合在线书店实战案例,演示项目初始化、数据库设计、分层架构实现全流程。同时探讨热部署、多环境配置、缓存机制与事务管理等高级特性,助你高效掌握Spring Boot开发技巧。代码示例详尽,适合从入门到进阶的学习者。
4328 0