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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner(上)

前言

在分析Spring IOC容器启动流程的时候,在加载Bean定义信息BeanDefinition的时候,用到了两个非常关键的类:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。它俩完成对Bean信息的加载。


因此为了更加顺畅的去理解Bean的加载的一个过程,本文主要介绍Spring的这两员大将的一个初始化过程,以及它俩扮演的重要角色

环境准备



因为我们只需要了解Bean的加载,所以只需要启动一个容器就行,并不需要web环境,因此本文用一个相对简单的环境,来进行讲解,如下:

  @ComponentScan(value = "com.fsx", excludeFilters = {
          @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
          //排除掉web容器的配置文件,否则会重复扫描
          @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AppConfig.class}),
  })
  @Configuration
  public class RootConfig {
      @Bean
      public Parent parent() {
          return new Parent();
      }
  }
    public static void main(String[] args) {
        // 备注:此处只能用RootConfig,而不能AppConfig(启动报错),因为它要web容器支持,比如Tomcat
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
    }


环境准备好了,启动之后也能正常打印出Bean的信息。因此接下来,就是要去分析源码,看看这两大工具起的作用


IOC容器加载Bean定义信息分析


AnnotationConfigApplicationContext(spring-context包下)的继承图谱如下:


需要注意的是,我们在Tomcat等web环境下的容器类为:AnnotationConfigWebApplicationContext,它在spring-web包下

image.png


Spring容器里通过BeanDefinition对象来表示Bean,BeanDefinition描述了Bean的配置信息。

而BeanDefinitionRegistry接口提供了向容器注册,删除,获取BeanDefinition对象的方法。


简单来说,BeanDefinitionRegistry可以用来管理BeanDefinition,所以理解AnnotationConfigApplicationContext很关键,它是spring加载bean,管理bean的最重要的类。


源码跟踪:


  public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    // 把该配置类(们)注册进来
    register(annotatedClasses);
    // 容器启动核心方法
    refresh();
  }


this()如下:

  //AnnotatedBeanDefinitionReader是一个读取注解的Bean读取器,这里将this传了进去。
  public AnnotationConfigApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
  }
  //从上面这个构造函数可以顺便提一句:如果你仅仅是这样ApplicationContext applicationContext = new AnnotationConfigApplicationContext()
  // 容器是不会启动的(也就是不会执行refresh()的),这时候需要自己之后再手动启动容器

进而再看看register()方法:


  public void register(Class<?>... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    this.reader.register(annotatedClasses);
  }
  public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
      registerBean(annotatedClass);
    }
  }
  public void registerBean(Class<?> annotatedClass) {
    doRegisterBean(annotatedClass, null, null, null);
  }


实际逻辑在doRegisterBean()此方法上:

  <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
      @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    // 先把此实体类型转换为一个BeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    //abd.getMetadata() 元数据包括注解信息、是否内部类、类Class基本信息等等
    // 此处由conditionEvaluator#shouldSkip去过滤,此Class是否是配置类。
    // 大体逻辑为:必须有@Configuration修饰。然后解析一些Condition注解,看是否排除~
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
    }
    abd.setInstanceSupplier(instanceSupplier);
    // 解析Scope
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    // 得到Bean的名称 一般为首字母小写(此处为AnnotationBeanNameGenerator)
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    // 设定一些注解默认值,如lazy、Primary等等
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    // 解析qualifiers,若有此注解  则primary都成为true了
    if (qualifiers != null) {
      for (Class<? extends Annotation> qualifier : qualifiers) {
        if (Primary.class == qualifier) {
          abd.setPrimary(true);
        }
        else if (Lazy.class == qualifier) {
          abd.setLazyInit(true);
        }
        else {
          abd.addQualifier(new AutowireCandidateQualifier(qualifier));
        }
      }
    }
    // 自定义定制信息(一般都不需要)
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
      customizer.customize(abd);
    }
    // 下面位解析Scope是否需要代理,最后把这个Bean注册进去
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }


AnnotatedBeanDefinitionReader初始化,构造器如下


  public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
  }
  public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    //ConditionEvaluator完成条件注解的判断,在后面的Spring Boot中有大量的应用
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    //这句会把一些自动注解处理器加入到AnnotationConfigApplicationContext下的BeanFactory的BeanDefinitions中  具体见下面
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  }



这里将AnnotationConfigApplicationContext注册为管理BeanDefinition的BeanDefinitionRegistry,也就是说,spring中bean的管理完全交给了AnnotationConfigApplicationContext


AnnotationConfigUtils#registerAnnotationConfigProcessors()

我们要用一些注解比如:@Autowired/@Required/@Resource都依赖于各种各样的BeanPostProcessor来解析(AutowiredAnnotation、RequiredAnnotation、CommonAnnotationBeanPostProcessor等等)


但是向这种非常常用的,让调用者自己去申明,显然使用起来就过重了。所以Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式(若是xml方式,配置<context:annotation- config/>,若是全注解驱动的ApplicationContext,就默认会执行)


  public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null);
  }
  public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {
    // 把我们的beanFactory从registry里解析出来
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
      if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
        beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
      }
      // 相当于如果没有这个AutowireCandidateResolver,就给设置一份ContextAnnotationAutowireCandidateResolver
      if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
      }
    }
    //这里初始长度放4  是因为大多数情况下,我们只会注册4个BeanPostProcessor 如下(不多说了)
    // BeanDefinitionHolder解释:持有name和aliases,为注册做准备
    // Spring 4.2之后这个改成6我觉得更准确点
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4);
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    // 支持JSR-250的一些注解:@Resource、@PostConstruct、@PreDestroy等
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    // 若导入了对JPA的支持,那就注册JPA相关注解的处理器
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition();
      try {
        def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
            AnnotationConfigUtils.class.getClassLoader()));
      }
      catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
            "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
      }
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // 下面两个类,是Spring4.2之后加入进来的,为了更好的使用Spring的事件而提供支持 
    // 支持了@EventListener注解,我们可以通过此注解更方便的监听事件了(Spring4.2之后)
    // 具体这个Processor和ListenerFactory怎么起作用的,且听事件专题分解
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }
    return beanDefs;
  }



相关文章
|
19天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
19天前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
116 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
7天前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
9天前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
7天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
29 3
|
7天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
8天前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
|
2月前
|
Java Spring
Spring boot +Thymeleaf 本地图片加载失败(图片路径)的问题及解决方法
这篇文章详细讲解了在Spring Boot应用程序中本地图片无法加载的问题原因,并提供了两个示例来说明如何通过使用正确的相对路径或Thymeleaf语法来解决图片路径问题。
|
2月前
|
Kubernetes 监控 Shell
在K8S中,我们公司用户反应pod连接数非常多,希望看一下这些连接都是什么信息?什么状态?怎么排查?容器里面没有集成bash环境、网络工具,怎么处理?
在K8S中,我们公司用户反应pod连接数非常多,希望看一下这些连接都是什么信息?什么状态?怎么排查?容器里面没有集成bash环境、网络工具,怎么处理?
|
2月前
|
Java Spring
下一篇
无影云桌面