当前位置: 首页 > news >正文

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. 常见后置处理器

  1. ConfigurationClassPostProcessor 能解析@ComponentScan, @Bean, @Import, @ImportResource

  2. MapperScannerConfigurer 解析@MapperScanner, SSM中用, SpringBoot已经实现了自动装配因此用不上

  3. 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 流&#xff0c;分别时信息流&#xff0c;资金流&#xff0c;物流&#xff0c;而订单系统作为中枢将三者有机的集 合起来。 订单模块是电商系统的枢纽&#xff0c;在订单这个环节上需求获取多个模块的数据和信息&#xff0c;同时对这 些信息进行加…...

快递员配送手机卡,要求当面激活有“猫腻”吗?

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

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循环的执行次数是&#xff08;&#xff09; 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的特点 …...

如何把模糊的照片还原?

在我们工作和学习中&#xff0c;经常需要各种各样的照片&#xff0c;方便我们需要时可以使用。比如写文档就需要添加图片、或者上传文章、视频等都需要使用图片。由于网络上的图片质量都不一样&#xff0c;难免会遇到不能满足自己的需求。如果是遇到了模糊的照片&#xff0c;如…...

29-Golang中的切片

Golang中的切片基本介绍切片在内存中的形式切片使用的三种方式方式一&#xff1a;方式二&#xff1a;方式三&#xff1a;切片使用的区别切片的遍历切片注意事项和细节说明append函数切片的拷贝操作string和slice基本介绍 1.切片是数组的一个引用&#xff0c;因此切片是引用类型…...

闲聊一下开源

今天看了下中国开源开发者报告&#xff0c;感觉收货不少&#xff0c;针对里面的内容&#xff0c;我也加入一些自己的理解&#xff0c;写下来和大家一起闲聊一下。 AI 时至今日&#xff0c;我说一句AI已经在我国几乎各个行业都能找到应用&#xff0c;应该没人反对吧&#xff1…...

用这4招优雅的实现Spring Boot 异步线程间数据传递

Spring Boot 自定义线程池实现异步开发相信大家都了解&#xff0c;但是在实际开发中需要在父子线程之间传递一些数据&#xff0c;比如用户信息&#xff0c;链路信息等等 比如用户登录信息使用ThreadLocal存放保证线程隔离&#xff0c;代码如下&#xff1a; /*** author 公众号…...

RocketMQ源码分析之NameServer

1、RocketMQ组件概述 NameServer NameServer相当于配置中心&#xff0c;维护Broker集群、Broker信息、Broker存活信息、主题与队列信息等。NameServer彼此之间不通信&#xff0c;每个Broker与集群内所有的Nameserver保持长连接。 2、源码分析NameServer 本文不对 NameServer 与…...

如何优化认知配比

战略可以归结为三种要素的合理配比。我们对战略的一个定义是&#xff1a;在终局处的判断。这其实来自于一个宗教的命题——面死而生。死是终局&#xff0c;生是过程&#xff0c;当你想做一个思想实验&#xff0c;或者是你真的有缘能够直面死亡&#xff0c;你所有关于生的认知就…...

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角色涌现行为开发教程

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

vue处理一千张图片进行分页加载

vue处理一千张图片进行分页加载 开发过程中&#xff0c;如果后端一次性返回你1000多条图片或数据&#xff0c;那我们前端应该怎么用什么思路去更好的渲染呢&#xff1f; 第一种&#xff1a;我们可以使用分页加载 第二种&#xff1a;我们可以进行懒加载那我们用第一种方法使用…...

(三十三)Vue之消息订阅与发布

文章目录消息订阅与发布理解使用步骤改造TodoList为消息订阅与发布上一篇&#xff1a;&#xff08;三十二&#xff09;Vue之全局事件总线 消息订阅与发布 理解 这种方式的思想与全局事件总线很相似&#xff0c;一种组件间通信的方式&#xff0c;适用于任意组件间通信。 它包…...

Http中你必须知道那点事

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

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

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

vue3 daterange正则踩坑

<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...