第四讲-常用的Bean后处理器

第四讲 常用的Bean后处理器

Bean后处理器的作用:为Bean生命周期的各个阶段提供一些扩展功能

1. 常见的Bean后处理器

编写相关的Bean

package com.cherry.chapter1.a04;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

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

    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 javaHome;

    @Autowired
    public void setJavaHome(@Value("${JAVA_HOME}")String javaHome){
        log.debug("@Value注解生效:{}",javaHome);
        this.javaHome = javaHome;
    }

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

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

    @Override
    public String toString() {
        return "Bean1{" +
                "bean2=" + bean2 +
                ", bean3=" + bean3 +
                ", javaHome='" + javaHome + '\'' +
                '}';
    }
}
package com.cherry.chapter1.a04;

public class Bean3 { }
package com.cherry.chapter1.a04;

public class Bean2 {}

将写好的bean写入到容器中:

package com.cherry.chapter1.a04;

import org.springframework.context.support.GenericApplicationContext;

public class A04Application {
    public static void main(String[] args) {
        // GenericApplicationContext 是Spring提供的一个干净的容器
        GenericApplicationContext context = new GenericApplicationContext();

        // 适用于原始方法注册三个bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);

        // 初始化容器
        context.refresh();
        // 销毁容器
        context.close();
    }
}

我们来查看一下这些Bean有没有被注入销毁等:

22:22:51.197 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@41a4555e
22:22:51.276 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
22:22:51.310 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
22:22:51.310 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
22:22:51.393 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@41a4555e, started on Sun Jul 21 22:22:51 CST 2024

我们发现,上面的信息都没有打印,接下来我们添加一些常见的Bean后处理器来支持对上面注解的解析。

我们首先加入对@Autowired, @Value注解的解析:

// 加入对@Aitowired, @Value注解的解析
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

测试运行:

23:36:24.131 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor'
23:36:24.174 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
23:36:24.301 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
23:36:24.304 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @Autowired注解生效:com.cherry.chapter1.a04.Bean2@27f723
23:36:24.313 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
23:36:24.323 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @Value注解生效:D:\development_tools\jdk17\jdk-17.0.10
23:36:24.323 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
23:36:24.335 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@39ed3c8d, started on Sun Jul 21 23:36:24 CST 2024

我们发现,@Autowired注解和@Value注解都生效了。但是Bean的初始化,@Resource, Bean的销毁都没有被解析,此时再加入相关处理器

// 处理@Resource, @PostConstruct, @PreDestroy注解
context.registerBean(CommonAnnotationBeanPostProcessor.class);

此时再次运行:

12:43:34.574 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @Resource注解生效:com.cherry.chapter1.a04.Bean3@1e7c7811
12:43:34.589 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
12:43:34.591 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @Autowired注解生效:com.cherry.chapter1.a04.Bean2@462d5aee
12:43:34.600 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
12:43:34.627 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @Value注解生效:D:\development_tools\jdk17\jdk-17.0.10
12:43:34.627 [main] DEBUG com.cherry.chapter1.a04.Bean1 - @PostConstruct 生效
12:43:34.651 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@39ed3c8d, started on Mon Jul 22 12:43:34 CST 2024
12:43:34.653 [main] INFO com.cherry.chapter1.a04.Bean1 - @PreDestroy 生效

在Spring容器中,有一个属性绑定的功能,根据属性名称去和配置文件中的键值信息进行匹配绑定,例如下面的代码:

package com.cherry.chapter1.a04;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 找到配置文件中 java.home
 *              java.version
 */
@ConfigurationProperties(prefix = "java")
public class Bean4 {
    private String home;
    private String version;
    
    

    public String getVersion() {
        return version;
    }

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

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

    public String getHome() {
        return home;
    }

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

我想从配置文件或者环境变量中获取java.home或者java.version信息

在主类中注册该Bean:

// 处理@Resource, @PostConstruct, @PreDestroy注解
context.registerBean(CommonAnnotationBeanPostProcessor.class);

在容器初始化后刷新该Bean

// 初始化容器
context.refresh();
// 获取Bean4
System.out.println(context.getBean(Bean4.class));

运行如下:

Bean4{home='null', version='null'}

我们发现绑定失败了,都为null,这是因为我们在容器中加入对@ConfigurationProperties注解解析的支持,这是我们绑定用于解析@ConfigurationProperties注解的后处理器:

// 加入对@ConfigurationProperties注解解析的后处理器
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

再次测试:

Bean4{name='null', version='1.8.0_202'}

2. 对AutowiredAnnotationBeanPostProcessor后处理器的分析

如下面的代码对AutowiredAnnotationBeanPostProcessor后处理器的分析:

package com.cherry.chapter1.a04;

import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DigInAutowired {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2",new Bean2());
        beanFactory.registerSingleton("bean3",new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//@Value

        // 查找哪些属性,方法添加了@Autowired注解,这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //System.out.println(bean1);
        //processor.postProcessProperties(null, bean1,"bean1");  // 执行依赖注入(解析@Autowired和@Value)
        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata",
                String.class, Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        // 反射调用该方法
        // 获取Bean1上加了@Autowired和@Value注解的成员变量和方法参数等信息
        InjectionMetadata metData = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
        System.out.println(metData);	// 在此处打断点

    }
}

image-20240722131718237

我们找到了被@Autowired注解标注的各个方法!

接下来,就是调用 InjectionMetaData 来进行依赖注入,注入式按照类型找值

// 2. 接下来,就是调用 InjectionMetaData 来进行依赖注入,注入式按照类型找值
metData.inject(bean1,"bean1",null);
System.out.println(bean1);
Bean1{bean2=com.cherry.chapter1.a04.Bean2@4b952a2d, bean3=null, javaHome='${JAVA_HOME}'}

我们发现注入到额时候没有对${}内的内容进行解析,这是因为此时还缺少对${}的解析器,我们加入对${}的解析器再次测试:

//加入对${}的解析器
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); //对${}的解析器
Bean1{bean2=com.cherry.chapter1.a04.Bean2@31dc339b, bean3=null, javaHome='D:\development_tools\jdk17\jdk-17.0.10'}

3. InjectionMetaData的执行流程分析

拿取Bean1中的bean3成员变量,并为该成员变量封装为DependencyDescriptor对象,并为此进行注入到操作

    // 拿到Bean1中的bean3成员变量
    Field bean3 = Bean1.class.getDeclaredField("bean3");
    // 将成员变量封装为DependencyDescriptor对象
    DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
    // 根据成员变量找要注入的对象
     Object o = beanFactory.doResolveDependency(dd1, null, null, null);
    System.out.println(o);

对方法上的@Autowired注解上的InjectionMetaData分析和上面一样:

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

运行如下:

com.cherry.chapter1.a04.Bean3@7403c468
com.cherry.chapter1.a04.Bean2@2e3fc542
posted @   LilyFlower  阅读(6)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示