SpringBoot学习日记(二)
Bean的生命周期
这个相信大家或多或少都有了解,网上也有很多类似的帖子,但今天我想用不同的角度去解读一下。
首先就是这个图,这个就是我们Spring项目启动后,会加载Bean到Spring容器的一个过程,大家可以了解一下,但是主包想把这个流程说清楚点、简单点,有时间的小伙伴可以根据这个流程去查看一下源码。
首先就是从我们的项目启动开始说起,我们说了Spring会加载这个上下文,这个上下文又很多东西,里面有我们需要的Bean信息也就是BeanDefinition,他记录了我们每一个Bean的信息。他怎么来的?
默认情况下会被加载的Bean
配置类中定义的Bean
- 使用
@Configuration
注解的类中通过@Bean
定义的所有Bean - 除非明确指定了懒加载(
@Lazy
)
- 使用
组件扫描发现的Bean
- 被
@Component
及其派生注解标记的类(@Service
,@Repository
,@Controller
等) - 在
@ComponentScan
指定包路径下的这些类
- 被
Spring Boot自动配置的Bean
META-INF/spring/auto-configuration
中定义的自动配置类- 条件满足时(
@Conditional
系列注解)才会加载
基础设施Bean
- Spring框架自身需要的Bean(如
BeanPostProcessor
实现类) - 各种
Aware
接口的实现类
- Spring框架自身需要的Bean(如
会被加载到BeanDefinition的只会是Bean而不是普通的Java类,这个要注意了,Bean和Java类上一期也说明白了。加载完后会调用刷新上下文的方法,这个方法中会创建BeanFactory,然后这个BeanFactory会get这个BeanDefinition的信息,然后会执行postProcessBeanFactory方法把
BeanDefinition中的占位符(比如yml中填的数据库密码)替换或者修改特定Bean的信息为接下来创建Bean做准备,然后开始创建单例Bean实例化,通过反射调用构造函数创建Bean实例,进行了属性填充(依赖注入)、注入其他Bean依赖(@Autowired等)从JVM的角度来说,就是在堆中申请了内存创建了对象把一些基础属性填充了。然后就是初始化,注入简单属性值 Aware接口回调等操作,这个Aware是干啥的呢?
接口名称 | 方法签名 | 注入对象 | 典型用途 |
---|---|---|---|
BeanNameAware | setBeanName(String name) | Bean 在容器中的名称 | 日志打印、动态代理标识 |
BeanClassLoaderAware | setBeanClassLoader(ClassLoader cl) | 加载该Bean的类加载器 | 类加载相关操作 |
BeanFactoryAware | setBeanFactory(BeanFactory bf) | 创建该Bean的BeanFactory | 编程式获取其他Bean |
EnvironmentAware | setEnvironment(Environment env) | 环境配置对象 | 读取环境变量和属性文件 |
ResourceLoaderAware | setResourceLoader(ResourceLoader rl) | 资源加载器 | 读取类路径/文件系统资源 |
ApplicationEventPublisherAware | setApplicationEventPublisher(...) | 事件发布器 | 发布应用事件 |
ApplicationContextAware | setApplicationContext(...) | 应用上下文 | 获取完整的容器功能 |
简单来说就是告诉当前这个Bean,他在Spring容器中叫什么名字、谁加载了他、加载的上下文是什么,就是给这个Bean发一个出生证明,为的就是提前让其他Bean或者需要使用它的类知道他已经被实例化了开始初始化了,不需要重复通过反射注册多个Bean了。
BeanPostProcessor
这个分为前置操作和后置操作,作用就如同下面的表格。
特性 | 前置处理 | 后置处理 |
---|---|---|
调用时机 | Aware回调后,初始化方法前 | 初始化方法执行后 |
主要用途 | 注解处理、环境准备 | AOP代理生成、对象增强 |
能否替换Bean | 可以 | 可以(更常见) |
典型处理器 | InitDestroyAnnotationBeanPostProcessor | AbstractAutoProxyCreator |
性能影响 | 较小 | 较大(可能创建代理) |
使用频率 | 较高(处理基础注解) | 较高(AOP场景) |
好了经历上面的这些步骤,一个Bean就完完整整的被创建出来并被Spring容器管理了,后面就是Bean的使用过程,和销毁过程了,这个其实也没什么好说的,现在问大家一个问题,Bean会被GC回收吗?我相信大家看过主包的JVM系列应该很容易得到答案,答案就是不可能的!为什么?因为我们的JVM是使用可达性分析算法去标记堆对象的,而Bean是被Spring容器管理的,那么就一定是可达的所以就一定不会被回收,明白了吗?Bean被销毁通常情况下都是程序结束了,或者设置了Bean的作用域,原型模式就是每次被依赖注入都会产生新的对象,那么这种肯定是会被GC回收的因为他不受Spring容器管理,使用这种Bean的类应该负起清理他的责任,也就是手动释放这个Bean的资源。也就是Bean的销毁要么就是程序结束了,要么就是手动释放了,当然这个并不绝对,这个只是大多数情况下的。
总结
本篇主要介绍了Bean的生命周期,结合JVM进行解读,没有涉及到源码,有兴趣的小伙伴可以通过启动类的run方法一层一层剖析,好了下期见。