Spring的Bean的生命周期与自动注入细节
1. Bean的生命周期
通过一个LifeCycleBean和一个MyBeanPostProcessor来观察Bean的生命周期:
构造(实例化)->依赖注入(前后处理)->初始化(前后处理)->销毁
LifeCycleBean
@Component
public class LifeCycleBean {private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);//1.最先执行public LifeCycleBean(){log.info("构造");}//2.依赖注入@Autowired
// public void autowire(@Value("${JAVA_HOME}")String home){ 这种方式好像要较新版本Spring才行
// log.debug("依赖注入:{}", home);
// }public void autowire(Bean1 bean1){log.info("依赖注入:{}", bean1);}//3.初始化@PostConstructpublic void init(){log.info("初始化");}//4.销毁方法(仅在单例的时候调用, 其他scope调用时机不一样)@PreDestroypublic void destroy(){log.info("销毁");}}
MyBeanPostProcessor, 实现了InstantiationAwareBeanPostProcessor
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);// 实例化之前@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.info("初始化之前执行, 这里返回的bean会替换原版本的bean");}return null;}// 实例化之后@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.info("实例化之后执行, 这里返回false会跳过依赖注入阶段");//return false;}return true;}// 依赖注入阶段执行@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if(beanName.equals("lifeCycleBean")){log.info("依赖注入阶段执行, 如@Autowired, @Value, @Resource");}return pvs;}// 初始化之前@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if(beanName.equals("lifeCycleBean")){log.info("初始化之前执行, 这里返回的对象会替换原本的bean, 如@PostConstruct, @ConfigurationProperties");}return bean;}// 初始化之后执行@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.info("初始化之后执行, 这里返回的对象会替换原本的bean, 如代理增强");}return bean;}// 销毁之前@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if(beanName.equals("lifeCycleBean")){log.info("销毁之前执行, 如 @PreDestroy");}}}
运行结果:
[main]: 初始化之前执行, 这里返回的bean会替换原版本的bean --- postProcessBeforeInstantiation
[main]: 构造
[main]: 实例化之后执行, 这里返回false会跳过依赖注入阶段 --- postProcessAfterInstantiation
[main]: 依赖注入阶段执行, 如@Autowired, @Value, @Resource --- postProcessProperties
[main]: 依赖注入:com.yadong.springsourcestudy.chapter2.Bean1@21c64522
[main]: 初始化之前执行, 这里返回的对象会替换原本的bean, 如@PostConstruct, @ConfigurationProperties --- postProcessBeforeInitialization
[main]: 初始化
[main]: 初始化之后执行, 这里返回的对象会替换原本的bean, 如代理增强 --- postProcessAfterInitialization
[main]: 销毁之前执行, 如 @PreDestroy --- postProcessBeforeDestruction
[main]: 销毁
2. @Autowired依赖注入的处理细节
依赖注入依靠AutowiredAnnotationBeanPostProcessor通过容器调用processor.postProcessProperties()执行依赖注入,
其过程是postProcessProperties()->InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs)
->metadata.inject(bean, beanName, pvs);
这个过程找了需要@Autowired的字段或方法后, 需要一些解析器来解析, 如 ContextAnnotationAutowireCandidateResolver, StandardEnvironment()::resolvePlaceholders
以下是一个手动调用解析@Autowired的过程:
public class AutowiredAnnotationBPP {public static void main(String[] args) throws Throwable {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); //解析@ValuebeanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); //解析${}beanFactory.registerSingleton("bean2", new Bean2());beanFactory.registerSingleton("bean3", new Bean3());//1. 查找有哪些属性方法加了@Autowire, 这称之为InjectionMetadataAutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();processor.setBeanFactory(beanFactory);Bean1 bean1 = new Bean1();Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);findAutowiringMetadata.setAccessible(true);InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);System.out.println("metadata = " + metadata);//2. metadata中存储了该bean要autowire的字段,方法等信息, 调用inject完成依赖注入metadata.inject(bean1, "bean1", null);System.out.println(bean1);//以下是要查找注入对象的过程Field bean2 = Bean1.class.getDeclaredField("bean2");DependencyDescriptor dd1 = new DependencyDescriptor(bean2, false);Object o = beanFactory.doResolveDependency(dd1, null, null, null);System.out.println(o);Method setBean2 = Bean1.class.getDeclaredMethod("setBean3", Bean3.class);DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);System.out.println(o1);}
}//Bean1.java
@Component
public class Bean1 {@AutowiredBean2 bean2;Bean3 bean3;@Autowiredpublic void setBean3(Bean3 bean3){this.bean3 = bean3;}
}
3. 常见后置处理器
-
ConfigurationClassPostProcessor
能解析@ComponentScan, @Bean, @Import, @ImportResource -
MapperScannerConfigurer
解析@MapperScanner, SSM中用, SpringBoot已经实现了自动装配因此用不上 -
AutowiredAnnotationBeanPostProcessor
解析@Autowire
…
4. 自实现ComponentScanPostProcessor
public class Chapter4Application {public static void main(String[] args) throws IOException {// 干净的容器GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);// 自己实现的, 解析@ComponentScancontext.registerBean(ComponentScanPostProcessor.class);context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}// 自己实现的ComponentScanPostProcessor
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {// 在 context.refresh()时, 读取bd后, 实例化bean前, 会调用这个方法@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {try{ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if(componentScan != null){for (String p : componentScan.basePackages()) {System.out.println(p);// com.yadong.springsourcestudy.chapter4 -> classpath*:com/yadong/springsourcestudy/chapter4/**/*.classString path = "classpath:*" + p.replace(".", "/") + "/**/*.class";CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); //读取资源的元信息System.out.println(path);Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {System.out.println(resource);MetadataReader metadataReader = factory.getMetadataReader(resource);ClassMetadata classMetadata = metadataReader.getClassMetadata();System.out.println("类名:" + classMetadata.getClassName());//hasMetaAnnotation间接继承的注解也会扫描到System.out.println("是否加了 @Component " + metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));if(metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())|| metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();DefaultListableBeanFactory beanFactory = null;if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;}String bdn = generator.generateBeanName(bd, beanFactory);beanFactory.registerBeanDefinition(bdn, bd);}}}}}catch (Exception e){e.printStackTrace();}}
}
相关文章:
Spring的Bean的生命周期与自动注入细节
1. Bean的生命周期 通过一个LifeCycleBean和一个MyBeanPostProcessor来观察Bean的生命周期: 构造(实例化)->依赖注入(前后处理)->初始化(前后处理)->销毁 LifeCycleBean Component public class LifeCycleBean {private static final Logger log LoggerFactory.g…...

谷粒商城:订单中心概念解析
1、订单中心 电商系统涉及到 3 流,分别时信息流,资金流,物流,而订单系统作为中枢将三者有机的集 合起来。 订单模块是电商系统的枢纽,在订单这个环节上需求获取多个模块的数据和信息,同时对这 些信息进行加…...

快递员配送手机卡,要求当面激活有“猫腻”吗?
咨询:快递员配送手机卡,要求当面激活有“猫腻”吗?有些朋友可能在网上看到了一些关于快递小哥激活会采集信息的文章,所以觉得让快递小哥激活流量卡并不安全,其实,哪有这么多的套路,只要你自己在…...

Sage X3 ERP的称重插件帮助食品和化工企业实现精细化管理
目录 需要称重插件管理的行业客户 Sage X3 ERP称重插件管理的两个关键单位 Sage X3 ERP称重插件的特色 Sage X3 ERP称重插件管理的重要性 需要称重插件管理的行业客户 术语“实际重量”表示在销售和运输时捕获的物品重量。生产销售家禽、肥料、钢材或任何其他需要跟踪实…...

【笔试强训】Day_01
目录 一、选择题 1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 二、编程题 1、组队竞赛 2、删除公共字符 一、选择题 1、 以下for循环的执行次数是() for(int x 0, y 0; (y 123) && (x < 4); x); A 、是无限循环 B 、循环次…...

字节跳动青训营--前端day9
文章目录前言PC web端一、 前端Debug的特点二、 前端Debug的方式1. 浏览器动态修改元素和样式2. Console3. Sorce Tab4. NetWork5. Application6. Performancee7. Lighthouse移动端调试IOSAndroid通过代理工具调试前言 仅以此文章记录学习。 PC web端 一、 前端Debug的特点 …...
如何把模糊的照片还原?
在我们工作和学习中,经常需要各种各样的照片,方便我们需要时可以使用。比如写文档就需要添加图片、或者上传文章、视频等都需要使用图片。由于网络上的图片质量都不一样,难免会遇到不能满足自己的需求。如果是遇到了模糊的照片,如…...

29-Golang中的切片
Golang中的切片基本介绍切片在内存中的形式切片使用的三种方式方式一:方式二:方式三:切片使用的区别切片的遍历切片注意事项和细节说明append函数切片的拷贝操作string和slice基本介绍 1.切片是数组的一个引用,因此切片是引用类型…...
闲聊一下开源
今天看了下中国开源开发者报告,感觉收货不少,针对里面的内容,我也加入一些自己的理解,写下来和大家一起闲聊一下。 AI 时至今日,我说一句AI已经在我国几乎各个行业都能找到应用,应该没人反对吧࿱…...

用这4招优雅的实现Spring Boot 异步线程间数据传递
Spring Boot 自定义线程池实现异步开发相信大家都了解,但是在实际开发中需要在父子线程之间传递一些数据,比如用户信息,链路信息等等 比如用户登录信息使用ThreadLocal存放保证线程隔离,代码如下: /*** author 公众号…...
RocketMQ源码分析之NameServer
1、RocketMQ组件概述 NameServer NameServer相当于配置中心,维护Broker集群、Broker信息、Broker存活信息、主题与队列信息等。NameServer彼此之间不通信,每个Broker与集群内所有的Nameserver保持长连接。 2、源码分析NameServer 本文不对 NameServer 与…...

如何优化认知配比
战略可以归结为三种要素的合理配比。我们对战略的一个定义是:在终局处的判断。这其实来自于一个宗教的命题——面死而生。死是终局,生是过程,当你想做一个思想实验,或者是你真的有缘能够直面死亡,你所有关于生的认知就…...
WuThreat身份安全云-TVD每日漏洞情报-2023-02-15
漏洞名称:TOTOLINK A7100RU 安全漏洞 漏洞级别:严重 漏洞编号:CVE-2023-24276,CNNVD-202302-367 相关涉及:TOTOlink A7100RU(V7.4cu.2313_B20191024)路由器 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-02977 漏洞名称:Windows NTLM 特权提…...

Unreal Engine角色涌现行为开发教程
在本文中,我将讨论如何使用虚幻引擎、强化学习和免费的机器学习插件 MindMaker 在 AI 角色中生成涌现行为。 目的是感兴趣的读者可以使用它作为在他们自己的游戏项目或具体的 AI 角色中创建涌现行为的指南。 推荐:使用 NSDT场景设计器 快速搭建 3D场景。…...

vue处理一千张图片进行分页加载
vue处理一千张图片进行分页加载 开发过程中,如果后端一次性返回你1000多条图片或数据,那我们前端应该怎么用什么思路去更好的渲染呢? 第一种:我们可以使用分页加载 第二种:我们可以进行懒加载那我们用第一种方法使用…...
(三十三)Vue之消息订阅与发布
文章目录消息订阅与发布理解使用步骤改造TodoList为消息订阅与发布上一篇:(三十二)Vue之全局事件总线 消息订阅与发布 理解 这种方式的思想与全局事件总线很相似,一种组件间通信的方式,适用于任意组件间通信。 它包…...

Http中你必须知道那点事
1, HTTP 1.1 简介 HTTP概念 HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。 数据传输的规则指的是请求数据和响应数据需要按照指定的格式进行传输。如果想知道具体的格式,可以打开浏览器…...

ViewBinding使用入门
ViewBinding 参考资料: 新技术 ViewBinding 最佳实践 & 原理击穿 更多 ViewBinding 的封装思路 1. kotlin-android-extensions(KAE) 的问题 根据Google官方的说法, KAE存在以下问题: 污染全局命名空间不能暴露可空性信息仅支持Kotlin代码 kotlin在1.4.20中 开始废弃这…...
Retrofit源码分析实践(七)【Retrofit ConvertFactory的功能实现】
Retrofit源码分析&实践系列文章目录 Retrofit源码分析&实践(一)【从使用入手分析源码】Retrofit源码分析&实践(二)【Retrofit 免费的api测试工具引入】Retrofit源码分析&实践(三)【Retrofit 代码框架搭建】Retrofit源码分析&实践(四)【Retrofit 实…...

介电常数常用测量方法综述
张扬1,徐尚志1,赵文晖2,龚增2,赵晓群1 1同济大学,上海 2上海市计量测试技术研究院,上海 在设计电路、天线、电容器等过程中经常会涉及所用材料的介电常数, 所以深入了解介电常数的相关概念对实际工作有重…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...