Spring Bean的生命周期解读

目录

1. Spring IOC容器

1.1 Spring IOC 容器的设计

1.1.1 BeanFactory

1.1.2 ApplicationContext

1.2 Spring Bean的生命周期

1.2.1 BeanDefinition

1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor

1.2.3 测试生命周期


1. Spring IOC容器

1.1 Spring IOC 容器的设计

Spring IOC 容器的设计主要是基于BeanFactoryApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,换句话说BeanFactory是Spring IOC容器所定义的最顶层接口,而ApplicationContext是其高级接口之一,并且对于BeanFactory功能做了许多有用的扩展,所以在绝大部分的工作场景中,都会使用ApplicationContext作为Spring IOC 容器,如下图所示:

 首先我们定义一个User实体类:

public class User {String username;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +'}';}
}

1.1.1 BeanFactory

BeanFactory的使用注册Bean对象以及获取Bean对象代码如下:

DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(User.class);beanFactory.registerBeanDefinition("user",rootBeanDefinition);System.out.println(beanFactory.getBean("user",User.class));

1.1.2 ApplicationContext

ApplicationContext的使用注册Bean对象以及获取Bean对象代码如下:

 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(User.class);applicationContext.refresh();System.out.println(applicationContext.getBean("user", User.class));

1.2 Spring Bean的生命周期

Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。

而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。

生命周期主要是为了了解Spring IOC容器初始化和销毁Bean的过程,通过对它的学习就可以知道如何在初始化和销毁的时候加入自定义的方法,以满足特定的需求。如下图:

从上图可以看到,Spring IoC容器对Bean 的管理还是比较复杂的,Spring loC容器在执行了初始化和依赖注入后,会执行一定的步骤来完成初始化,通过这些步骤我们就能自定义初始化,而在Spring IoC 容器正常关闭的时候,它也会执行一定的步骤来关闭容器,释放资源。除需要了解整个生命周期的步骤外,还要知道这些生命周期的接口是针对什么而言的,首先介绍生命周期的步骤。

1. 如果 Bean 实现了接口 BeanNameAware的setBeanName方法,那么它就会调用这个方法。
2. 如果 Bean 实现了接口 BeanFactoryAware 的 setBeanFactory方法,那么它就会调用这个方法。
3. 如果 Bean实现了接口 ApplicationContextAware 的 setApplicationContext方法,且
Spring loC容器也必须是一个ApplicationContext 接口的实现类,那么才会调用这个方法,否则是不调用的。
4. 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessBeforeInitialization方法,那么它就会调用这个方法。

5 .如果 Bean实现了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就会调用这个方法。
6. 如果 Bean自定义了初始化方法,它就会调用已定义的初始化方法。
7. 如果Bean 实现了接口 BeanPostProcessor 的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean 就完成了初始化,那么 Bean就生存在Spring loC的容器中了,使用者就可以从中获取 Bean的服务。
8. 当服务器正常关闭,或者遇到其他关闭 Spring loC 容器的事件,它就会调用对应的方完成Bean 的销毁,其步骤如下:
        如果Bean实现了接口 DisposableBean 的 destroy方法,那么就会调用它。
        如果定义了自定义的销毁方法,那么就会调用它。

1.2.1 BeanDefinition

Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构。不管是是通过xml配置文件的<Bean>标签,还是通过注解配置的@Bean,还是@Compontent标注的类,还是扫描得到的类,它最终都会被解析成一个BeanDefinition对象,最后我们的Bean工厂就会根据这份Bean的定义信息,对bean进行实例化、初始化等等操作。

你可以把BeanDefinition丢给Bean工厂,然后Bean工厂就会根据这个信息帮你生产一个Bean实例,拿去使用。

BeanDefinition里面里面包含了bean定义的各种信息,如:bean对应的class、scope、lazy信息、dependOn信息、autowireCandidate(是否是候选对象)、primary(是否是主要的候选者)等信息。

 RootBeanDefinition类:表示根bean定义信息,通常bean中没有父bean的就使用这种表示。

ChildBeanDefinition类:表示子bean定义信息,如果需要指定父bean的,可以使用ChildBeanDefinition来定义子bean的配置信息,里面有parentName属性,用来指定父bean的名称。

GenericBeanDefinition类:通用的bean定义信息,既可以表示没有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,这个类里面也有parentName属性,用来指定父bean的名称。

ConfigurationClassBeanDefinition类:表示通过配置类中@Bean方法定义bean信息

可以通过配置类中使用@Bean来标注一些方法,通过这些方法来定义bean,这些方法配置的bean信息最后会转换为ConfigurationClassBeanDefinition类型的对象。

AnnotatedBeanDefinition接口:表示通过注解的方式定义的bean信息。

BeanDefinitionBuilder:构建BeanDefinition的工具类

1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor

InstantiationAwareBeanPostProcessor实际上继承了BeanPostProcessor接口。InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。如下图:

 BeanPostProcessor是一个接口,还有很多子接口,这些接口中提供了很多方法,spring在bean生命周期的不同阶段,会调用BeanPostProcessor中的一些方法,来对生命周期进行扩展,bean生命周期中的所有扩展点都是依靠这个集合中的BeanPostProcessor来实现的。该接口提供了两个函数:postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。postProcessAfterInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。


Spring Aware是Spring定义的回调接口。何为回调?就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。

1.2.3 测试生命周期

我们自定义一个User实体类,要求Spring容器使用我们自定义的@MyAutowired注解标注的构造方法进行构造Bean对象,然后我们观察在Bean周期的日志打印,更好的理解Bean周期过程。

自定义@MyAutowired注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.CONSTRUCTOR)
public @interface MyAutowired {
}

定义User实体类

public class User implements InitializingBean, DisposableBean {String username;//Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,// 该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。// init-method本质上仍然使用了InitializingBean接口。public void init(){System.out.println(this.getClass().getSimpleName()+" 执行自定义初始化方法");}public User() {}@MyAutowiredpublic User(String username) {this.username = username;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +'}';}@Overridepublic void destroy() throws Exception {System.out.println("调用DisposableBean接口的destroy方法");}//afterPropertiesSet()这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,// 但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,// 只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,把要增加的逻辑写在该函数中。// 然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("调用afterPropertiesSet方法");}
}

测试代码

public class MyTest {public static void main(String[] args) {DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();//InstantiationAwareBeanPostProcessor接口在Bean对象实例化前的方法beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if(beanName.equals("user"))System.out.println(beanName+" Bean对象在实例化之前操作**");return null;}});//InstantiationAwareBeanPostProcessor接口在Bean对象实例化后的方法beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if(beanName.equals("user"))System.out.println(beanName+" Bean对象在实例化之后操作**");return false;}});//使用自己自定义含有@MyAutowired注解的构造方法,实例化Bean对象beanFactory.addBeanPostProcessor(new SmartInstantiationAwareBeanPostProcessor() {public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {if(beanName.equals("user"))System.out.println("实例化Bean对象");Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();List<Constructor<?>> collect = Arrays.stream(declaredConstructors).filter(i -> i.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList());Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);return constructors.length>0?constructors:null;}});//BeanPostProcessor接口在Bean对象初始化之前的方法调用beanFactory.addBeanPostProcessor(new BeanPostProcessor() {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if(beanName.equals("user"))System.out.println(beanName+" Bean对象在初始化之前操作**");return null;}});//BeanPostProcessor接口在Bean对象初始化之后的方法调用beanFactory.addBeanPostProcessor(new BeanPostProcessor() {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if(beanName.equals("user"))System.out.println(beanName+" Bean对象在初始化之后操作**");return null;}});RootBeanDefinition rootBeanDefinition= (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(User.class).setInitMethodName("init").getBeanDefinition();beanFactory.registerBeanDefinition("user",rootBeanDefinition);beanFactory.registerBeanDefinition("username", BeanDefinitionBuilder.genericBeanDefinition(String.class).addConstructorArgValue("admin").getBeanDefinition());System.out.println(beanFactory.getBean("user",User.class));beanFactory.destroySingletons();}
}

测试截图

至此这篇文章到此结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/1928.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络那些事之 MTU 篇 pt.2

哈喽大家好&#xff0c;我是咸鱼 在《计算机网络那些事之 MTU 篇 》中&#xff0c;咸鱼跟大家介绍了 MTU 是指数据链路层能够传输的最大数据帧的大小 如果发送的数据大于 MTU&#xff0c;则就会进行分片操作&#xff08;Fragment&#xff09;&#xff1b;如果小于 MTU&#x…

SpringBoot整合Redis缓存管理

1. 添加 Spring Data Redis 依赖启动器。在 chapter06 项目的 pom.xml 文件中添加 Spring Data Redis 依赖 启动器。 <!-- 引入整合 Redis 缓存的依赖启动器 --> <dependency> <groupId> org.springframework.boot </groupId> <artifactId>…

Java安全——应用安全

Java安全 Java 应用安全 JCE&#xff08;Java Cryptography Extension&#xff09;java加密扩展包 Java Cryptography Extension&#xff08;JCE&#xff09;是一个可选的Java标准扩展&#xff0c;提供了一组用于加密、密钥生成和密钥协商等功能的类和接口。JCE包含了导入、生…

android checkBox的使用

一、前言&#xff1a;之前工作中遇到的checkbox的使用是左边一个复选框&#xff0c;右边一个text。系统学完之后发现那样做的话有点别扭&#xff0c;还是中规中矩的舒坦。记录一下学习经过。 二、代码展示&#xff1a; 1.使用系统自带的checkbox插件。 创建一个CheckBoxActiv…

Solr框架 01 Solr框架简介,安装,配置(Analysis,Dataimport)

Solr简介&#xff1a; Solr是一个高性能&#xff0c;基于Lucene的全文搜索服务器。同时对其进行了扩展&#xff0c;提供了比Lucene更为丰富的查询语言&#xff0c;同时实现了可配置、可扩展&#xff0c;并对查询性能进行了优化&#xff0c;并且提供了一个完善的功能管理界面&am…

CentOs中文件权限命令

文件权限&#xff1a; ls -l命令查看文件详情&#xff0c;前十位就是文件的类型和权限 第一位&#xff1a;类型&#xff1a; - 普通文件 d 目录 l 链接文件&#xff08;快捷方式&#xff09;link 2~4位&#xff1a;所有者的权限 5~7位&#xff1a;所有者所在组其它用户的权限 …

2021 年高教社杯全国大学生数学建模竞赛 E 题 中药材的鉴别 第一题

目录 1.数据预处理 1.1 数据基本信息探索 1.2 数据可视化 1.3 异常值处理 2. 数据特征值提取 2.1 数据标准化 2.2 PCA提取特征值 3. 数据聚类鉴别药材种类 3.1 肘部图确定K值 3.2 轮廓系数图确定K值 3.3 数据聚类 3.4 聚类结果可视化 4. 研究不同种类药材…

【案例实战】SpringBoot整合Redis实现缓存分页数据查询

正式观看本文之前&#xff0c;设想一个问题&#xff0c;高并发情况下&#xff0c;首页列表数据怎么做&#xff1f; 类似淘宝首页&#xff0c;这些商品是从数据库中查出来的吗&#xff1f;答案肯定不是&#xff0c;在高并发的情况下&#xff0c;数据库是扛不住的&#xff0c;那么…

Redis分布式问题

Redis实现分布式锁 Redis为单进程单线程模式&#xff0c;采用队列模式将并发访问变成串行访问&#xff0c;且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。当且仅当 key 不存在&#xff0c;将 key 的值设为 value。 若给定的 key 已经存在&…

『赠书活动 | 第十三期』《算力经济:从超级计算到云计算》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第十三期』 本期书籍&#xff1a;《算力经济&#xff1a;从超级计算到云计算》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff5c;收…

全志V3S嵌入式驱动开发(开发环境再升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们陆陆续续开发了差不多有10个驱动&#xff0c;涉及到网口、串口、音频和视频等几个方面。但是整个开发的效率还是比较低的。每次开发调试的…

Matlab论文插图绘制模板第105期—带缺口的分组填充箱线图

在之前的文章中&#xff0c;分享了Matlab带缺口的分组箱线图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下带缺口的分组填充箱线图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自…