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上海市计量测试技术研究院,上海 在设计电路、天线、电容器等过程中经常会涉及所用材料的介电常数, 所以深入了解介电常数的相关概念对实际工作有重…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
