bean的一生
你曾读spring源码 “不知所云”、“绞尽脑汁”、“不知所措”嘛
那这篇文章可能会对你有所帮助,小编尝试用简单、易懂的例子来模拟spring经典代码Spring Bean生命周期及扩展点,
让你能够轻松的读懂Spring Bean的生命周期,更加深入的理解Spring。
那好,下面小编将从如下几个步骤来介绍✍️✍️✍️
1》回顾Spring Bean相关知识点
1.1》什么是Bean
1.2》什么是 Spring Bean 的生命周期
1.3》 Spring Bean 的生命周期的扩展点
2》模拟 Spring Bean 生命周期及扩展点
2.1》 流程图
2.2》 代码功能介绍
2.3》 代码实现
2.3.1》 指定扫描路径
2.3.2》 扫描、生成classList
2.3.3》 bean定义、建立beanName映射关系、保存beanPostProcessor对象
2.3.4》 实例化bean、对象填充属性、执行award方法、BeanPostProcessor干预、初始化、放入单例池、获取bean
2.3.5》 业务类实现
2.3.6》 运行结果
3》总结
1 》回顾Spring Bean相关知识点
1. 1 》 什么是Bean
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。
简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。
1.2 》 什么是 Spring Bean 的生命周期
实例化
该对象不再被使用时通过垃圾回收机制进行回收
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
1.3 》 Spring Bean 的生命周期的扩展点
实例化 -> 属性赋值 -> 初始化 -> 销毁
主要是后处理器方法,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor 接口方法。
这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。
在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。
可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean、DisposableBean 等方法,这些方法只对当前 Bean 生效。

如上为Spring Bean知识的回顾,如果忘记了,没关系,让咱们在代码中寻找记忆。下面咱们开始模拟Spring Bean 生命周期。
2 》 模拟 Spring Bean 生命周期及扩展点
在看代码之前,首先,先给大家展示一下流程图、代码功能介绍,方便大家理解。
2.1 》 流程图

2.2 》 代码功能介绍

Application.java 启动类,实例化spring容器
AnnotationConfig.java 配置类,指定扫描路径
AnnotationApplicationContext.java 核心类,bean创建、获取
BeanDefinition.java BeanDefinition定义
SqBeanPostProcessor.java 后置处理器,初始化前后对bean进行干预
User.java 业务类,用户对象,用户信息设置
UserService.java 业务类,用户service,实现BeanNameAware、InitializingBean
spring注解模拟
Autowired.java
Component.java
Lazy.java
Scope.java
ComponentScan.java
spring接口模拟
BeanNameAware.java
BeanPostProcessor.java
InitializingBean.java
现在咱们开始看代码、看代码、看代码️️️
2.3》 代码实现
2.3.1》 AnnotationConfig.java 指定扫描路径
@ComponentScan("com.hz.spring.demo.service")
public class AnnotationConfig {
}
2.3.2 》 AnnotationApplicationContext.java 扫描、生成classList
public AnnotationApplicationContext(Class<AnnotationConfig> config) {
this.config = config;
try {
// 扫描
ComponentScan componentScan = config.getAnnotation(ComponentScan.class);
// com.hz.spring.demo.service
String path = componentScan.value();
path = path.replace(".", "/");
// 通过类加载器 获取target class 路径
ClassLoader classLoader = AnnotationApplicationContext.class.getClassLoader();
URL url = classLoader.getResource(path);
if (url != null) {
// 获取classList
File file = new File(url.getFile());
List<Class<?>> classList = this.scanAndGetClassList(classLoader, file);
// bean定义、建立beanName映射关系、保存beanPostProcessor对象
this.compBdMap(classList);
// 实例化。创建bean。放入单例池
// 核心、核心、核心
this.instanceBean();
}
} catch (Exception e) {
log.error("AnnotationApplicationContext error:", e);
}
}
扫描、加载class
private List<Class<?>> scanAndGetClassList(ClassLoader classLoader, File file) {
List<Class<?>> classList = Lists.newArrayList();
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
try {
String absolutePath = f.getAbsolutePath();
// Window target文件路径
// D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\User.class
// D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\UserService.class
// absolutePath = absolutePath.substring(absolutePath.indexOf("com\\"), absolutePath.indexOf(".class"));
// absolutePath = absolutePath.replace("\\", ".");
// Mac target文件路径
// /Users/huzhong5/hz/IdeaProjects/yb-claim/psc-invoicing/target/test-classes/com/hz/spring/demo/service/UserService.class
absolutePath = absolutePath.substring(absolutePath.indexOf("com/"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("/", ".");
// absolutePath: com.hz.spring.demo.service.UserService
Class<?> clazz = classLoader.loadClass(absolutePath);
classList.add(clazz);
} catch (Exception e) {
log.error("scanAndGetClassList error e:", e);
}
}
}
}
return classList;
}
2.3.3 》 bean定义、建立beanName映射关系、保存beanPostProcessor对象
private void compBdMap(List<Class<?>> classList) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
for (Class<?> clazz : classList) {
// 遍历 @Component("") 类。设置bean属性
if (clazz.isAnnotationPresent(Component.class)) {
Component clazzAnnotation = clazz.getAnnotation(Component.class);
String beanName = clazzAnnotation.value();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClass(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefinition.setScope(scope);
} else {
// 默认单例
beanDefinition.setScope(SCOPE_SINGLETON);
}
// userService:beanDefinition
beanDefinitionMap.put(beanName, beanDefinition);
// 保存beanPostProcessor对象
// 通过反射获取对象
// clazz : SQYCBeanPostProcessor
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
beanPostProcessorList.add(instance);
}
}
}
}
遍历beanDefinitionMap,判断beanDefinition作用域
private void instanceBean() {
Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet();
for (Map.Entry<String, BeanDefinition> entry : entries) {
BeanDefinition beanDefinition1 = entry.getValue();
String beanName = entry.getKey();
if (SCOPE_SINGLETON.equals(beanDefinition1.getScope())) {
if (!singletonObjects.containsKey(beanName)) {
// create
Object instance = this.doCreateBean(beanName, beanDefinition1);
singletonObjects.put(beanName, instance);
}
} else {
this.doCreateBean(beanName, beanDefinition1);
}
}
}
2.3.4 》 实例化bean、对象填充属性、执行award 方法、BeanPostProcessor干预、初始化、放入单例池、获取bean
private Object doCreateBean(String beanName, BeanDefinition beanDefinition1) {
// com.hz.spring.demo.service.UserService
Class<?> beanClass = beanDefinition1.getBeanClass();
try {
// 实例化bean
Object instance = beanClass.getConstructor().newInstance();
// 填充属性。为bean添加属性。如:userService 添加属性 user
// 解析Autowired注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
// user 他也是一个bean。执行从单例池获取就可以
// 根据beanName获取对象
Object bean = this.getBean(declaredField.getName());
declaredField.setAccessible(true);
// 将属性declaredField 赋值给 instance
declaredField.set(instance, bean);
}
}
// award.通过beanNameAward实现获取beanName
if (instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
// 初始化之前。BeanPostProcessor干预。应用场景:AOP 、属性注入、资源回收
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
}
// 初始化。在属性注入完成后调用。可以对属性进行一些改动
if (instance instanceof InitializingBean) {
try {
((InitializingBean) instance).afterPropertiesSet();
} catch (Exception e) {
log.error("doCreateBean InitializingBean error:", e);
}
}
// 初始化之后。BeanPostProcessor干预
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
}
return instance;
} catch (Exception e) {
log.error("doCreateBean error:", e);
}
return null;
}
public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
String scope = beanDefinition.getScope();
if (SCOPE_SINGLETON.equals(scope)) {
Object bean = singletonObjects.get(beanName);
if (bean == null) {
bean = this.doCreateBean(beanName, beanDefinition);
}
return bean;
} else if (SCOPE_PROTOTYPE.equals(scope)) {
return this.doCreateBean(beanName, beanDefinition);
}
return null;
}
BeanPostProcessor实现类定义
@Component("sqBeanPostProcessor")
@Slf4j
public class SqBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
log.info("SqBeanPostProcessor {} 初始化之前", beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
log.info("SqBeanPostProcessor {} 初始化之后", beanName);
// 可以改变对象。很强大
return bean;
}
}
如上,bean创建流程就完成啦️️️
2.3.5 》 业务类实现
下面,咱们看看业务类怎样对Spring Bean 进行扩展,
UserService业务核心类定义,实现BeanNameAware、InitializingBean,对bean进行干预;
@Component("userService")
@Scope(value = "singleton")
@Slf4j
public class UserService implements BeanNameAware, InitializingBean {
@Autowired
private User user;
/**
* 可以通过spring award回调方法实现
* beanName应用场景:
* spring + dubbo。dubbo暴露服务,单个服务的地址可能是beanName的名称组成
*/
private String beanName;
/**
* 所有属性填充后。获得
*/
private String userName;
public void test() {
log.info("UserService test() user.age:{},beanName:{},userName:{}", user.getAge(), beanName, userName);
}
/**
* BeanNameAward
*
* @param beanName
*/
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
log.info("UserService setBeanName() beanName:{}", beanName);
}
/**
* InitializingBean
* 所有属性填充后获得
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
this.userName = "w";
this.user.setAge("24");
log.info("UserService afterPropertiesSet() userName:{},age:{}", userName, user.getAge());
}
}
2.3.6 》 运行结果
Application.java 启动类,实例化spring容器,获取userService对象,调用test方法。
@Slf4j
public class Application {
public static void main(String[] args) {
AnnotationApplicationContext configWebApplicationContext = new AnnotationApplicationContext(AnnotationConfig.class);
UserService userService = (UserService) configWebApplicationContext.getBean("userService");
userService.test();
}
}
下面我们来运行下,结果如下:
17:41:39.466 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之前 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之后 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之前 17:41:39.472 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService setBeanName() beanName:userService 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之前 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService afterPropertiesSet() userName:w,age:24 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService test() user.age:24,beanName:userService,userName:w
3 》 总结
如上为Spring Bean生命周期及扩展点代码模拟, 希望对大家有所帮助。
作者:京东保险 胡忠
来源:京东云开发者社区 转载请注明来源
bean的一生的更多相关文章
- Spring 了解Bean的一生(生命周期)
转载 https://blog.csdn.net/w_linux/article/details/80086950 该篇博客就来了解IoC容器下Bean的一生吧,也可以理解为bean的生命周期. ## ...
- Spring Bean的一生
Spring Bean的一生 When you work directly in Java, you can do anything you like with your objects and do ...
- spring揭秘 读书笔记 六 bean的一生
我们知道,Spring容器具有对象的BeanDefinition来保存该对象实例化时需要的数据. 对象通过container.getBean()方法是才会初始化该对象. BeanFactory 我们知 ...
- Bean的一生(Bean的生命周期)
1. 什么是Bean? Bean是spring中组成应用程序的主体及由spring IoC容器所管理的对象(IoC容器初始化.装配及管理的对象).如果把spring比作一座大型工厂,那么bean就是该 ...
- java-框架-索引
spring 整体了解 spring 入门demo Spring整体了解 spring梳理 Spring线程池的5个要素 spring的事务隔离级别以及传播性 事务4个隔离界别及脏读,不可重复读,幻读 ...
- spring 第一篇(1-2):管理你的beans
在基于spring的应用中,你的应用对象存活在spring container(容器中).容器创建,将它们装配到一起.还有配置和管理它们完整的生命周期(从生到死) 下一章节,你会看到如何配置Sprin ...
- Spring 初探(一) IoC 图集
Spring 框架总体结构 IoC 直观 Spring IoC容器 Spring提供两种容器类型: - BeanFactory - ApplicationContext ApplicationCont ...
- 关于Spring的IoC容器,你了解多少
IoC的基本概念 Ioc的全称是Inversion of Control,中文通常翻译为"控制反转".好莱坞原则"Dont't call us, we will ca ...
- Spring Bean生命周期,好像人的一生。。
大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像. 简单说说IoC和Bean IoC ...
- 扒一扒Bean注入到Spring的那些姿势,你会几种?
大家好,我是三友~~ 这篇文章我准备来扒一扒Bean注入到Spring的那些姿势. 其实关于Bean注入Spring容器的方式网上也有很多相关文章,但是很多文章可能会存在以下常见的问题 注入方式总结的 ...
随机推荐
- 云图说|云上应用监控神器——应用性能监控APM2.0
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 应用性能管理服务 ...
- 养殖场新来了个“AI管家”
摘要:定制化算法+端侧一键部署,打通AI全流程. 本文分享自华为云社区<[云享·伙伴]第4期:养殖场新来了个"AI管家">,作者: 华为云社区精选. 民以食为天.肉类是 ...
- Linux设置SSH连接时间,解决断开速度快,不停输密码问题: connection reset by
ssh 登录,没有设置ssh key 登录的情况下(临时登录),断开速度太快.如何解决? 修改:/etc/ssh/ssh_config 文件 #设置连接保持的时间ClientAliveInterval ...
- 火山引擎云原生数据仓库 ByteHouse 技术白皮书 V1.0 (Ⅴ)
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 近日,<火山引擎云原生数据仓库 ByteHouse 技术白皮书>正式发布.白皮书简述了 ByteHou ...
- PPT 图片框架排版万能能公式
图片作用 提升设计感 辅助表达 传递情感 如何选择一张高大上的图片? 星空.地球.城市.海洋.线条.粒子.山脉.壁纸(系统.手机厂商千挑万选的) https://cn.bing.com/images ...
- 【论文笔记#1】SPGAN-DA:用于领域自适应遥感图像语义分割的语义保留生成对抗网络
作者: Yansheng Li 发表年代: 2023 使用的方法: 无监督领域自适应(UDA).GAN.ClassMix.边界增强 来源: IEEE TGRS 方向: 语义分割 期刊层次: CCF B ...
- QE01/QA11/QA02屏幕增强
1.业务需求 需要对来料检验增加"合格数量"和"不合格数量"字段,涉及三个增强开发 2.QE01\QE02\QE03\QE51N屏幕增强 增强表 增强点BADI ...
- Codeforces 1326A Bad Ugly Numbers (思维)
Codeforces 1326A Bad Ugly Numbers 看完题目,第一直觉,质数肯定满足题意,再看数据范畴,\(1≤n≤10^5\), 质数线性筛仅能做到 n=7 的情况,即处理到1000 ...
- rabbitMq消息持久化机制,和延时队列
1.RabbitMQ的一大特色是消息的可靠性,那么它是如何保证消息可靠性的呢? 消息持久化.可以将Queue,Exchange,Message都设置为可持久化的.为了保证RabbitMQ在退出,服务重 ...
- location对象的方法
location.assign() 跟href一样,可以跳转页面(也称为重定向页面). location.replace() 替换当前页面,因为不记录历史,所以不能后退页面. location.rel ...