第四讲-常用的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); // 在此处打断点
}
}
我们找到了被@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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .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语句:使用策略模式优化代码结构