spring(3)

spring6

  • 1、bean生命周期
    • 1.1 bean生命周期之五步
    • 1.2bean生命周期之七步
    • 1.3 bean生命周期之十步
    • 1.4 bean作用域与管理周期
  • 2、把自己new的对象交给spring管理
  • 3、Bean循环依赖
    • 3.1 set+singleton
    • 3.2 构造+singleton
    • 3.3 propotype+set注入
    • 3.4 bean循环依赖源码分析:
    • 3.5 常见面试问题

1、bean生命周期

1.1 bean生命周期之五步

第一步:实例化Bean
第二步:Bean属性赋值
第三步:初始化Bean
第四步:使用Bean
第五步:销毁Bean
注意 初始化bean和销毁bean 都是需要我们在类中自己写,自己配置到xml文件中的。名字随便写。

package com.cky.bean;

public class User {
    private String name;

    public User() {
        System.out.println("第一步 无参构造执行");
    }

    public void setName(String name) {
        System.out.println("第二步 属性配置");
        this.name = name;
    }
    public void initbean(){
        System.out.println("第三步 初始化bean");
    }
    public void destorybean(){
        System.out.println("第五步 销毁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="user" class="com.cky.bean.User" init-method="initbean" destroy-method="destorybean">
    <property name="name" value="cui"></property>
</bean>
</beans>
package com.cky.test;

import com.cky.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest {
    @Test
    public void test(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        User user1 = applicationContext.getBean("user", User.class);
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        System.out.println("第四步 使用bean");
        context.close();
    }
}

在这里插入图片描述

1.2bean生命周期之七步

第一步:实例化Bean
第二步:Bean属性赋值
第三步:执行“Bean后处理器”的before方法。
第四步:初始化Bean
第五步:执行“Bean后处理器”的after方法。
第六步:使用Bean
第七步:销毁Bean

在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。
编写一个类实现BeanPostProcessor类,并且重写before和after方法:

package com.cky.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class beanPostpropoess implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后处理器 before");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后处理器 after");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

<?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 class="com.cky.bean.beanPostpropoess"></bean>
<bean id="user" class="com.cky.bean.User" init-method="initbean" destroy-method="destorybean" >
    <property name="name" value="cui"></property>
</bean>
</beans>

在这里插入图片描述
注意 该后处理器 是对该配置中所有bean对象都适用。但是只是对这一个配置类中的所有bean。

1.3 bean生命周期之十步

点位1:在“Bean后处理器”before方法之前
干了什么事儿?
检查Bean是否实现了Aware相关的接口,如果实现了接口则调用这些接口中的方法。
然后调用这些方法的目的是为了给你传递一些数据,让你更加方便使用。
点位2:在“Bean后处理器”before方法之后
干了什么事儿?
检查Bean是否实现了InitializingBean接口,如果实现了,则调用接口中的方法。
点位3:使用Bean之后,或者说销毁Bean之前
干了什么事儿?
检查Bean是否实现了DisposableBean接口,如果实现了,则调用接口中的方法。

添加的这三个点位的特点:都是在检查你这个Bean是否实现了某些特定的接口,如果实现了这些接口,则Spring容器会调用这个接口中的方法。
在这里插入图片描述
Aware相关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
● 当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。
● 当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。
● 当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。
测试以上10步,可以让User类实现5个接口,并实现所有方法:
● BeanNameAware
● BeanClassLoaderAware
● BeanFactoryAware
● InitializingBean
● DisposableBean

package com.cky.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
    private String name;

    public User() {
        System.out.println("第一步 无参构造执行");
    }

    public void setName(String name) {
        System.out.println("第二步 属性配置");
        this.name = name;
    }
    public void initbean(){
        System.out.println("第三步 初始化bean");
    }
    public void destorybean(){
        System.out.println("第五步 销毁bean");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("类加载器"+classLoader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("类工厂"+beanFactory);

    }

    @Override
    public void setBeanName(String name) {
        System.out.println("bean"+name);

    }

    @Override
    public void destroy() throws Exception {
        System.out.println("销毁之前");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化之前");
    }
}

在这里插入图片描述

1.4 bean作用域与管理周期

Spring 根据Bean的作用域来选择管理方式。
● 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
对于单例模式,spring管理整个生命周期,包括创建和销毁。
● 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
对于多例模式,spring只管到初始化之后 即销毁之前的和销毁这两个步骤spring是不管的
当我把scope改为scope=“prototype”
之后我们可以看出 销毁前的方法和销毁方法,spring是不管的。
在这里插入图片描述

2、把自己new的对象交给spring管理

package com.cky.test;

import com.cky.bean.Student;
import com.cky.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest {
    @Test
    public void test(){

        Student student=new Student();
        DefaultListableBeanFactory defaultListableBeanFactory=new DefaultListableBeanFactory();
        defaultListableBeanFactory.registerSingleton("studentbean",student);
        Student studentbean1 = defaultListableBeanFactory.getBean("studentbean", Student.class);
        System.out.println(studentbean1);

    }
}

3、Bean循环依赖

3.1 set+singleton

在singleton + setter模式下,为什么循环依赖不会出现问题,Spring是如何应对的?
主要的原因是,在这种模式下Spring对Bean的管理主要分为清晰的两个阶段:
第一个阶段:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行"曝光"【不等属性赋值就曝光】
第二个阶段:Bean“曝光”之后,再进行属性的赋值(调用set方法。)。
核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成的。
注意:只有在scope是singleton的情况下,Bean才会采取提前“曝光”的措施。

package com.cky.bean;


public class Husband {
    private String name;
    private Wife wife;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }

    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}

package com.cky.bean;

public class Wife {
    private String name;
    private Husband husband;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setHusband(Husband husband) {
        this.husband = husband;
    }

    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}

<?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="husbandBean" class="com.cky.bean.Husband" scope="singleton">
        <property name="name" value="张三"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.cky.bean.Wife" scope="singleton">
        <property name="name" value="小花"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>

在ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“spring1.xml”);spring容器就开始实例化bean对象以及给bean对象赋值。

3.2 构造+singleton

这种方法,循环依赖会出问题
因为我们无法提前曝光,在构造时,就需要给属性赋值才能实例化,而由于依赖另一个对象,另一个对象又依赖于当前对象,无法进行实例化,会出现BeanCurrentlyInCreationException 当前的Bean正在处于创建中异常。

3.3 propotype+set注入

循环依赖也会出现问题。会出现BeanCurrentlyInCreationException 当前的Bean正在处于创建中异常。由于是多例的,我们在实例化一个对象之后给对象赋值时会需要另外一个bean对象,但是这两个对象都是多例的,所以会一直创建新的对象,双方相互实例化,没有停止…

注意但是这种情况是两个对象都是propotype时才会出现的问题,只有一个时singleton,循环依赖就不会出现问题,因为比如上述例子,妻子是singleton,丈夫是propotype的,妻子在创建时只会创建一个,给妻子赋值时会去创建丈夫对象,此时丈夫对象又会去找妻子对象,由于妻子对象是单例的,只有一个所以该丈夫对象可以创建且成功赋值,妻子对象便也可以成功赋值。

3.4 bean循环依赖源码分析:

Bean生命周期的管理,可以参考Spring的源码:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。在这里插入图片描述
源码分析:
DefaultSingletonBeanRegistry类中有三个比较重要的缓存:
private final Map<String, Object> singletonObjects 一级缓存
private final Map<String, Object> earlySingletonObjects 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories 三级缓存

这三个缓存都是Map集合。
Map集合的key存储的都是bean的name(bean id)。

一级缓存存储的是:单例Bean对象。完整的单例Bean对象,也就是说这个缓存中的Bean对象的属性都已经赋值了。是一个完整的Bean对象。
二级缓存存储的是:早期的单例Bean对象。这个缓存中的单例Bean对象的属性没有赋值。只是一个早期的实例对象。
三级缓存存储的是:单例工厂对象。这个里面存储了大量的“工厂对象”,每一个单例Bean对象都会对应一个单例工厂对象。
这个集合中存储的是,创建该单例对象时对应的那个单例工厂对象。

3.5 常见面试问题

谈谈Spring是如何解决循环依赖的?

谈谈Spring有哪几种循环依赖问题?

Spring为什么不能解决构造器注入以及多例bean的循环依赖?

只有一级缓存、三级缓存的话,能不能解决循环依赖?

为什么需要使用二级缓存earlySingletonObject?

Spring三级缓存中为什么保存的是ObjectFacory对象工厂,而不是原始实例对象?

Spring的循环依赖只能使用三级缓存才能解决?【只解决循环依赖】是否可以减少为二级缓存。

那为什么Spring没有选择使用二级缓存的方案,而是额外加一级,用三级缓存。涉及AOP。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图论模板详解

目录 Floyd算法 例题&#xff1a;蓝桥公园 Dijkstra算法 例题&#xff1a;蓝桥王国 SPFA算法 例题&#xff1a;随机数据下的最短路问题 总结 最小生成树MST Prim算法 Kruskal算法 例题&#xff1a;聪明的猴子 Floyd算法 最简单的最短路径算法&#xff0c;使用邻接…

BGP联盟、对等体组、按组打包

BGP联盟 将大的AS划分为几个子AS&#xff08;成员AS&#xff09;&#xff0c;每个子AS内部建立全连接的IBGP邻居&#xff0c;子AS之间建立EBGP邻接关系。 联盟AS&#xff1a;大AS&#xff0c;就是常说的AS号&#xff0c;一般使用公有AS号。 成员AS&#xff1a;小AS&#xff…

MongoDB 启动异常

Failed to start up WiredTiger under any compatibility version. 解决方案: 删除WiredTiger.lock 和 mongod.lock两个文件&#xff0c;在重新启动。回重新生成新的文件。

Unicode在线编码和解码工具推荐(实用)

Unicode在线编码 - Unicode编码工具 - Unicode在线生成 - Unicode在线解码 - WGCLOUD

研发效能·创享大会—IDCF五周年专场

时光流转&#xff0c;IDCF即将迎来五周年的庆典。在这个意义非凡的时刻&#xff0c;我们精心筹备了一场盛大的聚会【研发效能创享大会—IDCF五周年专场】。 IDCF自2019年成立以来&#xff0c;携手百余位技术领头人共同打造DevOps技术学习平台&#xff0c;与30万社群伙伴联动&a…

数据类型和类型检测

Data Type And Type Checking 1.编程语言中的数据类型 类型和变量 一个类型是一系列值的集合&#xff0c;这些集合可以抽象出一个相同的特点&#xff0c;并且可以相互实现计算 例如&#xff1a; 布尔类型&#xff1a;true or false整形&#xff1a;1,2,3…浮点数类型&#xf…

OM6650AM支持蓝牙5.1协议栈与2.4GHz私有协议的双模无线连接SoC芯片

OM6650AM是一款超低功耗、同时支持蓝牙5.1协议栈与2.4GHz私有协议的双模无线连接SoC芯片&#xff0c;采用4.0 mm x 4.0 mm QFN32封装&#xff0c;具有丰富的资源&#xff0c;极低的功耗&#xff0c;优异的射频性能&#xff0c;可广泛应用于车载数字钥匙模组、胎压检测、PKE钥匙…

【教程】Flutter 应用混淆

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…

YOLOv9改进策略 :主干优化 | 无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023 RIFormer

💡💡💡本文改进内容: token mixer被验证能够大幅度提升性能,但典型的token mixer为自注意力机制,推理耗时长,计算代价大,而RIFormers是无需TokenMixer也能达成SOTA性能的极简ViT架构 ,在保证性能的同时足够轻量化。 💡💡💡RIFormerBlock引入到YOLOv9,多个数…

C语言第三十八弹---编译和链接

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 编译和链接 1、翻译环境和运行环境 2、翻译环境 2.1、预处理&#xff08;预编译&#xff09; 2.2、编译 2.2.1、词法分析 2.2.2、语法分析 2.2.3、语义分…

【Linux】自定义协议+序列化+反序列化

自定义协议序列化反序列化 1.再谈 "协议"2.Cal TCP服务端2.Cal TCP客户端4.Json 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈 “协议” 协议是一种 “约定”。在前面我们说过父亲和儿子约定打电话的例子&#xff0c;不过这是感性的认识&a…

YoloV8改进策略:BackBone改进|GCNet(独家原创)|附结构图

摘要 本文使用GCNet注意力改进YoloV8,在YoloV8的主干中加入GCNet实现涨点。改进方法简单易用&#xff0c;欢迎大家使用&#xff01; 论文:《GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond》 非局部网络&#xff08;NLNet&#xff09;通过为每个查…

大模型日报20240401

大模型实时打《街霸》捉对PK&#xff0c;GPT-4居然不敌3.5&#xff0c;新型Benchmark火了 链接&#xff1a;https://news.miracleplus.com/share_link/22340 让大模型直接操纵格斗游戏《街霸》里的角色&#xff0c;捉对PK&#xff0c;谁更能打&#xff1f;GitHub上一种你没有见…

隧道烘箱在线粒子监测系统解决方案

关于隧道烘箱定义 隧道烘箱是一种采用长箱体热风循环以及远红外干燥方式进行干燥的设备。它主要是为了满足产量高、效率要求高的烘干干燥需求而设计的。在计算机系统的监控下&#xff0c;物品随输送带的输送依次进入隧道烘箱的预热区、高温灭菌区&#xff08;温度≥5min&#x…

C++ | Leetcode C++题解之第1题两数之和

题目&#xff1a; C 题解&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hashtable;for (int i 0; i < nums.size(); i) {auto it hashtable.find(target - nums[i]);if (it …

4.机器学习-十大算法之一线性回归算法(LinearRegression)案例讲解

机器学习-十大算法之一线性回归算法案例讲解 一摘要二个人简介三什么是线性回归四LinearRegression使用方法五糖尿病数据线性回归预测1.数据说明2.导包3.导入数据4.脱敏处理5.抽取训练数据和预测数据6.创建模型7.预测8.线性回归评估指标9.研究每个特征和标记结果之间的关系.来分…

Java接口与继承实践:Ether通信系统的构建(day16)

创建一个接口Icontroller, 再创建一个接口IReceiver, 创建一个子类实现IReceiver&#xff0c; 创建一个子类实现IContrller&#xff0c; 创建一个类Ether 创建一个Signal类 创建一个类Radiosignal继承Signal 创建一个用户User 最后创建一个Main类 今日总结&#xff1a…

FreeRTOS 多任务系统

在最早接触嵌入式的时候&#xff0c;我们编写的代码都是在一个while循环里处理所有的事务。 int main() {while(1){do_something();do_something1();do_something2();} } 这三个事务轮流执行。逻辑简单。但会带来一个问题&#xff1a; 事务1在执行的时候&#xff0c;事务2得…

LabVIEW齿轮箱噪声监测系统

LabVIEW齿轮箱噪声监测系统 齿轮箱作为机械设备的“心脏”&#xff0c;其健康状态对设备的性能有着重要的影响。传统的齿轮箱监测方法依赖于直接的振动信号分析&#xff0c;但这种方法不仅成本高昂&#xff0c;而且在安装和拆卸过程中可能对设备造成损害。针对这些问题&#x…

激光雷达的量产车方案

文章目录 现在的量产方案共同点与差异技术方案应用场景未来发展趋势 现在的量产方案 在量产车领域&#xff0c;半固态激光雷达技术的发展和应用是实现高级自动驾驶功能的关键技术之一。半固态激光雷达&#xff0c;与传统的固态激光雷达相比&#xff0c;其最大特点是在内部采用…