深入探究Spring中Bean的生命周期

你好,我柳岸花开。

在使用Spring框架时,我们经常会听到关于Bean的生命周期的概念。本文将深入探讨Bean的生命周期,从Bean的生成到销毁,逐步解析Spring框架中的关键步骤。

什么是Bean的生命周期?

Bean的生命周期指的是Bean在Spring容器中的生成、初始化、使用和销毁的整个过程。在这个过程中,Spring框架负责管理Bean的创建和销毁,同时允许开发者通过扩展点来干预Bean的行为,以满足特定需求。

Bean的生成过程

在Spring中,Bean的生成过程是通过IOC(控制反转)实现的。下面我们来详细了解一下Bean的生成过程:

java 复制代码
// 入口代码
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();

生成BeanDefinition:

在Spring启动时,会扫描指定的包路径,得到所有的BeanDefinition。

扫描类文件流程如下:

  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource对象)
  2. 遍历每个Resource对象
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不难理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集。

CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用的ASM技术,并没有加载这个类到JVM。最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)

为什么要使用ASM技术,Spring启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样不太好,所以使用了ASM技术。

ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能; .class字节码是有严格的格式的,ASM可以通过格式来解析字节码,得到类名称、方法、属性等信息; ASM比jdk反射技术性能更高; JVM类加载是懒加载的,通过ASM来获取类信息,更节省JVM性能;

合并BeanDefinition:

Spring支持父子BeanDefinition的合并,以及工厂方法创建Bean。

加载类:

根据BeanDefinition中的类名加载对应的类,这一步是实例化对象的前提条件。

java 复制代码
// 加载类
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

if (mbd.hasBeanClass()) {
    // 如果beanClass属性的类型是Class,那么就直接返回
	return mbd.getBeanClass();
}
// 根据类名加载
if (System.getSecurityManager() != null) {
	return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
		doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
	}
else {
    // 利用BeanFactory所设置的类加载器来加载类,如果没有设置,则默认使用ClassUtils.getDefaultClassLoader()所返回的类加载器来加载。
	return doResolveBeanClass(mbd, typesToMatch);
}

实例化前扩展点:

Spring提供了InstantiationAwareBeanPostProcessor扩展点,允许在实例化对象之前进行一些操作。

java 复制代码
// 实例化前扩展点
@Component
public class BobBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		// 注意,此时还没有实例化,所以能拿到的是beanClass
        if ("userService".equals(beanName)) {
			System.out.println("实例化前");
            // 可以返回值,非null时,表示不需要Spring来实例化了,并且后续的Spring依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步
            return new UserService();
		}
		return null;
	}
}

实例化Bean:

根据BeanDefinition创建对象实例,可以通过Supplier创建、工厂方法创建或者推断构造方法创建等方式。

BeanDefinition的后置处理:

Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以对此时的BeanDefinition进行加工。

java 复制代码
@Component
public class BobMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if ("userService".equals(beanName)) {
			beanDefinition.getPropertyValues().add("orderService", new OrderService());
		}
	}
}

在Spring源码中,AutowiredAnnotationBeanPostProcessor就是一个MergedBeanDefinitionPostProcessor,它的postProcessMergedBeanDefinition()中会去查找注入点,并缓存在AutowiredAnnotationBeanPostProcessor对象的一个Map中(injectionMetadataCache)。

实例化后:

在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()。

java 复制代码
@Component
public class BobInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

		if ("userService".equals(beanName)) {
			UserService userService = (UserService) bean;
			userService.test();
		}

		return true;
	}
}

处理属性:

这个步骤中,就会处理@Autowired、@Resource、@Value等注解,也是通过InstantiationAwareBeanPostProcessor.postProcessProperties()扩展点来实现的。

java 复制代码
@Component
public class BobInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			for (Field field : bean.getClass().getFields()) {
				if (field.isAnnotationPresent(FireInject.class)) {
					field.setAccessible(true);
					try {
						field.set(bean, "123");
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}

		return pvs;
	}
}

执行Aware:

完成了属性赋值之后,Spring会执行一些回调,包括:

BeanNameAware:回传beanName给bean对象。

BeanClassLoaderAware:回传classLoader给bean对象。

BeanFactoryAware:回传beanFactory给对象。

初始化前:

初始化前,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessBeforeInitialization()。 利用初始化前,可以对进行了依赖注入的Bean进行处理。

java 复制代码
@Component
public class BobBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化前");
		}

		return bean;
	}
}

InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的方法, ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:

  • EnvironmentAware:回传环境变量

  • EmbeddedValueResolverAware:回传占位符解析器

  • ResourceLoaderAware:回传资源加载器

  • ApplicationEventPublisherAware:回传事件发布器

  • MessageSourceAware:回传国际化资源

  • ApplicationStartupAware:回传应用其他监听对象,可忽略

  • ApplicationContextAware:回传Spring容器ApplicationContext

初始化

  • 查看当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()方法
  • 执行BeanDefinition中指定的初始化方法

初始化后

这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization()。 可以在这个步骤中,对Bean最终进行处理,Spring中的AOP就是基于初始化后实现的,初始化后返回的对象才是最终的Bean对象。

java 复制代码
@Component
public class FireBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化后");
		}

		return bean;
	}
}

总结

  1. 实例化前(InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation())
  2. 实例化
  3. 合并Bean(MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition())
  4. 实例化后(InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation())
  5. 自动注入
  6. 属性赋值(InstantiationAwareBeanPostProcessor.postProcessProperties())
  7. Aware对象
  8. 初始化前(BeanPostProcessor.postProcessBeforeInitialization())
  9. 初始化
  10. 初始化后(BeanPostProcessor.postProcessAfterInitialization())

Bean的销毁过程

与生成过程相对应,Bean的销毁过程发生在Spring容器关闭时。下面是Bean销毁的主要步骤:

java 复制代码
// Bean的销毁过程
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

// 容器关闭
context.close();

在Spring容器关闭过程时:

  1. 首先发布ContextClosedEvent事件
  2. 调用lifecycleProcessor的onCloese()方法
  3. 销毁单例Bean

1.遍历disposableBeans,把每个disposableBean从单例池中移除,调用disposableBean的destroy()。如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean;如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉。

2.清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的beanName

3.清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的beanName数组

4.清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean

最后

通过以上步骤,我们可以清晰地了解Spring框架中Bean的生命周期。掌握Bean的生命周期对于深入理解Spring框架的运作机制和实现原理非常重要,也有助于我们在实际开发中更好地利用Spring框架的功能。

希望本文能够帮助您更好地理解Spring中Bean的生命周期,如果有任何疑问或意见,请随时留言交流。

本文由mdnice多平台发布

相关推荐
云边小网安32 分钟前
java集合(十) ---- LinkedList 类
java·开发语言·青少年编程·java集合
乐神嘎嘎嘎34 分钟前
springboot速通
java·spring boot
Zephyrtoria2 小时前
区间合并:区间合并问题
java·开发语言·数据结构·算法
yuren_xia6 小时前
RabbitMQ 知识详解(Java版)
java·rabbitmq·java-rabbitmq
kfyty7257 小时前
轻量级 ioc 框架 loveqq,支持接口上传 jar 格式的 starter 启动器并支持热加载其中的 bean
java·jvm·ioc·jar·热加载
早起鸟儿7 小时前
docker-Dockerfile 配置
java·linux·运维·docker
云边小网安7 小时前
java集合篇(六) ---- ListIterator 接口
java·开发语言·青少年编程·java集合
都叫我大帅哥7 小时前
Spring WebFlux:响应式编程的“未来战士”还是“花架子”?
java·spring·flux
都叫我大帅哥7 小时前
Reactor 深度解析:响应式编程的「核反应堆」是如何工作的?
java·spring
不太厉害的程序员7 小时前
NC65配置xml找不到Bean
xml·java·后端·eclipse