4.Spring【Java面试第三季】

news2024/11/24 4:09:17

4.Spring【Java面试第三季】

  • 前言
  • 推荐
  • 4.Spring
  • 27_Aop的题目说明要求
    • Spring的AOP顺序
      • AOP常用注解
      • 面试题
  • 28_spring4下的aop测试案例
      • 业务类
      • 新建一个切面类MyAspect并为切面类新增两个注解:
      • spring4+springboot1.5.9
        • pom
        • 测试类
  • 29_spring4下的aop测试结果
        • aop正常顺序+异常顺序
  • 30_spring5下的aop测试
      • spring5+springboot2.3.3
        • pom
        • 测试类
        • aop正常顺序+异常顺序
      • 结论
  • 31_spring循环依赖题目说明
    • Spring的循环依赖
      • 恶心的大厂面试题
      • 什么是循环依赖
      • 两种注入方式对循环依赖的影响
  • 32_spring循环依赖纯java代码验证案例
      • spring容器循环依赖报错演示BeanCurrentlylnCreationException
        • 循环依赖现象在Spring容器中注入依赖的对象,有2种情况
          • ==构造器方式注入依赖==
          • ==以set方式注入依赖==
  • 33_spring循环依赖bug演示
        • 重要code案例演示
          • code-java基础编码
          • spring容器
        • 重要结论(spring内部通过3级缓存来解决循环依赖)
  • 34_spring循环依赖debug前置知识
      • ==循环依赖Debug-困难,请坚持==
        • 实例化/初始化
        • 3个Map和四大方法,总体相关对象
        • A/B两对象在三级缓存中的迁移说明
  • spring循环依赖debug源码
  • 39_spring循环依赖小总结
      • ==总结spring是如何解决的循环依赖?==
        • 解释
        • Debug的步骤--->Spring解决循环依赖过程
  • 最后

前言

2023-2-3 13:51:54

以下内容源自
【尚硅谷Java大厂面试题第3季,跳槽必刷题目+必扫技术盲点(周阳主讲)-哔哩哔哩】
仅供学习交流使用

推荐

Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)

4.Spring

27_Aop的题目说明要求

Spring的AOP顺序

AOP常用注解

  • @Before 前置通知:目标方法之前执行
  • @After 后置通知:目标方法之后执行(始终执行)
  • @AfterReturning 返回后通知:执行方法结束前执行(异常不执行)
  • @AfterThrowing 异常通知:出现异常时候执行
  • @Around 环绕通知:环绕目标方法执行

面试题

  • 你肯定知道spring,那说说aop的全部通知顺序springboot或springboot2(spring4->spring5)对aop的执行顺序影响?

  • 说说你使用AOP中碰到的坑?

28_spring4下的aop测试案例

业务类

  • 接口CalcService
  • 接口实现类CalcServicelmpl新加@Service
  • 想在除法方法前后置入各种通知,引入切面编程

新建Maven工程,pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring4_aop</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring4_aop</name>
    <description>spring4_aop</description>
    <properties>
        <java.version>8</java.version>
    </properties>

    <dependencies>

        <!-- web  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <!-- Spring Boot AOP技术-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

启动类

package com.example.spring4_aop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Spring4AopApplication {

    public static void main(String[] args) {
        SpringApplication.run(Spring4AopApplication.class, args);
    }

}

接口CalcService

package com.example.spring4_aop.spring.aop;

public interface CalcService {
    public int div(int x, int y);
}



接口实现类CalcServiceImpl新加@Service

package com.example.spring4_aop.spring.aop;

import org.springframework.stereotype.Service;

@Service
public class CalcServiceImpl implements CalcService {

    @Override
    public int div(int x, int y) {
        int result = x / y;
        System.out.println("===>CalcServiceImpl被调用,计算结果为:" + result);
        return result;
    }
}

新建一个切面类MyAspect并为切面类新增两个注解:

  • @Aspect 指定一个类为切面类
  • @Component 纳入Spring容器管理
  • code
package com.example.spring4_aop.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {
    @Before("execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
    public void beforeNotify() {
        System.out.println("********@Before我是前置通知");
    }

    @After("execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
    public void afterNotify() {
        System.out.println("********@After我是后置通知");
    }

    @AfterReturning("execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
    public void afterReturningNotify() {
        System.out.println("********@AfterReturning我是返回后通知");
    }

    @AfterThrowing(" execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
    public void afterThrowingNotify() {
        System.out.println("********@AfterThrowing我是异常通知");
    }

    @Around(" execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object retvalue = null;
        System.out.println("我是环绕通知之前AAA");
        retvalue = proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知之后BBB");
        return retvalue ;
    }
}


spring4+springboot1.5.9

pom

	 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/>
    </parent>

测试类

package com.example.spring4_aop;

import javax.annotation.Resource;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.spring4_aop.spring.aop.CalcService;

@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest {

    @Resource
    private CalcService calcService;

    @Test
    public void testAop4() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

        calcService.div(10, 2);
    }
}


29_spring4下的aop测试结果

Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
===>CalcServiceImpl被调用,计算结果为:5
我是环绕通知之后BBB
********@After我是后置通知
********@AfterReturning我是返回后通知

修改测试类,让其抛出算术异常类:

package com.example.spring4_aop;

import javax.annotation.Resource;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.spring4_aop.spring.aop.CalcService;

@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest {

    @Resource
    private CalcService calcService;

    @Test
    public void testAop4() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

//        calcService.div(10, 2);
        
        calcService.div(10, 0);//将会抛出异常
    }
}


输出结果:

Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
********@After我是后置通知
********@AfterThrowing我是异常通知

java.lang.ArithmeticException: / by zero

aop正常顺序+异常顺序

0350

  • 正常情况下:@Before前置通知----->@After后置通知----->@AfterRunning正常返回
  • 异常情况下:@Before前置通知----->@After后置通知----->@AfterThrowing方法异常

30_spring5下的aop测试

spring5+springboot2.3.3

pom

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
<!--        <version>1.5.9.RELEASE</version>-->
        <version>2.3.3.RELEASE</version>
        <relativePath/>
    </parent>

测试类

package com.example.spring4_aop;

import javax.annotation.Resource;


import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.spring4_aop.spring.aop.CalcService;

@SpringBootTest

public class AopTest {

    @Resource
    private CalcService calcService;

    @Test
    public void testAop4() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

//        calcService.div(10, 2);

        calcService.div(10, 0);//将会抛出异常
    }

    @Test
    public void testAop5() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

        calcService.div(10, 2);

//        calcService.div(10, 0);//将会抛出异常
    }
}


Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
===>CalcServiceImpl被调用,计算结果为:5
********@AfterReturning我是返回后通知
********@After我是后置通知
我是环绕通知之后BBB

修改测试类,让其抛出算术异常类:

package com.example.spring4_aop;

import javax.annotation.Resource;


import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.spring4_aop.spring.aop.CalcService;

@SpringBootTest

public class AopTest {

    @Resource
    private CalcService calcService;

    @Test
    public void testAop4() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

//        calcService.div(10, 2);

        calcService.div(10, 0);//将会抛出异常
    }

    @Test
    public void testAop5() {
        System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
                SpringVersion.getVersion(), SpringBootVersion.getVersion()));

//        calcService.div(10, 2);

        calcService.div(10, 0);//将会抛出异常
    }
}


输出结果

Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
********@AfterThrowing我是异常通知
********@After我是后置通知

java.lang.ArithmeticException: / by zero

aop正常顺序+异常顺序

同上结果

结论

0536

31_spring循环依赖题目说明

Spring的循环依赖

恶心的大厂面试题

  • 你解释下spring中的三级缓存?
  • 三级缓存分别是什么?三个Map有什么异同?
  • 什么是循环依赖?请你谈谈?看过spring源码吗?一般我们说spring容器是什么
  • 如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
  • 多例的情况下,循环依赖问题为什么无法解决?
  • 。。。

什么是循环依赖

  • 多个bean之间相互依赖,形成了一个闭环。比如:A依赖于B、B依赖于C、C依赖于A。

    • 代码
      0314
  • 通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。
    0407

两种注入方式对循环依赖的影响

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core
在这里插入图片描述

  • 循环依赖官网说明
    5838

  • 结论
    我们AB循环依赖问题只要A的注入方式是setter且singleton ,就不会有循环依赖问题。

32_spring循环依赖纯java代码验证案例

spring容器循环依赖报错演示BeanCurrentlylnCreationException

循环依赖现象在Spring容器中注入依赖的对象,有2种情况

构造器方式注入依赖

code

  • serviceA
@Component
public class ServiceA{
    private ServiceB serviceB;

    public ServiceA(ServiceB serviceB){
        this.serviceB = serviceB;
    }
}


  • serviceB
@Component
public class ServiceB{
    private ServiceA serviceA;

    public ServiceB(ServiceA serviceA){
        this.serviceA = serviceA;
    }
}


  • ClientConstructor
public class ClientConstructor{
    public static void main(String[] args){
        new ServiceA(new ServiceB(new ServiceA()));//这会抛出编译异常
    }
}


结论

  • 构造器循环依赖是无法解决的你想让构造器注入支持循环依赖,是不存在的
    在这里插入图片描述
以set方式注入依赖

code

  • ServiceAA
@Component
public class ServiceAA{
    private ServiceBB serviceBB;
    
    public void setServiceBB(ServiceBB serviceBB){
        this.serviceBB = serviceBB;
        System.out.println("A里面设置了B");
    }
}

  • ServiceBB
@Component
public class ServiceBB{
    private ServiceAA serviceAA;

    public void setServiceAA(ServiceAA serviceAA){
        this.serviceAA = serviceAA;
        System.out.println("B里面设置了A");
    }
}


  • ClientSet
public class ClientSet{
    public static void main(String[] args){
        //创建serviceAA
        ServiceAA a = new ServiceAA();
        //创建serviceBB
        ServiceBB b = new ServiceBB();
        //将serviceA入到serviceB中
        b.setServiceAA(a);
        //将serviceB法入到serviceA中
        a.setServiceBB(b);
    }
}

输出结果:

B里面设置了A
A里面设置了B

33_spring循环依赖bug演示

重要code案例演示

code-java基础编码
  • A
package com.example.spring4_aop.spring;

public class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

    public A() {
        System.out.println("---A created success");
    }
}


  • B
package com.example.spring4_aop.spring;

public class B {

    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    public B() {
        System.out.println("---B created success");
    }
}


  • ClientCode
package com.example.spring4_aop.spring;

public class ClientCode {
    public static void main(String[] args) {
        A a=new A();
        B b=new B();

        b.setA(a);
        a.setB(b);
    }
}

测试结果

---A created success
---B created success
spring容器
  • 默认的单例(singleton)的场景是支持循环依赖的,不报错
  • 原型(Prototype)的场景是不支持循环依赖的,会报错
  • 步骤
    • applicationContext.xml
    • 默认单例,修改为原型scope="prototype"ClientspringContainer
    • 循环依赖异常
      0508

applicationContext.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <bean id="a" class="com.example.spring4_aop.spring.A">
        <property name="b" ref="b"></property>
    </bean>
    <bean id="b" class="com.example.spring4_aop.spring.B">
        <property name="a" ref="a"></property>
    </bean>

</beans>

ClientSpringContainer

package com.example.spring4_aop.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ClientSpringContainer {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
    }
}

输出结果

15:29:16.316 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61064425
15:29:16.552 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
15:29:16.607 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a'
---A created success
15:29:16.626 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b'
---B created success

原型(Prototype)的场景是不支持循环依赖的,会报错

applicationContext.xml

    <bean id="a" class="com.example.spring4_aop.spring.A" scope="prototype">
        <property name="b" ref="b"></property>
    </bean>
    <bean id="b" class="com.example.spring4_aop.spring.B" scope="prototype">
        <property name="a" ref="a"></property>
    </bean>

输出结果

15:33:03.481 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61064425
15:33:03.669 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
---A created success
---B created success
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'b' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

Exception in thread "main" org.springframework.beans.factory.BeanCreationException:Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

重要结论(spring内部通过3级缓存来解决循环依赖)

DefaultSingletonBeanRegistry

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象。

第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完。)

第三级缓存:Map<String, ObjectFactory<?>> singletonFactories,存放可以生成Bean的工厂。

package org.springframework.beans.factory.support;

...

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	...

	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
 
    ...
    
}

34_spring循环依赖debug前置知识

循环依赖Debug-困难,请坚持

实例化/初始化

  • 实例化

    • 内存中申请一块内存空间
    • 租赁好房子,自己的家当还未搬来。
  • 初始化属性填充

    • 完成属性的各种赋值
    • 装修,家具,家电进场。

3个Map和四大方法,总体相关对象

0433

第一层singletonObjects存放的是已经初始化好了的Bean,

第二层earlySingletonObjects存放的是实例化了,但是未初始化的Bean,

第三层singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean

package org.springframework.beans.factory.support;

...

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	...

	/** 
	单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
	表示已经经历了完整生命周期的Bean对象
	第一级缓存
	*/
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	早期的单例对象的高速缓存: bean名称—bean实例。
	表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
	第二级缓存
	*/
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	/**
	单例工厂的高速缓存:bean名称—ObjectFactory
	表示存放生成 bean的工厂
	第三级缓存
	*/
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 
    ...
    
}

A/B两对象在三级缓存中的迁移说明

1.A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B。

2.B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。

3.B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

@FunctionalInterface
public interface ObjectFactory<T> {

	T getObject() throws BeansException;

}

spring循环依赖debug源码

跳转
spring循环依赖debug源码

39_spring循环依赖小总结

总结spring是如何解决的循环依赖?

0327

解释

Spring创建 bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化

每次创建 bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个

当我们创建 beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖了beanA又是同样的流程,

不同的是:

这时候可以在三级缓存中查到刚放进去的原始对象beanA.所以不需要继续创建,用它注入 beanB,完成 beanB的创建

既然 beanB创建好了,所以 beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成

0913

Spring解决循环依赖依靠的是Bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成品
实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。”对

Spring为了解决单例的循坏依赖问题,使用了三级缓存:
其中一级缓存为单例池(singletonObjects)。
二级缓存为提前曝光对象(earlySingletonObjects)。
三级级存为提前曝光对象工厂(singletonFactories) 。

假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。

Debug的步骤—>Spring解决循环依赖过程

请添加图片描述

  1. 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
  2. 在getSingleton()方法中,从一级缓存中查找,没有,返回null
  3. doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
  4. 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法
  5. 进入AbstractAutowireCapableBeanFactory#ndoCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中
  6. 对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
  7. 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
  8. 此时 beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA
  9. 这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
  10. 随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

最后

2023-2-3 21:23:12

这篇博客能写好的原因是:站在巨人的肩膀上

这篇博客要写好的目的是:做别人的肩膀

开源:为爱发电

学习:为我而行

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

求极限方法总结

1.利用四则运算法则求极限 2.利用两个重要极限求极限 //0除以0型 //1的无穷次方型 3.利用等价无穷小替换替换求极限 //在等价替换时注意和差项 4.利用洛必达法则求极限 5.利用夹逼准则求极限 6.利用单调有界数列极限准则求极限 7.利用无穷小的性质求极限 8.利用函数的连续性…

来面试阿里测开工程师,HR问我未来3-5年规划,我给HR画个大饼。

在面试的过程中是不是经常被面试官问未来几年的职业规划?你会答吗&#xff1f;是不是经常脑袋里一片空白&#xff0c;未来规划&#xff1f;我只是想赚更多的钱啊&#xff0c;哈哈哈&#xff0c;今天我来教大家&#xff0c;如何给面试官画一个大饼&#xff0c;让他吃的不亦乐乎…

MySQL高级第二讲

目录 二、MySQL高级02 2.1 触发器 2.1.1 触发器介绍 2.1.2 创建触发器 2.2 MySQL的体系结构 2.3 存储引擎 2.3.1 存储引擎概述 2.3.2 各种存储引擎特性 2.3.3 InnoDB 2.3.4 MyISAM 2.3.5 MEMORY 2.3.6 MERGE 2.3.7 存储引擎的选择 2.4 优化sql 2.4.1 查看sql执行…

二分查找重复情况 找最左边或最右边的位置下标

目录二分找最左边二分找最右边综合应用(剑指offer)二分找最左边 核心思想: 先mid (lr)/2每次向左取整; 然后命中target的时候&#xff0c;右边界逼近到mid; 因为每次mid向左取整&#xff0c;mid命中target时l代替mid位置&#xff0c;则循环迭代最后会卡出重复数字最左侧的位置…

糖化学试剂55520-67-7,5-vinyl-2-deoxyuridine,5-乙烯基-2-脱氧尿苷特点分析说明

5-vinyl-2-deoxyuridine(5-VdU)&#xff0c;5-vinyl-2-deoxyuridine&#xff0c;5-Vinyldeoxyuridine5-乙烯基-2-脱氧尿苷 | CAS&#xff1a;55520-67-7 | 纯度&#xff1a;95%试剂信息&#xff1a;CAS&#xff1a;55520-67-7所属类别&#xff1a;糖化学分子量&#xff1a;C11H…

信息系统基本知识(一)

1.1 信息系统与信息化 信息论已发展成为一个内涵非常丰富的学科&#xff0c;与控制论和系统论并称为现代科学的“三论” 1.1.1 信息的基本概念 香农指出&#xff1a;信息是能够用来消除不确定性的东西。信息量的单位&#xff1a;比特 信息的特征 客观性&#xff1a;信息是…

四信塔式起重机监控系统应用方案

方案背景 塔式起重机是当前工业建设进行起重运输作业的重要设备&#xff0c;其设备性能、参数、技术指标的可靠性都关系起重机设备的危险&#xff0c;塔式起重机安全事故关系国计民生、危害面广&#xff0c;给国家财产和生命安全带来严重损失。 目前众多塔式起重机司机虽然有上…

界面开发(1) --- PyQt5环境配置

PyQt5环境配置 第一步&#xff1a;首先安装社区版Pycharm 下载地址&#xff1a;https://www.jetbrains.com/pycharm/download/#sectionwindows 第二步&#xff1a;安装Anaconda3&#xff0c;配置虚拟环境 下载地址&#xff1a;https://www.anaconda.com/ 第三步&#xff1…

win10安装docker

1.win10安装docker&#xff0c;前提必须是要安装WSL2。 现在Docker Desktop默认使用WSL 2来运行&#xff0c;而不是以前的Hyper-V。 WSL2 全称是Windows Subsystem on Linux。意思是&#xff0c;在win10&#xff0c;可以直接启动一个Linux。因为docker依赖Linux内核。 可查看…

洛谷 U91193:棋盘覆盖问题 ← 分治法

【题目来源】https://www.luogu.com.cn/problem/U91193【问题描述】 在一个2^k * 2^k&#xff08;k≥0&#xff09;个方格组成的棋盘中&#xff0c;恰有一个方格与其他方格不同&#xff0c;称该方格为一特殊方格。现在用4种不同形状的 L型&#xff08;占3小格&#xff09;骨牌覆…

java 元数据 和 元注解

基本介绍三种基本注解OverrideDeprecatedSuppressWarnings四种元注解RetentionTargetDocumentedInherited一、基本介绍1.概述java注解&#xff08;Annotation&#xff09;[ˌ nəˈ teɪʃn]&#xff0c;又称java标注&#xff0c;也被称为元数据&#xff08;关于数据的数据&…

Kotlin学习:5.2、异步数据流 Flow

Flow一、Flow1、Flow是什么东西&#xff1f;2、实现功能3、特点4、冷流和热流5、流的连续性6、流的构建器7、流的上下文8、指定流所在协程9、流的取消9.1、超时取消9.2、主动取消9.3、密集型任务的取消10、背压和优化10.1、buffer 操作符10.2、 flowOn10.3、conflate 操作符10.…

Substrate 基础 -- 教程(Tutorials)

官网 github DOC 面向未来的区块链框架 Substrate 使开发人员能够快速、轻松地构建适合任何用例的未来 证明区块链(future proof blockchains)。 Substrate 文档包括区块链构建器&#xff08;blockchain builders&#xff09;和parachain 项目团队的概念、过程和参考信息。…

Nginx面试问题总结

1. 什么是Nginx&#xff1f;Nginx是一个 轻量级/高性能的反向代理Web服务器&#xff0c;他实现非常高效的反向代理、负载平衡&#xff0c;他可以处理2-3万并发连接数&#xff0c;官方监测能支持5万并发&#xff0c;现在中国使用nginx网站用户有很多&#xff0c;例如&#xff1a…

阿里淘宝新势力造型合伙人P8、年薪百万的欧阳娜娜也躲不过的魔鬼面试,看的我心服口服

阿里淘宝新势力造型合伙人P8、年薪百万的欧阳娜娜跳槽了&#xff0c;这不是关键。 她参加了网易有道明星语音录音员/代言人的面试&#xff0c;这也不是关键。 关键是她教科书式的面试过程&#xff0c;狠狠地给我们上了一课。 我是无意间刷到的这个视频的时候&#xff0c;就一…

u盘文件夹空的但u盘内存显示满的,怎么找回文件?

最近&#xff0c;我朋友抱怨自己的u盘满了&#xff0c;但是文件夹却是空空如也。他焦急地问我&#xff0c;这到底是怎么回事&#xff1f;我给他分析了一些常见的原因。首先&#xff0c;可能是因为u盘感染了病毒&#xff0c;将文件夹里面的文件隐藏了&#xff1b;其次&#xff0…

9大插件,21张配图,编码路上助你起飞

大家好&#xff0c;我是阿Q&#xff0c;很高兴又跟大家见面了。 看过我以往文章的小伙伴可能会发现&#xff0c;我的大部分文章都偏向于实战。我的文章风格也是先理论后实战&#xff0c;有了落地的理论才能帮助大家更好的理解。 最近有好多小伙伴后台私信我&#xff0c;问我有…

你问我答|虚拟机、容器和无服务器,怎么选?

在新技术层出不穷的当下,每家企业都希望不断降低成本,并提高运营效率,一个方法就是寻找不同的技术方案来优化运营。      例如,曾经一台服务器只能运行一个应用(裸机);接着,一台服务器的资源可以划分为多个块,从而运行多个应用(虚拟化);再到后来,应用越来越多,为了方便它们…

【牛客刷题专栏】0x0E:JZ6 从尾到头打印链表(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录前言问题…

互联网衰退期,测试工程师35岁之路怎么走...

国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过35岁的基层研发类员工&#xff0c;往往因为家庭原因、身体原因&#xff0c;比较难以跟得上工作…