文章目录
【探索Spring底层】Bean生命周期与Bean后处理器
1. Bean生命周期
Spring管理的Bean,生命周期的由以下阶段:
- 创建,可以根据Bean的构造方法或者工厂方法来创建Bean实例对象
- 依赖注入,对Bean内部使用@Autowired,@Value 或其它一些手段进行依赖注入的成员变量,为其填充值、创建关系
- 初始化,调用对象各种初始化方法
- 销毁,在容器关闭的时候,会销毁所有的单例对象(即调用它们的销毁方法)
- prototype 对象也能够销毁,不过需要容器这边主动调用
这里就演示一下Bean的生命周期
准备一个Bean,交给Spring管理
@Component
public class LifeCycleBean {
private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);
public LifeCycleBean() {
log.debug("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.debug("依赖注入: {}", home);
}
@PostConstruct
public void init() {
log.debug("初始化");
}
//单例的时候才会调用
@PreDestroy
public void destroy() {
log.debug("销毁");
}
}
这里面由两个注解需要解释一下,分别是@PostConstruct 和 @PreDestroy
- @PostConstruct, 该注解是用来修饰一个非静态的void( )方法,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被调用一次
- @PreDestroy, 该注解也是是用来修饰一个非静态的void( )方法,被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被调用一次
除了这两个注释,里面的@Autowired的位置相信应该觉得奇怪吧,一般来说@Autowired的注解都是方法某个变量上面,为的是注入某个变量,但是这里却是放置在方法上。
@Autowired
public void autowire(UserService userService) {
log.debug("依赖注入: {}", home);
}
这方法的方法参数是UserService,然后方法中有@Autowired注解,所以Spring就在在Bean工厂中寻找有没有UserService这个Bean,找到之后就会注入进去。
其实这是一种写法,就比如说,这种注入只能是自己定义的对象,比如String这种,就不能像上面这样写,因为Bean工厂中并没有String的Bean,所以上面添加了@Value注解,通过@Value注解就可以找到环境变量JDK的位置
接下来准备一个SpringBoot启动类测试一下
@SpringBootApplication
public class A03 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
context.close();
}
}
在这里就可以清晰看到Bean的生命周期
1.1 Bean后处理器之Bean生命周期扩展
这里准备了一个Bean后处理器类,它实现了InstantiationAwareBeanPostProcessor和DestructionAwareBeanPostProcessor两个接口
- postProcessBeforeDestruction,这个方法是在销毁之前执行
- postProcessBeforeInstantiation,这个方法是在Bean实例化之前执行,若返回null则保持原有对象不变,不为nukk则会替换原来的Bean
- postProcessAfterInstantiation,此方法在Bean实例化之后执行,如果返回值是true继续后续依赖注入阶段,否则跳过依赖注入阶段
- postProcessProperties,这个方法是在依赖注入阶段执行,比如@Autowired、@Value、@Resource
- postProcessBeforeInitialization,和postProcessBeforeInstantiation方法差不多,返回的Bean会替换掉原来的Bean,解析@PostConstruct、@ConfigurationProperties等注解
- postProcessAfterInitialization,这个方法在初始化之后执行,如果返回Bean则会替换对象,这里做的是一些代理增强
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}
}
/**
* bean实例化之前,返回null则是保持原有对象不变,不为null则会替换掉原来的bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
}
return null;
}
/**
* 实例化之后,如果是true继续后续依赖注入阶段,否则跳过依赖注入阶段
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true;
}
/**
* 依赖注入阶段
* @param pvs
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
return pvs;
}
/**
* 初始化之前执行,返回的对象会被替换
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
return bean;
}
}
Bean生命周期的各个时期都会有增强,是靠Bean后处理器实现的
2. 常见的Bean后处理器
学习Bean后处理器之前先准备一个干净的容器,然后注册几个Bean
public class A04 {
public static void main(String[] args) {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
// ⬇️用原始方法注册三个 bean
//不指定Bean的配置范围默认为单例
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
// ⬇️销毁容器
context.close();
}
}
这个容器之所以说是干净的,是因为这里面没有添加有Bean处理器
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;
}
@Autowired
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 {
}
public class Bean3 {
}
这里根据Bean1的注解来讲些相关注解是由哪些Bean处理器实现的
现在运行一下程序发现,什么东东也没有
证明Bean1的相关注解并没有被解析
2.1 AutowiredAnnotationBeanPostProcessor
//拿到BeanFactory
//AutowireCandidateResolver默认解析器不能解析字符串
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
//解析@Autowired @Value注解
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
添加AutowiredAnnotationBeanPostProcessor,使得可以解析@Autowired @Value注解
但是AutowireCandidateResolver默认解析器不能解析字符串,所以还需要拿到Bean工厂设置自动装配候选者,设置为ContextAnnotationAutowireCandidateResolver的对象
这时候运行程序发现@Autowired @Value都生效了
但是@Resource @PostConstruct @PreDestroy这些注解并没有生效
2.2 CommonAnnotationBeanPostProcessor
接着添加CommonAnnotationBeanPostProcessor Bean后处理器就可以解析 @Resource @PostConstruct @PreDestroy这些注解
context.registerBean(CommonAnnotationBeanPostProcessor.class);
这里可以发现,这里是先解析 @Resource,再解析@Autowired,这是因为有排序,前面的【探索Spring底层】2.容器的实现中的1.1小节有提到过
2.3 ConfigurationPropertiesBindingPostProcessor
这个Bean后处理器是用来处理@ConfigurationProperties注解的,@ConfigurationProperties是SpringBoot的一个注解
@ConfigurationProperties(prefix = "java")
public class Bean4 {
private String home;
private String version;
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 + '\'' +
'}';
}
}
当Bean被实例化时,@ConfigurationProperties会将对应前缀的后面的属性与Bean对象的属性匹配。符合条件则进行赋值。
比如上面的前缀是java,然后它就会找java.home和java.version这些属性
给容器添加Bean后处理器,就可以解析@ConfigurationProperties注解了
当然前提是要先将Bean4添加到容器中
context.registerBean("bean4", Bean4.class);
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
在初始化容器之后获取到Bean4并打印出来
// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
System.out.println(context.getBean(Bean4.class));
3. 分析@Autowired Bean后处理器(AutowiredAnnotationBeanPostProcessor 运行分析)
先初始化,创建beanFactory和添加Bean
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器
这里使用了beanFactory的registerSingleton方法来注册Bean,较为方便,但是因为这里new了一个对象,所以创建过程、依赖注入、初始化这些操作都不会再进行一遍了
3.1 使用InjectionMetadata模拟@Autowired 依赖注入
之前都是将这个后处理器添加到beanFactory中,由beanFactory间接调用,为了更加清除AutowiredAnnotationBeanPostProcessor 的流程,可以把它单拿出来
// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
给这个@Autowired Bean后处理器设置BeanFctory,这是因为@Autowired需要根据成员变量的类型或者方法参数类型找到要注入的对象,但是这些Bean都注册再BeanFactory中,所以需要需要给这个Bean后处理器添加BeanFactory,也就是间接使用Bean工厂
Bean1 bean1 = new Bean1();
System.out.println(bean1);
//第一个参数是指定对象的值,也就是这个bean1第一个参数什么值、第二个参数什么值,这里设置为null,是因为这些成员变量参数不需要我们自己设置,而是需要在bean工厂中寻找
processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
System.out.println(bean1);
可以发现一开始Bean1没有值,但是执行依赖注入之后,Bean1的成员变量就有值了
通过查看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;
}
可以发现,第一步是执行findAutowiringMetadata方法,这个方法的作用是寻找有没有加@Autowired 的成员变量和方法参数,找到之后将信息封装在InjectionMetadata对象中
然后执行metadata.inject(bean, beanName, pvs);,通过反射对成员变量或者方法参数赋值
接下来就深究一下这个findAutowiringMetadata方法
查看源码之后发现这是一个私有方法
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
但是我们可以通过反射去获取这个方法
首先通过反射获取findAutowiringMetadata方法,String.class, Class.class, PropertyValues.class这些为findAutowiringMetadata的参数
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
因为这个findAutowiringMetadata方法是私有的,通过setAccessible方法,设置为true,就可以屏蔽Java语言的访问检查,使得对象的私有属性以及方法也能被调用
findAutowiringMetadata.setAccessible(true);
然后用反射来执行这个findAutowiringMetadata方法,要传的参数为findAutowiringMetadata方法中要传的参数
// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
由于返回的InjectionMetadata对象应该没有重写toString方法,所以可以使用DEBUG形式查到值
可以看到里面有一个集合,集合里面有三个元素,这是因为Bean里面有两个方法和一个成员变量添加了@Autowired注解,所以找到的是三个
然后调用inject注入bean1
// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);
也就是源码中的这一步
运行程序看可以发现Bean1注入成功
3.2 使用DependencyDescriptor模拟@Autowired基于类型查找实现注入
首先利用反射获取这个成员变量
Field bean3 = Bean1.class.getDeclaredField("bean3");
然后Spring会在InjectionMetadata内部将它封装成一个DependencyDescriptor对象
第一个参数为成员变量,第二个参数true代表必选,到时候在BeanFactory找不到这个Bean的话就会注入;false的话到时候找不到也不会报错
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
接着InjectionMetadata也就是上面的metadata就会调用beanFactory的doResolveDependency方法
根据成员变量找需要注入的数据,也就是根据封装好的DependencyDescriptor对象找
第一个参数为DependencyDescriptor
第二个参数为bean的名字,可以不指定
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
那为什么给了DependencyDescriptor对象就可以找到要注入谁呢?
这是因为@Autowired是根据类型来找,成员变量有了,就可以知道这个成员变量的类型,进而在容器中找到符合类型的Bean
同样的方法也类似,以方法参数为标准
new MethodParameter(setBean2, 0)是指setBean2方法的第一个参数
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 =
new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o1);
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o2);
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/94993.html