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

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

我们发现:内部定义的class都是带internal的


  • ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor处理器,BeanDefinitionRegistryPostProcessor的处理方法能处理@Configuration等注解。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法内部处理@Configuration,@Import,@ImportResource和类内部的@Bean。


ConfigurationClassPostProcessor类继承了BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor类继承了BeanFactoryPostProcessor。

通过BeanDefinitionRegistryPostProcessor可以创建一个特别后置处理器来将BeanDefinition添加到BeanDefinitionRegistry中。它和BeanPostProcessor不同,BeanPostProcessor只是在Bean初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor可以让我们在BeanDefinition中添加一些自定义操作。在Mybatis与Spring的整合中,就利用到了BeanDefinitionRegistryPostProcessor来对Mapper的BeanDefinition进行了后置的自定义处理。


  • AutowiredAnnotationBeanPostProcessor是用来处理@Autowired注解和@Value注解的
  • RequiredAnnotationBeanPostProcessor这是用来处理@Required注解
  • CommonAnnotationBeanPostProcessor提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持。
  • EventListenerMethodProcessor提供@PersistenceContext的支持。
  • EventListenerMethodProcessor提供@ EventListener 的支持。@ EventListener实在spring4.2之后出现的,可以在一个Bean的方法上使用@EventListener注解来自动注册一个ApplicationListener。


到此AnnotatedBeanDefinitionReader初始化完毕。

总结一下,AnnotatedBeanDefinitionReade读取器用来加载class类型的配置,在它初始化的时候,会预先注册一些BeanPostProcessor和BeanFactoryPostProcessor,这些处理器会在接下来的spring初始化流程中被调用

ClassPathBeanDefinitionScanner的初始化:


跟踪构造函数的重载,最终都到这里:


  public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, @Nullable ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    //useDefaultFilters为true,所以此处一般都会执行
    // 当然我们也可以设置为false,比如@ComponentScan里就可以设置为false,只扫描指定的注解/类等等
    if (useDefaultFilters) {
      registerDefaultFilters();
    }
    // 设置环境
    setEnvironment(environment);
    // 详情如下:  这里resourceLoader传值,还是我们的工厂。否则为null
    setResourceLoader(resourceLoader);
  }


registerDefaultFilters()


  protected void registerDefaultFilters() {
    // 这里需要注意,默认情况下都是添加了@Component这个注解的
    //(相当于@Service @Controller @Respository等都会扫描,因为这些注解都属于@Component)  另外@Configuration也属于哦
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    //下面两个 是兼容JSR-250的@ManagedBean和330的@Named注解
    try {
      this.includeFilters.add(new AnnotationTypeFilter(
          ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    } catch (ClassNotFoundException ex) {
      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
      this.includeFilters.add(new AnnotationTypeFilter(
          ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
      logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    } catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
    }
    // 所以,如果你想Spring连你自定义的注解都扫描,自己实现一个AnnotationTypeFilter就可以啦
  }


ClassPathBeanDefinitionScanner继承于ClassPathScanningCandidateComponentProvider,它内部维护有两个final类型的List:这两个对象在执行本类的scanCandidateComponents()方法时就会起作用。

  //includeFilters中的就是满足过滤规则的
  private final List<TypeFilter> includeFilters = new LinkedList<>();
  //excludeFilters则是不满足过滤规则的
  private final List<TypeFilter> excludeFilters = new LinkedList<>();


ClassPathScanningCandidateComponentProvider#setResourceLoader


ResourcePatternResolver,MetadataReaderFactory和CandidateComponentsIndex设定初始值

  @Override
  public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
    this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    // Spring5以后才有这句,优化了bean扫描
    this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
  }


备注:若要使用Spring5 的这个功能,需要添加如下包。这样子当工程重新编译的时候(编译期),会在自动生成META-INF/spring-components。然后我们在启动用@ComponentScan扫描时,直接读取这个文件即可,极大的提高了Spring启动的速度。而这期间,可以使用Spring5.0最新提供的注解@Indexed来配合使用。

需要注意的是:这种方式也是存在缺陷的,具体缺陷请参考官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-index


    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.0.7.RELEASE</version>
        <optional>true</optional>
    </dependency>


然后在运行后会生成一个 META-INF/spring.components 的文件,之后只要运行工程发现这个文件都会直接使用他。可以通过环境变量或工程根目录的spring.properties中设置spring.index.ignore=ture来禁用这个功能 (这个功能如果没有什么明确的需求,慎重使用,会提高工程的管理成本。)


  • ResourcePatternResolver是一个接口,继承了ResourceLoader,可以用来获取Resource 实例。返回的实例为PathMatchingResourcePatternResolver类型
  • MetadataReaderFactory用于解析资源信息对应的元数据,这里返回的实例为:CachingMetadataReaderFactory,带有缓存的
  • CandidateComponentsIndexLoader.loadIndex () 方法是spring5.0以后加入的新特性,Spring Framework 5 改进了扫描和识别组件的方法,使大型项目的性能得到提升。(具体是通过编译器完成扫描,并且往本地写索引,然后启动的时候再去扫描索引即可的思路)


至此,ClassPathBeanDefinitionScanner初始化完毕,总结一下:

ClassPathBeanDefinitionScanner是一个扫描指定类路径中注解Bean定义的扫描器,在它初始化的时候,会初始化一些需要被扫描的注解,初始化用于加载包下的资源的Loader。


AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化是spring上线文初始化的起点,很多预加载的类会在spring接下来的初始化中发挥重要作用

包扫描的启动方式



    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.fsx.config");
        System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
    }


启动函数为:


  public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
  }
  public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 核心在这个scan方法里,见下面
    this.scanner.scan(basePackages);
  }


image.png


下面就是重点看看doScan()方法:


  protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 装载扫描到的Bean
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
      // 这个方法是最重点,把扫描到的Bean就放进来了(比如此处只有RootConfig一个Bean定义,是个配置类)
      // 这个是重点,会把该包下面所有的Bean都扫描进去。Spring5和一下的处理方式不一样哦~
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
        // 拿到Scope元数据:此处为singleton
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
        candidate.setScope(scopeMetadata.getScopeName());
        // 生成Bean的名称,默认为首字母小写。此处为"rootConfig"
        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
        // 此处为扫描的Bean,为ScannedGenericBeanDefinition,所以肯定为true
        // 因此进来,执行postProcessBeanDefinition(对Bean定义信息做)   如下详解
        // 注意:只是添加些默认的Bean定义信息,并不是执行后置处理器~~~
        if (candidate instanceof AbstractBeanDefinition) {
          postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
        }
        // 显然,此处也是true  也是完善比如Bean上的一些注解信息:比如@Lazy、@Primary、@DependsOn、@Role、@Description   @Role注解用于Bean的分类分组,没有太大的作用
        if (candidate instanceof AnnotatedBeanDefinition) {
          AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
        }
        // 检查这个Bean  比如
        //如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition  所以这步检查第三方Bean的时候有必要检查一下
        if (checkCandidate(beanName, candidate)) {
          BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
          //AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用
          definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
          beanDefinitions.add(definitionHolder);
          // 注意 注意 注意:这里已经吧Bean注册进去工厂了,所有doScan()方法不接收返回值,也是没有任何问题的。。。。
          registerBeanDefinition(definitionHolder, this.registry);
        }
      }
    }
    return beanDefinitions;
  }



相关文章
|
10月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
746 26
|
4月前
|
安全 算法 Java
在Spring Boot中应用Jasypt以加密配置信息。
通过以上步骤,可以在Spring Boot应用中有效地利用Jasypt对配置信息进行加密,这样即使配置文件被泄露,其中的敏感信息也不会直接暴露给攻击者。这是一种在不牺牲操作复杂度的情况下提升应用安全性的简便方法。
1057 10
|
9月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
178 0
|
5月前
|
人工智能 安全 Java
Spring Boot yml 配置敏感信息加密
本文介绍了如何在 Spring Boot 项目中使用 Jasypt 实现配置文件加密,包含添加依赖、配置密钥、生成加密值、在配置中使用加密值及验证步骤,并提供了注意事项,确保敏感信息的安全管理。
1205 1
|
9月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
在微服务架构中,随着业务复杂度增加,项目可能需要调用多个微服务。为避免使用`@Value`注解逐一引入配置的繁琐,可通过定义配置类(如`MicroServiceUrl`)并结合`@ConfigurationProperties`注解实现批量管理。此方法需在配置文件中设置微服务地址(如订单、用户、购物车服务),并通过`@Component`将配置类纳入Spring容器。最后,在Controller中通过`@Resource`注入配置类即可便捷使用,提升代码可维护性。
186 0
|
11月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
437 7
|
12月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
362 12
|
12月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
501 12
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
388 6