Spring(11) Bean的生命周期

目录

    • 一、简介
    • 二、Bean的流程
      • 1.BeanDefinition
      • 2.Bean 的生命周期
    • 三、代码验证
      • 1.User 实体类
      • 2.MyBeanPostProcessor 后置处理器
      • 3.SpringConfig 扫描包配置
      • 4.UserTest 测试类
      • 5.测试结果
      • 6.模拟AOP增强
    • 三、总结

一、简介

首先,为什么要学习 Spring 中 Bean 的生命周期呢?

虽然不了解 Bean 的生命周期,并不影响日常工作中的开发。但是如果我们了解了 Bean 的生命周期,可以帮助我们更好地掌握 Spring 框架,并且能够让我们更好地去理解 Spring 容器是如何管理和创建 Bean 示例的。如果以后遇到 Bean 的相关问题(比如 Spring 的循环依赖问题),就可以方便我们去调试和解决这些问题。同时也让我们可以编写出更健壮、灵活、易维护的应用程序。所以说,理解 Bean 的生命周期对掌握 Spring 框架来说是至关重要的。

这里我们主要分两部分去说明:

  1. 先从整体上去介绍 Bean 创建的整个流程 -> Bean的流程;
  2. 然后再用代码的方式去验证流程 -> 代码验证。

二、Bean的流程

1.BeanDefinition

BeanDefinition:Bean 的定义信息。Spring 容器在进行实例化时,会将 xml 配置中 <bean> 的信息(注解也是如此)封装成一个 BeanDefinition 对象,Spring 根据 BeanDefinition 来创建 Bean 对象,里面有很多用来描述 Bean 的属性。

有了 BeanDefinition 之后,再去创建 Bean 对象的时候,就可以通过反射去创建。

<bean id="userDao" class="com.demo.dao.impl.UserDaoImpl" lazy-init="true"/>
<bean id="userService" class="com.demo.service.UserServiceImpl" scope="singleton"><property name="userDao" ref="userDao"></property>
</bean>
  • beanClassName:bean 的类名。
  • initMethodName:初始化方法名称。
  • propertyValues:bean 的属性值。
  • scope:作用域。
  • lazyInit:延迟初始化。

以上这些属性都被封装到 BeanDefinition 中备用。那具体什么时候用呢?这就是下面要说的 Bean 的生命周期。

2.Bean 的生命周期

目前我们已经有了 BeanDefinition 这个对象了,那第一步就是要去创建对象。

在这里插入图片描述

  • 第1步,调用 Bean 的构造函数。 来去实例化当前 Bean 的对象。

  • 第2步,依赖注入。 像是一些 @Autowired 或者 @Value 标明的属性,这些都是在依赖注入中完成。

  • 第3步,Aware接口。 比较常见的有三个,它们都是以 Aware 结尾的接口,如果 Bean 实现了这些接口,就要重写里面的方法:

    BeanNameAware:在初始化过程中可以获取到 Bean 的名称。

    BeanFactoryAware:在初始化过程中可以获取到 Bean 工厂。

    ApplicationContextAware:在初始化过程中可以获取到应用上下文。

    以上这些内容都是为了方便对 Bean 进行扩展。

  • 第4步,BeanPostProcessor#before。 BeanPostProcessor 是 Bean 的后置处理器,用来增强 Bean 的功能的,在初始化方法调用之前(before)进行回调。这个 BeanPostProcessor 后置处理器在 Bean 的生命中其中占有非常重要的作用,需要重点记忆。

  • 第5步,调用初始化方法。 里面有两部分:

    InitializingBean:这是一个接口类,如果当前 Bean 实现了这个接口的话,就要重写里面的方法,这一步就是来执行重写之后的方法。

    自定义init方法:比如在 Bean 中的某一个方法使用了注解 @PostConstruct,这里就会去执行标明了注解的方法。其实自定义init方法也是从 BeanDefinition 中读取到的信息。

  • 第6步,BeanPostProcessor#after。 还是在 Bean 的后置处理器中执行,但是是在初始化方法之后(after)执行。在 Spring 中对 Bean 进行增强的话,都是用到这个初始化方法之后执行的后置处理器。其实在 Spring 内部就使用到了很多的后置处理器,比较典型的就是:当一个类被曾倩了,使用到了 AOP,那这个类通常都是使用后置处理器(BeanPostProcessor#after)来增强的。我们知道 AOP 的底层使用的是动态代理,有两种:JDK动态代理CGLIB动态代理

    到这一步之后,基本上这个对象就创建完成了,现在就可以从 Spring 容器中去获取和使用这个 Bean 对象了。

    注意:依赖注入 开始到 BeanPostProcessor#after 完成之后,都是针对当前的 Bean 进行初始化赋值,当然也做了一些增强。这里需要注意,Spring 中 Bean 的创建是一步一步完成的,也就是说 Bean 的创建和初始化赋值是分开的。调用构造函数就是创建 Bean 对象,但是这里创建好后是一个空对象,里面没有值,下面就是初始化赋值的过程。

  • 第6步,销毁Bean。 当 Spring 容器关闭之后,这个 Bean 对象就要执行销毁的操作了。比较典型的是,如果在某个方法上使用了 @PreDestroy 这个注解,那这个方法就是一个销毁的方法,Spring 容器关闭的时候就会执行这个销毁的方法。

以上就是 Bean 的生命周期,里面的步骤还是挺多的。为了方便加深印象,我们可以用代码的方式去验证一下。

三、代码验证

1.User 实体类

User.java

继承了 BeanNameAwareBeanFactoryAwareApplicationContextAwareInitializingBean 接口。

package com.demo.lifecycle;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;@Component
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {public User() {System.out.println("User类的构造方法被调用了...");}/*** 姓名*/private String name;@Value("张三")public void setName(String name) {System.out.println("setName方法被调用了...");this.name = name;}@Overridepublic void setBeanName(String name) {System.out.println("BeanNameAware接口的setBeanName方法被调用了,bean的名字是:" + name);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("BeanFactoryAware接口的setBeanFactory方法被调用了...");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("ApplicationContextAware接口的setApplicationContext方法被调用了...");}@Overridepublic void afterPropertiesSet() {System.out.println("InitializingBean接口的afterPropertiesSet方法被调用了...");}@PreDestroypublic void destroy() {System.out.println("PreDestroy注解的destroy方法被调用了...");}
}

2.MyBeanPostProcessor 后置处理器

MyBeanPostProcessor.java

定义了 #before#after 方法的增强实现。

package com.demo.lifecycle;import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Objects;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("BeanPostProcessor接口的postProcessBeforeInitialization方法被调用了 -> user对象初始化方法前开始增强....");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (Objects.equals(beanName, "user")) {System.out.println("BeanPostProcessor接口的postProcessAfterInitialization方法被调用了 -> user对象初始化方法后开始增强....");// cglib代理对象/*Enhancer enhancer = new Enhancer();// 设置需要增强的类enhancer.setSuperclass(bean.getClass());// 执行回调方法,增强方法enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {// 执行目标方法return method.invoke(bean, objects);}});// 创建代理对象return enhancer.create();*/}return bean;}
}

3.SpringConfig 扫描包配置

SpringConfig.java

配置了扫描包位置,只扫描 User 实体类和 MyBeanPostProcessor 后置处理器。

package com.demo.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("com.demo.lifecycle")
public class SpringConfig {
}

4.UserTest 测试类

UserTest.java

main 方法执行,使用 SpringConfig 配置创建上下文,并从容器中获取 user 的 Bean 对象。

package com.demo.test;import com.demo.config.SpringConfig;
import com.demo.lifecycle.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class UserTest {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);User user = ctx.getBean(User.class);System.out.println(user);}
}

5.测试结果

由于我使用的是 SpringBoot 项目进行的测试,会多打印一些日志,其中不带时间前缀的是我们手动 println 的日志。

Connected to the target VM, address: '127.0.0.1:61642', transport: 'socket'
20:03:56.949 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@9660f4e
20:03:56.985 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
20:03:57.100 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\IdeaProjects\SpringBootExamples\springboot-demo\target\classes\com\demo\lifecycle\MyBeanPostProcessor.class]
20:03:57.105 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\IdeaProjects\SpringBootExamples\springboot-demo\target\classes\com\demo\lifecycle\User.class]
20:03:57.266 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
20:03:57.270 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
20:03:57.272 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
20:03:57.275 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
20:03:57.281 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myBeanPostProcessor'
20:03:57.304 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'springConfig'
BeanPostProcessor接口的postProcessBeforeInitialization方法被调用了 -> user对象初始化方法前开始增强....
20:03:57.305 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
User类的构造方法被调用了...
setName方法被调用了...
BeanNameAware接口的setBeanName方法被调用了,bean的名字是:user
BeanFactoryAware接口的setBeanFactory方法被调用了...
ApplicationContextAware接口的setApplicationContext方法被调用了...
BeanPostProcessor接口的postProcessBeforeInitialization方法被调用了 -> user对象初始化方法前开始增强....
InitializingBean接口的afterPropertiesSet方法被调用了...
BeanPostProcessor接口的postProcessAfterInitialization方法被调用了 -> user对象初始化方法后开始增强....
User类的构造方法被调用了...
com.demo.lifecycle.User@2235eaab
Disconnected from the target VM, address: '127.0.0.1:61642', transport: 'socket'Process finished with exit code 0

可以看到日志是按照我们梳理的 Bean 的生命周期顺序打印的,验证完毕。

6.模拟AOP增强

MyBeanPostProcessor.javapostProcessAfterInitialization() 方法中注释了 AOP 底层 CGLIB 动态代理的演示代码的,可以在 UserTestmain 方法中获取到 user 的 Bean 对象后,将鼠标放到 user 上看下,打开和关闭 CGLIB 代理的注释生成的 user Bean 会不同:

关闭 CGLIB 代理:

在这里插入图片描述

打开 CGLIB 代理:

在这里插入图片描述

三、总结

问: Spring 中 Bean 的生命周期是什么样的?

答:

① 通过 BeanDefinition 获取 Bean 的定义信息;
② 调用构造函数实例化 Bean;
③ Bean 的依赖注入;
④ 处理 Aware 接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware);
⑤ Bean 的后置处理器 BeanPostProcessor-前置;
⑥ 初始化方法(InitializingBean、init-method);
⑦ Bean 的后置处理器 BeanPostProcessor-后置;
⑧ 销毁 Bean。

整理完毕,完结撒花~ 🌻





参考地址:

1.框架篇-05-Spring-bean的生命周期,https://www.bilibili.com/video/BV1yT411H7YK/?p=39&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=cf16f2e5f7c4612add10f9f9268a2c8a

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

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

相关文章

腾讯云轻量应用服务器端口打开方法

腾讯云轻量应用服务器端口放行在哪设置&#xff1f;在防火墙中可以开启端口号&#xff0c;腾讯云轻量应用服务器端口怎么开通&#xff1f;在轻量服务器管理控制台的防火墙中开启端口&#xff0c;如果是CVM云服务器在安全组中开通&#xff0c;腾讯云服务器网以轻量应用服务器开通…

网络安全进阶学习第十二课——SQL手工注入3(Access数据库)

文章目录 注入流程&#xff1a;1、判断数据库类型2、判断表名3、判断列名4、判断列数1&#xff09;判断显示位 5、判断数据长度6、爆破数据内容 注入流程&#xff1a; 判断数据库类型 ——> 判断表名 ——> 判断列名 ——> 判断列名长度 ——> 查出数据。 asp的网…

Python:Spider爬虫工程化入门到进阶(1)创建Scrapy爬虫项目

Python&#xff1a;Spider爬虫工程化入门到进阶系列: Python&#xff1a;Spider爬虫工程化入门到进阶&#xff08;1&#xff09;创建Scrapy爬虫项目Python&#xff1a;Spider爬虫工程化入门到进阶&#xff08;2&#xff09;使用Spider Admin Pro管理scrapy爬虫项目 本文通过简…

MES系统在机器人行业生产管理种的运用

机器人的智能水平也伴随技术的迭代不断攀升。 2021年的春晚舞台上&#xff0c;来自全球领先工业机器人企业abb的全球首款双臂协作机器人yumi&#xff0c;轻松自如地表演了一出写“福”字&#xff0c;赢得了全国观众的赞叹。 在汽车装配领域&#xff0c;一台机器人可以自主完成一…

ElasticSearch:项目实战(1)

es环境搭建参考&#xff1a;ElasticSearch&#xff1a;环境搭建步骤_Success___的博客-CSDN博客 需求&#xff1a; 用户输入关键可搜索文章列表 关键词高亮显示 文章列表展示与home展示一样&#xff0c;当用户点击某一篇文章&#xff0c;可查看文章详情 思路&#xff1a; …

《Java-SE-第三十三章》之函数式编程

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…

webpack复习

webpack webpack复习 webpack基本配置 拆分配置 - 公共配置 生产环境配置 开发环境配置 使用merge webpack-dev-server 启动本地服务 在公共中引入babel-loader处理es6 webpack高级配置 多入口文件 enty 入口为一个对象 里面的key为入口名 value为入口文件路径 例如 pa…

xxx酒业有限责任公司突发环境事件应急预案WORD

导读&#xff1a;原文《xxx酒业有限责任公司突发环境事件应急预案word》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 目 录 1 总则 1.1 编制目的 1.2 编制…

【Git】 git push origin master Everything up-to-date报错

hello&#xff0c;我是索奇&#xff0c;可以叫我小奇 git push 出错&#xff1f;显示 Everything up-to-date 那么看看你是否提交了message 下面是提交的简单流程 git add . git commit -m "message" git push origin master 大多数伙伴是没写git commit -m "…

在elementUI的表格(table)内嵌入svg图标

参考文档&#xff1a; https://element.eleme.cn/#/zh-CN/component/table demo效果图如下&#xff08;在表格的类型列中添加一个对应类型的svg图标&#xff09;&#xff1a; 本文主要关注以下两点&#xff1a; elementUI的表格&#xff08;table&#xff09;的自定义列模…

用python做一个小游戏代码,用python制作一个小游戏

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;如何用python编写一个简单的小游戏&#xff0c;用python做一个小游戏代码&#xff0c;今天让我们一起来看看吧&#xff01; 今天呢&#xff0c;给大家展示一下Python有趣的小地方&#xff0c;展示给大家看看&#xff0c…

QMainwindow窗口

QMainwindow窗口 菜单栏在二级菜单中输入中文的方法给菜单栏添加相应的动作使用QMenu类的API方法添加菜单项分隔符也是QAction类 工具栏状态栏停靠窗口 菜单栏 只能有一个, 位于窗口的最上方 关于顶级菜单可以直接在UI窗口中双击, 直接输入文本信息即可, 对应子菜单项也可以通…

【C++学习手札】new和delete看这一篇就够了!

​ 食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C类 ♈️今日夜电波&#xff1a; Prover—milet 1:21 ━━━━━━️&#x1f49f;──────── 4:01 …

安卓如何卸载应用

卸载系统应用 首先需要打开手机的开发者选项&#xff0c;启动usb调试。 第二步需要在电脑上安装adb命令&#xff0c;喜欢的话还可以将它加入系统path。如果不知道怎么安装&#xff0c;可以从这里下载免安装版本。 第三步将手机与电脑用数据线连接&#xff0c;注意是数据线&a…

Linux mysql5.7开启 binlog

查看 mysql是否开启 binlog。 查看命令&#xff1a; show variables like %log_bin%; log_bin OFF 是关闭的状态。 编辑my.cnf配置文件 vim /etc/my.cnf 默认的配置文件内容&#xff1a; 增加下面内容 server_id 1 binlog_format ROW log-bin mysql_log_bin 重启mysq…

C++友元函数和友元类的使用

1.友元介绍 在C中&#xff0c;友元&#xff08;friend&#xff09;是一种机制&#xff0c;允许某个类或函数访问其他类的私有成员。通过友元&#xff0c;可以授予其他类或函数对该类的私有成员的访问权限。友元关系在一些特定的情况下很有用&#xff0c;例如在类之间共享数据或…

pdf怎么删除不要的页面?这几种删除方法了解一下

pdf怎么删除不要的页面&#xff1f;在处理pdf文档时&#xff0c;我们经常会遇到需要删除某些页面的情况。一些多余或无关的页面可能会对文档的整体结构造成混乱&#xff0c;甚至会影响文档的可读性。此外&#xff0c;删除多余页面还可以减小文件大小&#xff0c;方便存储和传输…

每天五分钟机器学习:梯度下降算法和正规方程的比较

本文重点 梯度下降算法和正规方程是两种常用的机器学习算法,用于求解线性回归问题。它们各自有一些优点和缺点,下面将分别对它们进行详细的讨论。 区别 1. 梯度下降算法是一种迭代的优化算法,通过不断迭代调整参数来逼近最优解。它的基本思想是根据目标函数的梯度方向,沿…

springboot房地产管理java购房租房二手房j客户sp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 springboot房地产管理 系统1权限&#xff1a;管理员 …

基于SLAM的规划算法仿真复现|SLAM|智能规划

图片来自百度百科 前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总https://blog.csdn.n…