spring-reading./spring-context/spring-context-annotationCo.../README.md

8.5 KiB
Raw Blame History

AnnotationConfigApplicationContext

一、基本信息

✒️ 作者 - Lex 📝 博客 - 掘金 📚 源码地址 - github

二、知识储备

  • ClassPathBeanDefinitionScanner
    • ClassPathBeanDefinitionScanner 类,用于在类路径上扫描指定包及其子包中的类,识别符合条件的类,并将其注册为 Spring Bean 的定义,从而实现组件扫描和自动装配,使我们能够方便地管理和配置应用程序中的 Bean。它允许我们定义过滤条件以确定哪些类应被注册为 Bean以及配合自动装配实现依赖注入提高了应用程序的可维护性和扩展性。
  • AnnotatedBeanDefinitionReader
    • AnnotatedBeanDefinitionReader是一个用于读取和解析带有注解的Bean定义的类它主要用于基于注解的配置方式允许开发者将Java类标记为Spring组件从而让Spring容器自动扫描和注册这些组件而不需要显式配置这些组件的Bean定义。

三、基本描述

AnnotationConfigApplicationContext 提供了一种基于 Java 注解的轻量级、类型安全的方式来配置和管理 Spring 容器中的 bean。这种方式相对于传统的 XML 配置方式更加灵活,并且有助于减少配置文件的数量和复杂性。

四、最佳实践

通过AnnotationConfigApplicationContext容器以手动注册和包扫描两种方式注册Bean定义其中手动注册了单个BeanMyBean类),并通过包扫描注册了指定包路径下的所有标有@Component及其派生注解的类。最后通过打印输出了所有已注册的Bean定义的名称。

public class RegisterBeanDefinitionApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册Bean
        context.register(MyBean.class);
        // 扫描包
        context.scan("com.xcs.spring");
        // 打印Bean定义
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }
    }
}

一个简单的Java类 MyBean,这个类没有任何注解或其他特殊标记。

public class MyBean {
    
}

分别使用@Controller@Service@Repository。这些注解是Spring框架中用于标识不同层次组件的特殊注解。

@Controller
public class MyController {
    
}

@Service
public class MyService {

}

@Repository
public class MyRepository {

}

运行结果发现Spring应用上下文中成功加载了配置并注册了各种Bean定义包括手动注册和通过注解自动注册的。

PS前面4个是Spring容器内部的注册的Bean定义。

beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = myBean
beanDefinitionName = myController
beanDefinitionName = myRepository
beanDefinitionName = myService

五、时序图

sequenceDiagram
Title: AnnotationConfigApplicationContext时序图

AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext()
Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 创建 AnnotationConfigApplicationContext 实例

AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: new AnnotatedBeanDefinitionReader(this)
Note over AnnotationConfigApplicationContext,AnnotatedBeanDefinitionReader: 创建 AnnotatedBeanDefinitionReader 实例

AnnotatedBeanDefinitionReader->>AnnotationConfigApplicationContext: 返回 AnnotatedBeanDefinitionReader
Note over AnnotatedBeanDefinitionReader,AnnotationConfigApplicationContext: 获取 AnnotatedBeanDefinitionReader 实例

AnnotationConfigApplicationContext->>ClassPathBeanDefinitionScanner: new ClassPathBeanDefinitionScanner(this)
Note over AnnotationConfigApplicationContext,ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例

ClassPathBeanDefinitionScanner->>AnnotationConfigApplicationContext: 返回 ClassPathBeanDefinitionScanner
Note over ClassPathBeanDefinitionScanner,AnnotationConfigApplicationContext: 获取 ClassPathBeanDefinitionScanner 实例

AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: register(componentClasses)
Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 注册组件类

AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: this.reader.register(componentClasses)
Note over AnnotationConfigApplicationContext,AnnotatedBeanDefinitionReader: 使用 AnnotatedBeanDefinitionReader 注册组件类

AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: scan(basePackages)
Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 扫描指定包

AnnotationConfigApplicationContext->>ClassPathBeanDefinitionScanner: this.scanner.scan(basePackages)
Note over AnnotationConfigApplicationContext,ClassPathBeanDefinitionScanner: 使用 ClassPathBeanDefinitionScanner 扫描包

六、源码分析

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()方法中,初始化AnnotationConfigApplicationContext实例中的两个关键组件:AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner这两者分别用于处理注解式Bean定义和类路径扫描注册Bean定义。

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

org.springframework.context.annotation.AnnotationConfigApplicationContext#register方法中,主要作用是通过AnnotatedBeanDefinitionReader注册一组组件类将它们解析为Spring容器中的Bean定义。

this.reader.register(componentClasses)方法的源码分析已经在另外一篇关于AnnotatedBeanDefinitionReader类的博客中详细分析了。

@Override
public void register(Class<?>... componentClasses) {
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
        .tag("classes", () -> Arrays.toString(componentClasses));
    this.reader.register(componentClasses);
    registerComponentClass.end();
}

org.springframework.context.annotation.AnnotationConfigApplicationContext#scan方法中,主要作用是通过ClassPathBeanDefinitionScanner扫描指定的基础包路径下的类将它们解析为Spring容器中的Bean定义。

this.scanner.scan(basePackages)方法的源码分析已经在另外一篇关于ClassPathBeanDefinitionScanner类的博客中详细分析了。

@Override
public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
        .tag("packages", () -> Arrays.toString(basePackages));
    this.scanner.scan(basePackages);
    scanPackages.end();
}