一文学会Spring的@Configuration配置类解析

本文详尽分析了Spring中@Configuration注解的工作原理,重点解析了ConfigurationClassPostProcessor如何处理配置类。通过示例工程,展示了配置类的加载过程,包括BeanFactoryPostProcessor的执行顺序,以及ConfigurationClassPostProcessor在解析配置类时对@ComponentScan、@Import和@Bean注解的处理。文章最后总结了ConfigurationClassPostProcessor在整个Spring初始化过程中的作用和重要性。
摘要由CSDN通过智能技术生成


前言

本篇文章将详细分析Spring中如何加载并解析由@Configuration注解修饰的配置类。

@Configuration注解是Spring中特别重要且知名的注解,大家都知道怎么使用,作用就是可以向容器注册bean,并且就像“每一个成功的男人背后都有一个优秀的女人在支持”,@Configuration注解的背后也有一个功能强大的类在支撑,那就是ConfigurationClassPostProcessor,所以本文的重点就是ConfigurationClassPostProcessor如何解析由@Configuration注解修饰的配置类。

本文的一个思维导图如下所示。

最后,请务必知道SpringBeanDefinition是什么,不能只知道Springbean但不知道bean的前生BeanDefinition,如果真不知道,可以看一下超详细分析Spring的BeanDefinition

Springboot版本:2.4.1

Spring版本:5.3.2

正文

一. 示例工程搭建

在进行ConfigurationClassPostProcessor源码分析前,需要搭建示例工程来演示ConfigurationClassPostProcessor的执行逻辑。

定义如下几个业务类。

public class MyController {}

@Service
public class MyService {}

public class MyDao {}
复制代码

配置类如下所示。

@ComponentScan
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class,
            MyImportSelector.class})
public class MyConfig {

    @Bean
    public MyDao myDao() {
        return new MyDao();
    }

}
复制代码

MyImportBeanDefinitionRegistrar实现了ImportBeanDefinitionRegistrar接口,定义如下。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = new RootBeanDefinition(MyController.class);
        registry.registerBeanDefinition("myController", beanDefinition);
    }

}
复制代码

MyImportSelector实现了ImportSelector接口,定义如下。

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                "com.learn.beanfactory.postprocessor.further.MyFurtherConfig"
        };
    }

}
复制代码

上面中的MyFurtherConfig也是一个配置类,定义如下。

@Configuration
public class MyFurtherConfig {

    @Bean
    public MyFurtherService myFurtherService() {
        return new MyFurtherService();
    }

}
复制代码

MyFurtherService是一个业务类,定义如下。

public class MyFurtherService {}
复制代码

测试类定义如下。

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }

}
复制代码

示例工程目录结构如下所示。

二. BeanFactoryPostProcessor

Spring中提供了大量的扩展点,而ConfigurationClassPostProcessor的扩展点类型叫做bean工厂后置处理器,也就是BeanFactoryPostProcessor

先观察一下BeanFactoryPostProcessor的类图,如下所示。

BeanFactoryPostProcessor接口允许其实现类修改注册表中的BeanDefinition,这里的注册表指ConfigurableListableBeanFactory接口的实现类,通常为DefaultListableBeanFactory(如果不知道这是什么,可以就理解为我们常说的Spring容器)。

Spring启动并初始化容器阶段,会调用到AbstractApplicationContext#refresh方法来刷新容器,在AbstractApplicationContext#refresh方法中,会先执行invokeBeanFactoryPostProcessors() 方法来执行每一个BeanFactoryPostProcessor的逻辑,并且这一步在初始化bean之前完成。

refresh() 方法简要表示如下。

public void refresh() throws BeansException, IllegalStateException {
        
    ......

    // 执行bean工厂后置处理器逻辑
    invokeBeanFactoryPostProcessors(beanFactory);

    ......

    // 添加bean后置处理器到容器
    registerBeanPostProcessors(beanFactory);

    ......
    
    // 初始化bean
    finishBeanFactoryInitialization(beanFactory);

    ......

}
复制代码

既然BeanFactoryPostProcessor接口允许其实现类修改注册表中的BeanDefinition,但是在Spring启动并且初始化容器的最开始阶段,Spring容器持有的注册表DefaultListableBeanFactory中是没有BeanDefinition的,那么肯定有一个时机存在一个类完成了将BeanDefinition加载到注册表中,时机就是invokeBeanFactoryPostProcessors() 方法的调用,类则是ConfigurationClassPostProcessor

ConfigurationClassPostProcessor用于解析由@Configuration注解修饰的类(称为配置类),其类图如下所示。

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口继承于BeanFactoryPostProcessor接口,所以ConfigurationClassPostProcessor有如下两层身份。

  1. ConfigurationClassPostProcessor是一个bean工厂后置处理器;
  2. ConfigurationClassPostProcessor具备向容器注册BeanDefinition的功能。

所以在Spring启动并初始化容器的最开始阶段,Spring会执行ConfigurationClassPostProcessor的逻辑来解析配置类,并且Spring还会通过流程控制,让BeanDefinitionRegistryPostProcessor的执行先于非BeanDefinitionRegistryPostProcessorbean工厂后置处理器。

小结

  1. BeanFactoryPostProcessor叫做bean工厂后置处理器,ConfigurationClassPostProcessor是由Spring提供的可以实现向容器注册BeanDefinition功能的bean工厂后置处理器;
  2. ConfigurationClassPostProcessor的执行先于非BeanDefinitionRegistryPostProcessorbean工厂后置处理器;
  3. ConfigurationClassPostProcessor向容器注册BeanDefinition是通过解析配置类来实现。

三. BeanFactoryPostProcessor执行顺序

前文已知ConfigurationClassPostProcessor就是一个Spring内置的BeanFactoryPostProcessor,但是Spring中有那么多的BeanFactoryPostProcessor,包括Spring内置提供的,用户自定义的,这些BeanFactoryPostProcessor之间的执行肯定有一个先后,本节将对BeanFactoryPostProcessor执行顺序进行一个分析。

在示例工程中,创建AnnotationConfigApplicationContext容器时传入了MyConfig配置类的Class对象,称创建容器时传入的配置类为初始配置类,下面再给出初始配置类MyConfig的定义,如下所示。

@ComponentScan
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class,
            MyImportSelector.class})
public class MyConfig {

    @Bean
    public MyDao myDao() {
        return new MyDao();
    }

}
复制代码

MyConfig的定义中,使用了如下注解。

  1. @ComponentScan注解。默认将配置类所在包及其子包下所有由@Component,@Controller,@Service,@Repository,@Configuration注解修饰的类注册为容器中的bean
  2. @Configuration注解。表示当前类是一个配置类,配置类也会被注册为容器中的bean
  3. @Import注解。通过@Import注解可以导入ImportBeanDefinitionRegistrarImportSelector的实现类,并通过它们的实现类来向容器注册bean或配置类;
  4. @Bean注解。在配置类中所有由@Bean注解修饰的方法的返回值,会被注册为容器中的bean

在配置类中,有两种方式来向容器注册bean

  1. 通过配置类直接向容器注册bean。例如使用@Bean注解,使用@ComponentScan注解和使用@Import(业务类.class);
  2. 通过配置类向容器注册配置类从而间接注册bean。被注册的配置类也会被解析,从而被注册的配置类所配置的bean也会被注册到容器中,Springboot中的自动装配就是这样实现的。

上述讨论的通过配置类向容器注册bean,并且可以使用多种注解和基于多种方式,能够实现这样的功能,实际是依赖的ConfigurationClassPostProcessor。在Spring中,new一个容器就会触发这个容器的初始化逻辑,例如示例工程中AnnotationConfigApplicationContext的构造函数会调用到AbstractApplicationContextrefresh() 方法,在refresh() 方法中会在初始化bean之前调用到invokeBeanFactoryPostProcessors() 方法来执行bean工厂后置处理器的逻辑,invokeBeanFactoryPostProcessors() 方法实现如下所示。

protected void invokeBeanFactoryPostProcessors(Co
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值