Spring中Bean的作用域、实例化方式、生命周期、循环依赖问题

Spring中Bean的作用域、实例化方式、生命周期、循环依赖问题

  • 一、Bean的作用域
    • 1.singleton
    • 2.prototype
    • 3.其他scope值
  • 二、Bean的实例化方式
    • 1.通过构造方法实例化
    • 2.通过简单工厂模式实例化
    • 3.通过factory-bean实例化
    • 4.通过FactoryBean接口实例化
    • 5.BeanFactory和FactoryBean的区别
      • (1)BeanFactory
      • (2)FactoryBean
  • 三、Bean的生命周期
    • 1.什么是Bean的生命周期
    • 2.为什么要知道Bean的生命周期
    • 3.Bean的生命周期之5步
    • 4.Bean生命周期之7步
    • 5.Bean生命周期之10步
    • 6.Bean的作用域不同,管理方式不同
    • 7.自己new的对象如何让Spring管理
  • 四、Bean的循环依赖问题
    • 1.什么是Bean的循环依赖
    • 2.singleton下的set注入产生的循环依赖
    • 3. prototype下的set注入产生的循环依赖
    • 4.singleton下的构造注入产生的循环依赖
    • 5.Spring解决循环依赖的机理


一、Bean的作用域

1.singleton

  • 默认情况下,Spring的IoC容器创建的Bean对象是单例的。
  • 默认情况下,Bean对象的创建是在初始化Spring上下文的时候就完成了。

2.prototype

  • 如果想让Spring的Bean对象以多例的形式存在,可以在bean标签中指定scope属性的值为:prototype,这样Spring会在每一次执行getBean()方法的时候创建Bean对象,调用几次则创建几次。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="sb" class="com.gdb.spring6.beans.SpringBean" scope="prototype" />
</beans>

3.其他scope值

  • scope属性的值不止两个,它一共包括8个选项:
    • singleton:默认的,单例。
    • prototype:原型。每调用一次getBean()方法则获取一个新的Bean对象。或每次注入的时候都是新对象。
    • request:一个请求对应一个Bean。仅限于在WEB应用中使用。
    • session:一个会话对应一个Bean。仅限于在WEB应用中使用。
    • global session:portlet应用中专用的。如果在Servlet的WEB应用中使用global session的话,和session一个效果。(portlet和servlet都是规范。servlet运行在servlet容器中,例如Tomcat。portlet运行在portlet容器中。)
    • application:一个应用对应一个Bean。仅限于在WEB应用中使用。
    • websocket:一个websocket生命周期对应一个Bean。仅限于在WEB应用中使用。
    • 自定义scope:很少使用。

二、Bean的实例化方式

  • Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)

1.通过构造方法实例化

  • 参考我的博客====>Spring对IoC的是实现中的Spring的第一个程序。

2.通过简单工厂模式实例化

  • 第一步:定义一个Bean
package com.gdb.spring6.bean;public class Vip {
}
  • 第二步:编写简单工厂模式当中的工厂类
package com.gdb.spring6.bean;public class VipFactory {public static Vip get(){return new Vip();}
}
  • 第三步:在Spring配置文件中指定创建该Bean的方法(使用factory-method属性指定)
<bean id="vipBean" class="com.gdb.spring6.bean.VipFactory" factory-method="get"/>

3.通过factory-bean实例化

  • 这种方式本质上是:通过工厂方法模式进行实例化。
  • 第一步:定义一个Bean
package com.gdb.spring6.bean;public class Vip {
}
  • 第二步:定义具体工厂类,工厂类中定义实例方法
    • 在这里可以在创建Bean的前后进行加工处理。
package com.gdb.spring6.bean;public class VipFactory {public Vip get(){return new Vip();}
}
  • 第三步:在Spring配置文件中指定factory-bean以及factory-method
<bean id="vipFactory" class="com.gdb.spring6.bean.VipFactory"/>
<bean id="vipBean" factory-bean="vipFactory" factory-method="get"/>

4.通过FactoryBean接口实例化

  • 以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。
  • 在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
  • factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。
  • 第一步:定义一个Bean
package com.gdb.spring6.bean;public class Vip {
}
  • 第二步:编写一个类实现FactoryBean接口
package com.gdb.spring6.bean;import org.springframework.beans.factory.FactoryBean;public class VipFactoryBean implements FactoryBean<Vip> {@Overridepublic Vip getObject() throws Exception {return new Vip ();}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {// true表示单例// false表示原型return true;}
}
  • 第三步:在Spring配置文件中配置FactoryBean
<bean id="vipBean" class="com.gdb.spring6.bean.VipFactoryBean"/>
  • FactoryBean在Spring中是一个接口。被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean。所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的。
  • 其实FactoryBean就是一个抽象工厂。
  • 通过FactoryBean这个工厂Bean主要是想对普通Bean进行加工处理。

5.BeanFactory和FactoryBean的区别

(1)BeanFactory

  • Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。
  • BeanFactory是工厂。

(2)FactoryBean

  • FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
  • 在Spring中,Bean可以分为两类:
    • 第一类:普通Bean
    • 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)

三、Bean的生命周期

1.什么是Bean的生命周期

  • Spring其实就是一个管理Bean对象的工厂。它负责对象的创建,对象的销毁等。
  • 所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。
  • 什么时候创建Bean对象?
  • 创建Bean对象的前后会调用什么方法?
  • Bean对象什么时候销毁?
  • Bean对象的销毁前后调用什么方法?

2.为什么要知道Bean的生命周期

  • 其实生命周期的本质是:在哪个时间节点上调用了哪个类的哪个方法。
  • 我们需要充分的了解在这个生命线上,都有哪些特殊的时间节点。
  • 只有我们知道了特殊的时间节点都在哪,到时我们才可以确定代码写到哪。
  • 我们可能需要在某个特殊的时间点上执行一段特定的代码,这段代码就可以放到这个节点上。当生命线走到这里的时候,自然会被调用。

3.Bean的生命周期之5步

  • Bean生命周期的管理,可以参考Spring的源码:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。
  • Bean生命周期可以粗略的划分为五大步:
    • 第一步:实例化Bean
    • 第二步:Bean属性赋值
    • 第三步:初始化Bean
    • 第四步:使用Bean
    • 第五步:销毁Bean
  • 需要注意的:
    • 第一:只有正常关闭spring容器,bean的销毁方法才会被调用。
    • 第二:ClassPathXmlApplicationContext类才有close()方法。
    • 第三:配置文件中的init-method指定初始化方法。destroy-method指定销毁方法。(初始化和销毁方法,需要自己在Bean中编写,然后在配置文件中进行配置)

4.Bean生命周期之7步

  • 在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。
  • 编写一个类实现BeanPostProcessor类,并且重写before和after方法:
package com.gdb.spring6.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class LogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的before方法执行,即将开始初始化");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的after方法执行,已完成初始化");return bean;}
}
  • 在spring.xml文件中配置“Bean后处理器”:
<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
  • 一定要注意:在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。

5.Bean生命周期之10步

在这里插入图片描述

6.Bean的作用域不同,管理方式不同

  • Spring 根据Bean的作用域来选择管理方式。
    • 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
    • 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
      * 对于 prototype 作用域Spring容器管理 Bean生命周期的前八步。

7.自己new的对象如何让Spring管理

  • 有些时候可能会遇到这样的需求,某个java对象是我们自己new的,然后我们希望这个对象被Spring容器管理,怎么实现?
package com.gdb.spring6.test;import com.gdb.spring6.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class RegisterBeanTest {@Testpublic void testBeanRegister(){// 自己new的对象User user = new User();System.out.println(user);// 创建 默认可列表BeanFactory 对象DefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 注册Beanfactory.registerSingleton("userBean", user);// 从spring容器中获取beanUser userBean = factory.getBean("userBean", User.class);System.out.println(userBean);}
}

四、Bean的循环依赖问题

1.什么是Bean的循环依赖

  • A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
    在这里插入图片描述

2.singleton下的set注入产生的循环依赖

  • 在singleton + setter模式注入的情况下,循环依赖是没有问题的。Spring可以解决这个问题。
    • 主要原因是:在这种模式下 Spring 对 Bean 的管理主要分为清晰的两个阶段:
      • 第一阶段:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行“曝光”【不等属性赋值曝光】。
      • 第二阶段:Bean“曝光”之后,再进行属性的赋值(调用set方法。)
    • 核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成的。

3. prototype下的set注入产生的循环依赖

  • 当循环依赖的所有Bean的scope="prototype"的时候,产生的循环依赖,Spring是无法解决的,会出现BeanCurrentlyInCreationException异常。
  • 如果其中一个是singleton,另一个是prototype,是没有问题的。

4.singleton下的构造注入产生的循环依赖

  • 因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。

5.Spring解决循环依赖的机理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 总结:
    • Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。

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

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

相关文章

Java基础-运算符,表达式和语句

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Java 运算符 算术运算符 关系运算符 位运算符 逻辑运算符 赋值运算符 条件运算符&#xff…

基于MVO优化的Bi-LSTM多输入时序预测(Matlab)多元宇宙算法优化长短期神经网络时序预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

05. Nginx入门-Nginx访问控制

测试环境 此处使用的yum安装的Nginx路径。 此处域名均在本地配置hosts。 主配置文件 路径&#xff1a;/etc/nginx/nginx.conf user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_connection…

计算机网络-物理层-传输媒体

传输媒体的分类 导向型-同轴电缆 导向型-双绞线 导向型-光纤 非导向型

H12-821_126

126.如图所示&#xff0c;在MA网络中&#xff0c;若要实现R1一定为DR&#xff0c;R2一定为BDR&#xff0c;R3&#xff0c;R4&#xff0c;R5不参与选举&#xff0c;那么R1的dr-priority最大为_____;R2的dr-priority________;R3的dr-priority为________;R4的dr-priority为_____;R…

WordPress建站入门教程:小皮面板phpstudy如何安装PHP和切换php版本?

小皮面板phpstudy支持的PHP版本有很多&#xff0c;包括5.2.17、5.3.29、5.4.45、5.5.9、5.6.9、7.0.9、7.1.9、7.2.9、7.3.4、7.3.9、7.4.3、8.0.2、8.2.9。那么我们如何安装其他的php版本和切换网站的php版本呢&#xff1f;只需要简单几步即可&#xff0c;具体如下&#xff1a…

C++ map用法

int main() {void *p;str *st;st (str*)malloc(sizeof(str));st->a 23;st->b 24;p st;//使用void指针需强制类型转换printf("%d\n%d\n",((str*)p)->a, ((str*)p)->b);free(st);map<char, int> mpci;mpci[m] 20;mpci.insert(pair<char, int…

Spring事务管理与模板对象

1.事务管理 1.事务回顾 事务指数据库中多个操作合并在一起形成的操作序列 事务的作用 当数据库操作序列中个别操作失败时&#xff0c;提供一种方式使数据库状态恢复到正常状态&#xff08;A&#xff09;&#xff0c;保障数据库即使在异常状态下仍能保持数据一致性&#xff…

C# WinForm AndtUI第三方库 Tree控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Tree控件至窗体。 使用方法集合 1.添加节点、子节点 using AntdUI; private void UpdateTreeView() {Tree tvwTestnew Tree();TreeItem rootTreeItem;TreeItem subTreeItem;Dictionary<str…

pdf怎么转换成word?这三种方法简单实用

pdf怎么转换成word&#xff1f;在日常工作和学习中&#xff0c;我们经常会遇到需要将PDF文件转换成Word文档的情况。PDF文件虽然方便阅读&#xff0c;但编辑起来却相对困难。而Word文档则更加灵活&#xff0c;方便我们对内容进行修改和排版。那么&#xff0c;如何将PDF转换成Wo…

2024年【烟花爆竹经营单位主要负责人】考试报名及烟花爆竹经营单位主要负责人新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 烟花爆竹经营单位主要负责人考试报名是安全生产模拟考试一点通总题库中生成的一套烟花爆竹经营单位主要负责人新版试题&#xff0c;安全生产模拟考试一点通上烟花爆竹经营单位主要负责人作业手机同步练习。2024年【烟…

【优选算法】前缀和

前缀和思想其实就是一种简单的dp思想&#xff0c;也就是动态规划 什么时候用到前缀和&#xff1f;当要快速求出数组中某一个区间的和 前缀和模板 暴力解法 定义一个指针从左向右遍历&#xff0c;并且累加值即可&#xff0c;这里就不过多赘述&#xff0c;主要还是来看前缀和…