Spring容器之ClassPathXmlApplicationContext和AnnotationConfigApplicationContext

在 Spring Framework 中,AnnotationConfigApplicationContext 和 ClasspathXmlApplicationContext是两个不同的应用程序上下文实现,用于配置和管理 Spring Bean 容器。它们之间的主要区别在于配置的方式和使用场景。

ClassPathXmlApplicationContext

ClasspathXmlApplicationContext是基于 XML 配置的应用程序上下文实现。它从类路径中加载 XML 配置文件,其中包含了 Spring Bean 的定义和配置信息。

在 XML 文件中,你会使用一些特定的标签(如 <bean>)来定义和配置 Spring Bean。这种方式被称为基于 XML 的配置。

优点:

  • 配置文件独立于代码,可以在不重新编译代码的情况下进行更改。
  • 在一些情况下,XML 配置可能更加直观。

适用场景:

  • 喜欢使用 XML 配置的开发人员。
  • 需要在不重新编译代码的情况下修改配置。

创建ClasspathXmlApplicationContext容器:

ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-context.xml");

时序图如下:

new ClassPathXmlApplicationContext(),初始化ClassPathXmlApplicationContext

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    super(parent);
    this.setConfigLocations(configLocations);
    if (refresh) {
        this.refresh();
    }

}

调用AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations),设置xml文件路径

public void setConfigLocations(String... locations) {
    if (locations != null) {
        Assert.noNullElements(locations, "Config locations must not be null");
        this.configLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) {
            this.configLocations[i] = resolvePath(locations[i]).trim();
        }
    }
    else {
        this.configLocations = null;
    }
}

调用AbstractRefreshableConfigApplicationContext的refresh(),其中obtainFreshBeanFactory方法,获取BeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    this.refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
    }

    return beanFactory;
}

调用AbstractRefreshableApplicationContext的refreshBeanFactory方法

protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        //创建BeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}
protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

调用AbstractXmlApplicationContext的loadBeanDefinitions方法,加载bean

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

加载bean主要是对配置文件的读取,并将配置中的bean读取到DefaultListableBeanFactory中。

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext是基于 Java 注解的配置的应用程序上下文实现。它允许你通过 Java 配置类来定义和注册 Spring Bean,而不是使用传统的 XML 配置文件。这种方式被称为基于 Java 的配置(JavaConfig)或者基于注解的配置。

通常,你会在一个或多个 Java 配置类中使用@Configuration注解来定义 Spring Bean,并在这些类中使用@Bean注解来标记方法,以声明要由 Spring 容器管理的 Bean。然后,你可以通过将这些配置类传递给 AnnotationConfigApplicationContext来创建应用程序上下文。

优点:

  • 基于代码的配置更加类型安全,IDE 可以提供更好的支持。
  • 避免了 XML 配置文件的编写和维护。

适用场景:

  • 喜欢在 Java 代码中管理配置的开发人员。
  • 希望利用 Java 的强类型特性来配置 Spring Bean。
ClassPathXmlApplicationContext加载bean的逻辑是在AbstractRefreshableApplicationContext的refreshBeanFactory()方法中,但是我们从类图中可以看出,AnnotationConfigApplicationContext并没有实现这个类,那么它是如何加载bean的呢?

创建AnnotationConfigApplicationContext容器:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

初始化

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
}

1)this()

private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
public AnnotationConfigApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

无参构造函数中主要是初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner两个类。这里主要关注AnnotatedBeanDefinitionReader,跟踪这个类的初始化发现它会注册一堆BeanFactoryPostProcessor处理器,我们只需关注ConfigurationClassPostProcessor。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, Object source) {
    //...
    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));
    }
    // ...
}
2)register(annotatedClasses)

这个方法主要是把所有的配置类注册成bean。

3)refresh()

一个很熟悉的方法,因为ClassPathXmlApplicationContext加载也调用了它,但是ClassPathXmlApplicationContext在调用obtainFreshBeanFactory()的时候就把所有的bean加载完成,但是AnnotationConfigApplicationContext并没有继承自AbstractRefreshableApplicationContext,所以在obtainFreshBeanFactory()这步还是没有加载bean。真正加载bean的操作是在invokeBeanFactoryPostProcessors(beanFactory),这个方法调用所有实现BeanFactoryPostProcessor接口的bean。那么BeanFactoryPostProcessor又是干嘛的呢?

BeanFactoryPostProcessor处理器

和BeanPostProcessor原理一致,Spring提供了对BeanFactory进行操作的处理器BeanFactoryProcessor,简单来说就是获取容器BeanFactory,这样就可以在真正初始化bean之前对bean做一些处理操作。BeanFactoryProcessor定义如下:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,也只要一个方法

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

调用逻辑

AbstractApplicationContext#refresh()中调用了invokeBeanFactoryPostProcessors(beanFactory);这个方法逻辑如下:

  1. 遍历所有实现了BeanDefinitionRegistryPostProcessor接口的bean
  2. 调用实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessors的postProcessBeanDefinitionRegistry
  3. 调用实现了Ordered接口的BeanDefinitionRegistryPostProcessors的postProcessBeanDefinitionRegistry
  4. 调用普通的BeanDefinitionRegistryPostProcessors(没有实现Ordered接口和PriorityOrdered接口)的postProcessBeanDefinitionRegistry
  5. 遍历所有实现BeanFactoryPostProcessor接口的bean,剩下的操作和BeanDefinitionRegistryPostProcessors的处理逻辑是一样的。

 

posted @ 2023-11-14 09:09  残城碎梦  阅读(161)  评论(0编辑  收藏  举报