5.Spring5底层原理之Bean后处理器执行流程分析

常见的Bean后处理器

这章我们来看看常见的一些bean处理器,首先我们使用GenericApplicationContext,这个ApplicationContext是一个纯净的context,没有包含后处理器,方便我们后续观察。

先上代码

三个bean类
public class Bean1 {
    private final static Logger log = LoggerFactory.getLogger(Bean1.class);

    public Bean1(){
        log.debug("bean1 构造");
    }

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2){
        log.debug("@Autowired 生效:{}",bean2);
        this.bean2 = bean2;
    }

    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3){
        log.debug("@Resource 生效:{}",bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}")String home){
        log.debug("@Value 生效:{}",home);
        this.home = home;
    }


    @PostConstruct
    public void init(){
        log.debug("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy(){
        log.debug("@PreDestroy 生效");
    }


    @Override
    public String toString() {
        return "Bean1{" +
                "bean2=" + bean2 +
                ", bean3=" + bean3 +
                ", home='" + home + '\'' +
                '}';
    }
}


public class Bean2 {
    private final static Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2(){
        log.debug("bean2 构造");
    }
}

public class Bean3 {
    private final static Logger log = LoggerFactory.getLogger(Bean3.class);

    public Bean3(){
        log.debug("bean3 构造");
    }
}
测试类
public class A04Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("bean1",Bean1.class);
        context.registerBean("bean2",Bean2.class);
        context.registerBean("bean3",Bean3.class);

        context.refresh();

        System.out.println(context.getBean(Bean1.class));

        context.close();
    }
}

现在我们来讲解一下这些代码,首先我们新建了3个bean,其中bean1比较关键,它用@Autowired注入bean2,用@Resource注入bean3,用@Autowired+@Value注入方法的String参数,并且还有@PostConstruct和@PreDestroy注解的使用,在这些关键的地方,我们都进行了日志输出。

在测试类中,我们注册了3个bean,其中context.refresh方法,用来刷新Spring上下文,做初始化操作,其中包含帮我们自动执行后处理器,现在我们还没有添加后处理器,添加以后会自动帮我们执行。然后获取bean1,观察输出结果。

我们知道这种情况Spring会帮我们创建这些bean的实例,但是由于GenericApplicationContext没有包含任何bean后处理器,所以类似@Autowired注解无法解析。我们运行一下程序来验证我们的想法。看看日志输出结果。

17:21:07.257 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@74a10858
17:21:07.319 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
17:21:07.335 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - bean1 构造
17:21:07.336 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
17:21:07.336 [main] DEBUG com.zhaojun.springsource.a04.Bean2 - bean2 构造
17:21:07.336 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
17:21:07.337 [main] DEBUG com.zhaojun.springsource.a04.Bean3 - bean3 构造
Bean1{bean2=null, bean3=null, home='null'}
17:21:07.397 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@74a10858, started on Thu Apr 21 17:21:07 CST 2022

通过日志我们可以看到,3个bean都已实例化完成,但是却没有为我们进行注入,所有注解都没用解析生效。

AutowiredAnnotationBeanPostProcessor

接下来我们看看第一个bean后处理器AutowiredAnnotationBeanPostProcessor,这个后处理器主要用来解析@Autowired注解,也可以解析@Value注解

public class A04Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("bean1",Bean1.class);
        context.registerBean("bean2",Bean2.class);
        context.registerBean("bean3",Bean3.class);
        
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 不加在取@Value注解值时会报错
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value

        context.refresh();

        System.out.println(context.getBean(Bean1.class));

        context.close();
    }
}

然后我们再看日志输出

17:27:17.814 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@28ac3dc3
17:27:17.862 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor'
17:27:17.899 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
17:27:17.929 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - bean1 构造
17:27:18.088 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
17:27:18.098 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Value 生效:C:\Program Files\Java\jdk1.8.0_121
17:27:18.102 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
17:27:18.103 [main] DEBUG com.zhaojun.springsource.a04.Bean2 - bean2 构造
17:27:18.104 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Autowired 生效:com.zhaojun.springsource.a04.Bean2@e1de817
17:27:18.104 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
17:27:18.104 [main] DEBUG com.zhaojun.springsource.a04.Bean3 - bean3 构造
Bean1{bean2=com.zhaojun.springsource.a04.Bean2@e1de817, bean3=null, home='C:\Program Files\Java\jdk1.8.0_121'}
17:27:18.128 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@28ac3dc3, started on Thu Apr 21 17:27:17 CST 2022

其中bean2和home注入成功,@Autowired和@Value注解生效。

CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor主要用来处理 @Resource @PostConstruct @PreDestroy 注解

public class A04Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("bean1",Bean1.class);
        context.registerBean("bean2",Bean2.class);
        context.registerBean("bean3",Bean3.class);
        
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 不加在取@Value注解值时会报错
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
        
        context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

        context.refresh();

        System.out.println(context.getBean(Bean1.class));

        context.close();
    }
}

日志输出

10:55:47.877 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@28ac3dc3
10:55:47.923 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor'
10:55:47.949 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.CommonAnnotationBeanPostProcessor'
10:55:47.960 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
10:55:47.984 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - bean1 构造
10:55:48.098 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
10:55:48.098 [main] DEBUG com.zhaojun.springsource.a04.Bean3 - bean3 构造
10:55:48.099 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Autowired 生效:com.zhaojun.springsource.a04.Bean3@17a7f733
10:55:48.109 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
10:55:48.109 [main] DEBUG com.zhaojun.springsource.a04.Bean2 - bean2 构造
10:55:48.110 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Autowired 生效:com.zhaojun.springsource.a04.Bean2@50caa560
10:55:48.116 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
10:55:48.126 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Value 生效:C:\Program Files\Java\jdk1.8.0_121
10:55:48.126 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @PostConstruct 生效
Bean1{bean2=com.zhaojun.springsource.a04.Bean2@50caa560, bean3=com.zhaojun.springsource.a04.Bean3@17a7f733, home='C:\Program Files\Java\jdk1.8.0_121'}
10:55:48.168 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@28ac3dc3, started on Fri Apr 22 10:55:47 CST 2022
10:55:48.170 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @PreDestroy 生效

ConfigurationPropertiesBindingPostProcessor

这个后处理器,用来处理@ConfigurationProperties,为此我们创建一个bean4,它来注入环境中java的版本信息

@ConfigurationProperties(prefix = "java")
public class Bean4 {
    private final static Logger log = LoggerFactory.getLogger(Bean4.class);

    private String home;

    private String version;

    public Bean4(){
        log.debug("bean4 构造");
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
                "home='" + home + '\'' +
                ", version='" + version + '\'' +
                '}';
    }
}

测试类

public class A04Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("bean4",Bean4.class);
        
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); //@ConfigurationProperties

        context.refresh();

        System.out.println(context.getBean(Bean4.class));

        context.close();
    }
}

ConfigurationPropertiesBindingPostProcessor的使用方式与前面的不一样,我们运行看看结果

11:03:26.771 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@74a10858
11:03:26.805 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor'
11:03:26.823 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.boot.context.internalConfigurationPropertiesBinder'
11:03:26.824 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.boot.context.internalConfigurationPropertiesBinderFactory'
11:03:26.832 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean4'
11:03:26.834 [main] DEBUG com.zhaojun.springsource.a04.Bean4 - bean4 构造
Bean4{home='C:\Program Files\Java\jdk-11.0.11', version='11.0.11'}
11:03:27.047 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@74a10858, started on Fri Apr 22 11:03:26 CST 2022

常用的后处理器讲完后,我们来了解下后处理器的执行流程。

后处理器执行流程

这一节我们来了解下后处理器的大致执行流程,我们以AutowiredAnnotationBeanPostProcessor后处理器为例,后处理器执行处理的方法是postProcessProperties(),我们试试手动调用这个方法。

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
//        AbstractBeanDefinition bean3 = BeanDefinitionBuilder.genericBeanDefinition(Bean3.class).setScope("singleton").getBeanDefinition();
//        beanFactory.registerBeanDefinition("bean3",bean3);
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); //@Value
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // 解析${}

        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        System.out.println("---");

        // pvs 参数:自动装配的值 这里填null 根据类型去匹配
        processor.postProcessProperties(null, bean1, "bean1"); //执行依赖注入 解析@Autowired @Value
        System.out.println(bean1);
    }
}

其中需要注意的是,在注册bean的时候,我们采用的直接new出来,这样spring会认为是一个完成的bean,就不会执行创建、依赖注入、初始化过程,其实bean2,bean3也不需要看到这些,简单起见就直接new来注册的bean,当然也可以采用注册bean定义的方法。注释中有示例。

我们进行手动调用postProcessProperties,观察打印日志

11:23:33.204 [main] DEBUG com.zhaojun.springsource.a04.Bean2 - bean2 构造
11:23:33.212 [main] DEBUG com.zhaojun.springsource.a04.Bean3 - bean3 构造
11:23:33.241 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - bean1 构造
Bean1{bean2=null, bean3=null, home='null'}
---
11:23:33.411 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Autowired 生效:com.zhaojun.springsource.a04.Bean2@41d477ed
11:23:33.423 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
11:23:33.424 [main] DEBUG com.zhaojun.springsource.a04.Bean1 - @Value 生效:C:\Program Files\Java\jdk1.8.0_121
Bean1{bean2=com.zhaojun.springsource.a04.Bean2@41d477ed, bean3=null, home='C:\Program Files\Java\jdk1.8.0_121'}

日志中我们可以看到,没有执行postProcessProperties,没有给我们注入。执行后就注入成功了。

我们来看看postProcessProperties()的源码

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

断点看看metadata是什么

image-20220422113117465

metadata中有两个属性,一个是targetClass,就是目标类,就是bean1。另外一个属性是injectedElements,是一个集合,分别对应着@Autowired注解标注的位置,一个是bean2方法,一个是setHome方法。

接下来看看inject方法,inject方法其实就是获取到@Autowired注解的字段或方法,通过反射创建注入的bean实例。

注入的是字段
@Autowired
private Bean3 bean3;
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean3", new Bean3());

Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3,false);
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o);

输出结果

14:56:42.341 [main] DEBUG com.zhaojun.springsource.a04.Bean3 - bean3 构造
com.zhaojun.springsource.a04.Bean3@314c508a
注入的是方法
private Bean2 bean2;

@Autowired
public void setBean2(Bean2 bean2){
    this.bean2 = bean2;
}
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2());

Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
Object o2 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o2);

输出结果

15:07:35.252 [main] DEBUG com.zhaojun.springsource.a04.Bean2 - bean2 构造
com.zhaojun.springsource.a04.Bean2@59717824
值注入方法
private String home;

@Autowired
public void setHome(@Value("${JAVA_HOME}")String home){
    this.home = home;
}
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); //@Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // 解析${}

Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), false);
Object o3 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o3);

输出结果

15:13:13.470 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
C:\Program Files\Java\jdk1.8.0_121

5.Spring5底层原理之Bean后处理器执行流程分析
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/spring-bean-processor-analyse
作者
卑微幻想家
发布于
2022-04-22
许可协议