【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner(下)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner(下)

ClassPathScanningCandidateComponentProvider#findCandidateComponents

  public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    // 上面说过了CandidateComponentsIndex是Spring5提供的优化扫描的功能
    // 显然这里编译器我们没有写META-INF/spring.components索引文件,所以此处不会执行Spring5 的扫描方式,所以我暂时不看了(超大型项目才会使用Spring5的方式)
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    } else {
      // Spring 5之前的方式(绝大多数情况下,都是此方式)
      return scanCandidateComponents(basePackage);
    }
  }


scanCandidateComponents:根据basePackage扫描候选的组件们(非常重要)


  private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
      // 1.根据指定包名 生成包搜索路径
      //通过观察resolveBasePackage()方法的实现, 我们可以在设置basePackage时, 使用形如${}的占位符, Spring会在这里进行替换 只要在Enviroment里面就行
      // 本次值为:classpath*:com/fsx/config/**/*.class
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
          resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      //2. 资源加载器 加载搜索路径下的 所有class 转换为 Resource[]
      // 拿着上面的路径,就可以getResources获取出所有的.class类,这个很强大~~~
      // 真正干事的为:PathMatchingResourcePatternResolver#getResources方法
      // 此处能扫描到两个类AppConfig(普通类,没任何注解标注)和RootConfig。所以接下里就是要解析类上的注解,以及过滤掉不是候选的类(比如AppConfig)
      // 注意:这里会拿到类路径下(不包含jar包内的)的所有的.class文件 可能有上百个,然后后面再交给后面进行筛选~~~~~~~~~~~~~~~~(这个方法,其实我们也可以使用)
      // 当然和getResourcePatternResolver和这个模版有关
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      // 记录日志(下面我把打印日志地方都删除)
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      // 接下来的这个for循环:就是把一个个的resource组装成
      for (Resource resource : resources) {
        //文件必须可读 否则直接返回空了
        if (resource.isReadable()) {
          try {
            //读取类的 注解信息 和 类信息 ,两大信息储存到  MetadataReader
            MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
            // 根据TypeFilter过滤排除组件。因为AppConfig没有标准@Component或者子注解,所以肯定不属于候选组件  返回false
            // 注意:这里一般(默认处理的情况下)标注了默认注解的才会true,什么叫默认注解呢?就是@Component或者派生注解。还有javax....的,这里省略啦
            if (isCandidateComponent(metadataReader)) {
              //把符合条件的 类转换成 BeanDefinition
              ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
              sbd.setResource(resource);
              sbd.setSource(resource);
              // 再次判断 如果是实体类 返回true,如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true (注意 这个和上面那个是重载的方法) 
              // 这和上面是个重载方法  个人觉得旨在处理循环引用以及@Lookup上
              if (isCandidateComponent(sbd)) {
                candidates.add(sbd);
              }
            }
          } 
        }
      }
    }
    return candidates;
  }
// 备注:此时ComponentScan这个注解还并没有解析


ClassPathBeanDefinitionScanner#postProcessBeanDefinition


  protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    // 位Bean定义 执行些默认的信息
    // BeanDefinitionDefaults是个标准的javaBean,有一些默认值
    beanDefinition.applyDefaults(this.beanDefinitionDefaults);
    // 自动依赖注入 匹配路径(此处为null,不进来)
    if (this.autowireCandidatePatterns != null) {
      beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
    }
  }
AbstractBeanDefinition#applyDefaults:
  public void applyDefaults(BeanDefinitionDefaults defaults) {
    setLazyInit(defaults.isLazyInit());
    setAutowireMode(defaults.getAutowireMode());
    setDependencyCheck(defaults.getDependencyCheck());
    setInitMethodName(defaults.getInitMethodName());
    setEnforceInitMethod(false);
    setDestroyMethodName(defaults.getDestroyMethodName());
    setEnforceDestroyMethod(false);
  }


就这样,扫描的Bean就全部注册完成了(若你的Config比较多的情况下,用扫描的方式更加简单些~)


总结


观看Spring源码这么久,最大的感受就是:组件繁多,但管理有序。它完美的实现了单一职责的设计原则,谁的事情谁去干,这点体现得非常好。只有遵循了这个原则,才能做到无状态和组件化,达到随意装配的效果~

另外,还有一种设计思想就是预加载。什么组件在什么时候加载,什么时候可以预加载进去,都是设计的精妙之处


理解ClassPathBeanDefinitionScanner的工作原理,可以帮助理解Spring IOC 容器的初始化过程。

同时对理解MyBatis 的 Mapper 扫描 也是有很大的帮助。

因为 MyBatis 的MapperScannerConfigurer的底层实现也是一个ClassPathBeanDefinitionScanner的子类。就像我们自定义扫描器那样,自定定义了 过滤器的过滤规则。


这两名大将还提供了比如扫描包的核心方法,这个在讲解具体IOC容器流程的时候再具体分析~~~

相关文章
|
2月前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
571 1
|
6天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
33 6
|
22天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
32 1
|
1月前
|
监控 IDE Java
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
54 8
|
1月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
51 0
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
运维 JavaScript Linux
容器内的Nodejs应用如何获取宿主机的基础信息-系统、内存、cpu、启动时间,以及一个df -h的坑
本文介绍了如何在Docker容器内的Node.js应用中获取宿主机的基础信息,包括系统信息、内存使用情况、磁盘空间和启动时间等。核心思路是将宿主机的根目录挂载到容器,但需注意权限和安全问题。文章还提到了使用`df -P`替代`df -h`以获得一致性输出,避免解析错误。
|
3月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
155 3
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
80 0