精华内容
下载资源
问答
  • 循环依赖

    2021-01-10 11:37:51
    什么是循环依赖? 顾名思义,循环依赖就是A依赖B,B又依赖A,两者之间的依赖关系形成了一个圆环,通常是由于不正确的编码所导致。Spring只能解决属性循环依赖问题,不能解决构造函数循环依赖问题,因为这个问题无解...

    什么是循环依赖?
    顾名思义,循环依赖就是A依赖B,B又依赖A,两者之间的依赖关系形成了一个圆环,通常是由于不正确的编码所导致。Spring只能解决属性循环依赖问题,不能解决构造函数循环依赖问题,因为这个问题无解。

    接下来我们首先写一个Demo来演示Spring是如何处理属性循环依赖问题的。

    Talk is cheap. Show me the code
    第一步:定义一个类ComponentA,其有一个私有属性componentB。

    package com.tech.ioc;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * @author 君战
     * **/
    @Component
    public class ComponentA {
    
    	@Autowired
    	private ComponentB componentB;
    
    	public void say(){
    		componentB.say();
    	}
    
    }
    

    第二步:定义一个类ComponentB,其依赖ComponentA。并定义一个say方法便于打印数据。

    package com.tech.ioc;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    /**
     * @author 君战
     * **/
    @Component
    public class ComponentB {
    
    	@Autowired
    	private ComponentA componentA;
    
    	public void say(){
    		System.out.println("componentA field " + componentA);
    		System.out.println(this.getClass().getName() + " -----> say()");
    	}
    
    }
    

    第三步:重点,编写一个类-SimpleContainer,模仿Spring底层处理循环依赖。如果理解这个代码,再去看Spring处理循环依赖的逻辑就会很简单。

    package com.tech.ioc;
    
    import java.beans.Introspector;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * 演示Spring中循环依赖是如何处理的,只是个简版,真实的Spring依赖处理远比这个复杂。
     * 但大体思路都相同。另外这个Demo很多情况都未考虑,例如线程安全问题,仅供参考。
     * @author 君战
     *
     * **/
    public class SimpleContainer {
    
    	/***
    	 * 用于存放完全初始化好的Bean,Bean处于就绪状态
    	 * 这个Map定义和Spring中一级缓存命名一致
    	 * */
    	private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    	/***
    	 * 用于存放刚创建出来的Bean,其属性还没有处理,因此存放在该缓存中的Bean还不可用。
    	 * 这个Map定义和Spring中三级缓存命名一致
    	 * */
    	private final Map<String, Object> singletonFactories = new HashMap<>(16);
    
    
    	public static void main(String[] args) {
    		SimpleContainer container = new SimpleContainer();
    		ComponentA componentA = container.getBean(ComponentA.class);
    		componentA.say();
    	}
    
    	public <T> T getBean(Class<T> beanClass) {
    		String beanName = this.getBeanName(beanClass);
    		// 首先根据beanName从缓存中获取Bean实例
    		Object bean = this.getSingleton(beanName);
    		if (bean == null) {
    			// 如果未获取到Bean实例,则创建Bean实例
    			return createBean(beanClass, beanName);
    		}
    		return (T) bean;
    	}
    	/***
    	 * 从一级缓存和二级缓存中根据beanName来获取Bean实例,可能为空
    	 * */
    	private Object getSingleton(String beanName) {
    		// 首先尝试从一级缓存中获取
    		Object instance = singletonObjects.get(beanName);
    		if (instance == null) { // Spring 之所以能解决循环依赖问题,也是靠着这个三级缓存--singletonFactories
    			instance = singletonFactories.get(beanName);
    		}
    		return instance;
    	}
    
    	/***
    	 * 创建指定Class的实例,返回完全状态的Bean(属性可用)
    	 *
    	 * */
    	private <T> T createBean(Class<T> beanClass, String beanName) {
    		try {
    			Constructor<T> constructor = beanClass.getDeclaredConstructor();
    			T instance = constructor.newInstance();
    			// 先将刚创建好的实例存放到三级缓存中,如果没有这一步,Spring 也无法解决三级缓存
    			singletonFactories.put(beanName, instance);
    			Field[] fields = beanClass.getDeclaredFields();
    			for (Field field : fields) {
    				Class<?> fieldType = field.getType();
    				field.setAccessible(true); 
    				// 精髓是这里又调用了getBean方法,例如正在处理ComponentA.componentB属性,
    				// 执行到这里时就会去实例化ComponentB。因为在getBean方法首先去查缓存,
    				// 而一级缓存和三级缓存中没有ComponentB实例数据,所以又会调用到当前方法,
    				// 而在处理ComponentB.componentA属性时,又去调用getBean方法去缓存中查找,
    				// 因为在前面我们将ComponentA实例放入到了三级缓存,因此可以找到。
    				// 所以ComponentB的实例化结束,方法出栈,返回到实例化ComponentA的方法栈中,
    				// 这时ComponentB已经初始化完成,因此ComponentA.componentB属性赋值成功!
    				field.set(instance, this.getBean(fieldType));
    			}
    			// 最后再将初始化好的Bean设置到一级缓存中。
    			singletonObjects.put(beanName, instance);
    			return instance;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		throw new IllegalArgumentException();
    	}
    
    	/**
    	 * 将类名小写作为beanName,Spring底层实现和这个差不多,也是使用javaBeans的
    	 * {@linkplain Introspector#decapitalize(String)}
    	 **/
    	private String getBeanName(Class<?> clazz) {
    		String clazzName = clazz.getName();
    		int index = clazzName.lastIndexOf(".");
    		String className = clazzName.substring(index);
    		return Introspector.decapitalize(className);
    	}
    
    展开全文
  • Springboot循环依赖如何解决

    万次阅读 多人点赞 2019-03-26 13:59:52
    1. 循环依赖是什么? Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。 Bean A → Bean B → Bean A 更复杂的间接依赖造成的循环依赖如下。 Bean A → Bean B → Bean C → Bean D → Bean E → Bean A 2. ...

    微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路

    1. 循环依赖是什么?

    Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。
    Bean A → Bean B → Bean A
    更复杂的间接依赖造成的循环依赖如下。
    Bean A → Bean B → Bean C → Bean D → Bean E → Bean A

    2. 循环依赖会产生什么结果?

    当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。
    例如,有如下依赖:
    Bean A → Bean B → Bean C
    Spring先创建beanC,接着创建bean B(将C注入B中),最后创建bean A(将B注入A中)。

    但当存在循环依赖时,Spring将无法决定先创建哪个bean。这种情况下,Spring将产生异常BeanCurrentlyInCreationException。

    3.普通注入之间的循环依赖

    比如:我现在有一个ServiceA需要调用ServiceB的方法,那么ServiceA就依赖于ServiceB,那在ServiceB中再调用ServiceA的方法,就形成了循环依赖。Spring在初始化bean的时候就不知道先初始化哪个,bean就会报错。

    public class ClassA {
    
    @Autowired
    
    ClassB classB;
    
    }
    
    public class ClassB {
    
    @Autowired
    
    ClassA classA
    
    }

    如何解决循环依赖,最好的方法是重构代码,进行解耦,如果没有时间重构,可以使用下面的方法:

    (1)在你的配置文件中,在互相依赖的两个bean的任意一个加上lazy-init属性

    <bean id="ServiceDependent1" class="org.xyz.ServiceDependent1" lazy-init="true"> 
    
    <constructor-arg ref="Service"/> </bean>  
    
     <bean id="ServiceDependent2" class="org.xyz.ServiceDependent2" lazy-init="true"> 
    
    <constructor-arg ref="Service"/> </bean>   

      (2)在你注入bean时,在互相依赖的两个bean上加上@Lazy注解也可以

    @Autowired     
    
    @Lazy      
    
    private ClassA classA; 
    
    
    @Autowired 
    
    @Lazy      
    
    private ClassB classB; 

    4. 构造器注入循环依赖实例

    首先定义两个相互通过构造器注入依赖的bean。

    @Component
    public class CircularDependencyA {
     
        private CircularDependencyB circB;
     
        @Autowired
        public CircularDependencyA(CircularDependencyB circB) {
            this.circB = circB;
        }
    }
    
    @Component
    public class CircularDependencyB {
     
        private CircularDependencyA circA;
     
        @Autowired
        public CircularDependencyB(CircularDependencyA circA) {
            this.circA = circA;
        }
    }
    
    @Configuration
    @ComponentScan(basePackages = { "com.baeldung.circulardependency" })
    public class TestConfig {
    }
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { TestConfig.class })
    public class CircularDependencyTest {
     
        @Test
        public void givenCircularDependency_whenConstructorInjection_thenItFails() {
            // Empty test; we just want the context to load
        }
    }
    

    运行方法givenCircularDependency_whenConstructorInjection_thenItFails将会产生异常:BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA’:
    Requested bean is currently in creation: Is there an unresolvable circular reference?

    如何解决

    (1)重新设计

    重新设计结构,消除循环依赖。

    (2)使用注解 @Lazy

    一种最简单的消除循环依赖的方式是通过延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。

    @Component
    public class CircularDependencyA {
     
        private CircularDependencyB circB;
     
        @Autowired
        public CircularDependencyA(@Lazy CircularDependencyB circB) {
            this.circB = circB;
        }
    }
    

    使用@Lazy后,运行代码,可以看到异常消除。

    (3)使用Setter/Field注入

    Spring文档建议的一种方式是使用setter注入。当依赖最终被使用时才进行注入。对前文的样例代码少做修改,来观察测试效果。

    @Component
    public class CircularDependencyA {
     
        private CircularDependencyB circB;
     
        @Autowired
        public void setCircB(CircularDependencyB circB) {
            this.circB = circB;
        }
     
        public CircularDependencyB getCircB() {
            return circB;
        }
    }
    
    @Component
    public class CircularDependencyB {
     
        private CircularDependencyA circA;
     
        private String message = "Hi!";
     
        @Autowired
        public void setCircA(CircularDependencyA circA) {
            this.circA = circA;
        }
     
        public String getMessage() {
            return message;
        }
    }
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { TestConfig.class })
    public class CircularDependencyTest {
     
        @Autowired
        ApplicationContext context;
     
        @Bean
        public CircularDependencyA getCircularDependencyA() {
            return new CircularDependencyA();
        }
     
        @Bean
        public CircularDependencyB getCircularDependencyB() {
            return new CircularDependencyB();
        }
     
        @Test
        public void givenCircularDependency_whenSetterInjection_thenItWorks() {
            CircularDependencyA circA = context.getBean(CircularDependencyA.class);
    
            Assert.assertEquals("Hi!", circA.getCircB().getMessage());
        }
    }
    

    (4) 使用@PostConstruct

    @Component
    public class CircularDependencyA {
     
        @Autowired
        private CircularDependencyB circB;
     
        @PostConstruct
        public void init() {
            circB.setCircA(this);
        }
     
        public CircularDependencyB getCircB() {
            return circB;
        }
    }
    
    
    @Component
    public class CircularDependencyB {
     
        private CircularDependencyA circA;
         
        private String message = "Hi!";
     
        public void setCircA(CircularDependencyA circA) {
            this.circA = circA;
        }
         
        public String getMessage() {
            return message;
        }
    
    

    (5)实现ApplicationContextAware与InitializingBean

    @Component
    public class CircularDependencyA implements ApplicationContextAware, InitializingBean {
     
        private CircularDependencyB circB;
     
        private ApplicationContext context;
     
        public CircularDependencyB getCircB() {
            return circB;
        }
     
        @Override
        public void afterPropertiesSet() throws Exception {
            circB = context.getBean(CircularDependencyB.class);
        }
     
        @Override
        public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
            context = ctx;
        }
    }
    
    @Component
    public class CircularDependencyB {
     
        private CircularDependencyA circA;
     
        private String message = "Hi!";
     
        @Autowired
        public void setCircA(CircularDependencyA circA) {
            this.circA = circA;
        }
     
        public String getMessage() {
            return message;
        }
    }
    

    展开全文
  • 手写spring循环依赖的整个过程2. spring怎么解决循环依赖3. 为什么要二级缓存和三级缓存4. spring有没有解决构造函数的循环依赖5. spring有没有解决多例下的循环依赖.一. 什么是循环依赖?如下图所示:A类依赖了B类, ...

    本次博客的目标

    1. 手写spring循环依赖的整个过程

    2. spring怎么解决循环依赖

    3. 为什么要二级缓存和三级缓存

    4. spring没有解决构造函数的循环依赖

    5. spring有没有解决多例下的循环依赖.

    一. 什么是循环依赖?

    如下图所示:

    bf7267e10fcccbe537415f4e4777c74e.png

    A类依赖了B类, 同时B类有依赖了A类. 这就是循环依赖, 形成了一个闭环

    c5d764af728a1dbb25c61fc2ee77d7e6.png

    如上图: A依赖了B, B同时依赖了A和C , C依赖了A. 这也是循环依赖. , 形成了一个闭环

    那么, 如果出现循环依赖, spring是如何解决循环依赖问题的呢?

    二. 模拟循环依赖

    2.1 复现循环依赖

    我们定义三个类:

    1. 新增类InstanceA

    package com.lxl.www.circulardependencies;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component@Scope("prototype")public class InstanceA {    @Autowired    private InstanceB instanceB;    public InstanceA() {        System.out.println("调用 instanceA的构造函数");    }    public InstanceA(InstanceB instanceB) {        this.instanceB = instanceB;    }    public void say(){        System.out.println( "I am A");    }    public InstanceB getInstanceB() {        return instanceB;    }    public void setInstanceB(InstanceB instanceB) {        this.instanceB = instanceB;    }}

    这是InstanceA, 里面引用了InstanceB.

    2. 新增类instanceB

    package com.lxl.www.circulardependencies;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component@Scope("prototype")public class InstanceB {    @Autowired    private InstanceA instanceA;    public InstanceB() {        System.out.println("调用 instanceB的构造函数");    }    public InstanceA getInstanceA() {        return instanceA;    }    public void setInstanceA(InstanceA instanceA) {        this.instanceA = instanceA;    }}

    这是InstanceB, 在里面有引用了InstanceA

    3:模拟spring是如何创建Bean的

    这个在前面已经说过了, 首先会加载配置类的后置处理器, 将其解析后放入到beanDefinitionMap中. 然后加载配置类, 也将其解析后放入beanDefinitionMap中. 最后解析配置类. 我们这里直接简化掉前两步, 将两个类放入beanDefinitionMap中. 主要模拟第三步解析配置类. 在解析的过程中, 获取bean的时候会出现循环依赖的问题循环依赖.

    第一步: 将两个类放入到beanDefinitionMap中

    public class MainStart {    private static Map beanDefinitionMap = new ConcurrentHashMap<>();     /**     * 读取bean定义, 当然在spring中肯定是根据配置 动态扫描注册的     *     * InstanceA和InstanceB都有注解@Component, 所以, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中.     * 这里, 我们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中.     */    public static void loadBeanDefinitions(){        RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);        RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);        beanDefinitionMap.put("instanceA", aBeanDefinition);        beanDefinitionMap.put("instanceB", bBeanDefinition);    }    public static void main(String[] args) throws Exception {        // 第一步: 扫描配置类, 读取bean定义        loadBeanDefinitions();......}

    上面的代码结构很简单, 再看一下注释应该就能明白了. 这里就是模拟spring将配置类解析放入到beanDefinitionMap的过程.

    第二步: 循环创建bean

    首先,我们已经知道, 创建bean一共有三个步骤: 实例化, 属性赋值, 初始化.

    aac683f18b4ccec197c84fc1fe4794f1.png

    而在属性赋值的时候, 会判断是否引用了其他的Bean, 如果引用了, 那么需要构建此Bean. 下面来看一下代码

    /**     * 获取bean, 根据beanName获取     */    public static Object getBean(String beanName) throws Exception {/**         * 第一步: 实例化         * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数         */        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);        Class> beanClass = beanDefinition.getBeanClass();        // 调用无参的构造函数进行实例化        Object instanceBean = beanClass.newInstance();       /**         *  第二步: 属性赋值         *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.         *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type         */        Field[] declaredFields = beanClass.getDeclaredFields();        for (Field declaredField: declaredFields) {            // 判断每一个属性是否有@Autowired注解            Autowired annotation = declaredField.getAnnotation(Autowired.class);            if (annotation != null) {                // 设置这个属性是可访问的                declaredField.setAccessible(true);                // 那么这个时候还要构建这个属性的bean.                /*                 * 获取属性的名字                 * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.                 * 我们这里模拟, 所以简单一些, 直接根据名字获取.                 */                String name = declaredField.getName();                /**                 * 这样, 在这里我们就拿到了 instanceB 的 bean                 */                Object fileObject = getBean(name);                // 为属性设置类型                declaredField.set(instanceBean, fileObject);            }        }        /**         * 第三步: 初始化         * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了         */        return instanceBean;    }

    我们看到如上代码.

    第一步: 实例化:使用反射的方式, 根据beanName查找构建一个实例bean.

    第二步: 属性赋值:判断属性中是否有@Autowired属性, 如果有这个属性, 那么需要构建bean. 我们发现在为InstanceA赋值的时候, 里面引用了InstanceB, 所以去创建InstanceB, 而创建InstanceB的时候, 发现里面又有InstanceA, 于是又去创建A. 然后以此类推,继续判断. 就形成了死循环. 无法走出这个环. 这就是循环依赖

    第三步: 初始化:调用init-method, 这个方法不是必须有, 所以,我们这里不模拟了

    看看如下图所示

    8d48c1e92ee88ec4badb787e047ac341.png

    红色部分就形成了循环依赖.

    4: 增加一级缓存, 解决循环依赖的问题.

    我们知道上面进行了循环依赖了. 其实, 我们的目标很简单, 如果一个类创建过了, 那么就请不要在创建了.

    所以, 我们增加一级缓存

      // 一级缓存    private static Map singletonObjects = new ConcurrentHashMap<>();   /**     * 获取bean, 根据beanName获取     */    public static Object getBean(String beanName) throws Exception {        // 增加一个出口. 判断实体类是否已经被加载过了        Object singleton = getSingleton(beanName);        if (singleton != null) {            return singleton;        }        /**         * 第一步: 实例化         * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数         */        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);        Class> beanClass = beanDefinition.getBeanClass();        // 调用无参的构造函数进行实例化        Object instanceBean = beanClass.newInstance();        /**         * 第二步: 放入到一级缓存         */        singletonObjects.put(beanName, instanceBean);        /**         *  第三步: 属性赋值         *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.         *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type          */        Field[] declaredFields = beanClass.getDeclaredFields();        for (Field declaredField: declaredFields) {            // 判断每一个属性是否有@Autowired注解            Autowired annotation = declaredField.getAnnotation(Autowired.class);            if (annotation != null) {                // 设置这个属性是可访问的                declaredField.setAccessible(true);                // 那么这个时候还要构建这个属性的bean.                /*                 * 获取属性的名字                 * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.                 * 我们这里模拟, 所以简单一些, 直接根据名字获取.                 */                String name = declaredField.getName();                /**                 * 这样, 在这里我们就拿到了 instanceB 的 bean                 */                Object fileObject = getBean(name);                // 为属性设置类型                declaredField.set(instanceBean, fileObject);            }        }        /**         * 第四步: 初始化         * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了         */        return instanceBean;    }

    还是上面的获取bean的流程, 不一样的是, 这里增加了以及缓存. 当我们获取到bean实例以后, 将其放入到缓存中. 下次再需要创建之前, 先去缓存里判断,是否已经有了, 如果没有, 那么再创建.

    这样就给创建bean增加了一个出口. 不会循环创建了.

    418c70b10f9a48ddfa6b81eab962dea5.png

    如上图所示, 在@Autowired的时候, 增加了一个出口. 判断即将要创建的类是否已经存在, 如果存在了, 那么就直接返回, 不在创建

    虽然使用了一级缓存解决了循环依赖的问题, 但要是在多线程下, 这个依赖可能就会出现问题.

    比如: 有两个线程, 同时创建instanceA 和instanceB, instanceA和instanceB都引用了instanceC. 他们同步进行, 都去创建instanceC. 首先A去创建, A在实例化instanceC以后就将其放入到一级缓存了, 这时候, B去一级缓存里拿. 此时拿到的instanceC是不完整的. 后面的属性赋值, 初始化都还没有执行呢. 所以, 我们增加耳机缓存来解决这个问题.

    5. 增加二级缓存, 区分完整的bean和纯净的bean.

    public class MainStart {    private static Map beanDefinitionMap = new ConcurrentHashMap<>();    // 一级缓存    private static Map singletonObjects = new ConcurrentHashMap<>();    // 二级缓存    private static Map earlySingletonObjects = new ConcurrentHashMap<>();    /**     * 读取bean定义, 当然在spring中肯定是根据配置 动态扫描注册的     *     * InstanceA和InstanceB都有注解@Component, 所以, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中.     * 这里, 我们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中.     */    public static void loadBeanDefinitions(){        RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);        RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);        beanDefinitionMap.put("instanceA", aBeanDefinition);        beanDefinitionMap.put("instanceB", bBeanDefinition);    }    public static void main(String[] args) throws Exception {        // 第一步: 扫描配置类, 读取bean定义        loadBeanDefinitions();        // 第二步: 循环创建bean        for (String key: beanDefinitionMap.keySet()) {            // 第一次: key是instanceA, 所以先创建A类            getBean(key);        }        // 测试: 看是否能执行成功        InstanceA instanceA = (InstanceA) getBean("instanceA");        instanceA.say();    }    /**     * 获取bean, 根据beanName获取     */    public static Object getBean(String beanName) throws Exception {        // 增加一个出口. 判断实体类是否已经被加载过了        Object singleton = getSingleton(beanName);        if (singleton != null) {            return singleton;        }        /**         * 第一步: 实例化         * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数         */        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);        Class> beanClass = beanDefinition.getBeanClass();        // 调用无参的构造函数进行实例化        Object instanceBean = beanClass.newInstance();        /**         * 第二步: 放入到二级缓存         */        earlySingletonObjects.put(beanName, instanceBean);        /**         *  第三步: 属性赋值         *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.         *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type          */        Field[] declaredFields = beanClass.getDeclaredFields();        for (Field declaredField: declaredFields) {            // 判断每一个属性是否有@Autowired注解            Autowired annotation = declaredField.getAnnotation(Autowired.class);            if (annotation != null) {                // 设置这个属性是可访问的                declaredField.setAccessible(true);                // 那么这个时候还要构建这个属性的bean.                /*                 * 获取属性的名字                 * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.                 * 我们这里模拟, 所以简单一些, 直接根据名字获取.                 */                String name = declaredField.getName();                /**                 * 这样, 在这里我们就拿到了 instanceB 的 bean                 */                Object fileObject = getBean(name);                // 为属性设置类型                declaredField.set(instanceBean, fileObject);            }        }        /**         * 第四步: 初始化         * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了         */        /**         * 第二步: 放入到一级缓存         */        singletonObjects.put(beanName, instanceBean);        return instanceBean;    }    /**     * 判断是否是循环引用的出口.     * @param beanName     * @return     */    private static Object getSingleton(String beanName) {        // 先去一级缓存里拿,如果一级缓存没有拿到,去二级缓存里拿        if (singletonObjects.containsKey(beanName)) {            return singletonObjects.get(beanName);        } else if (earlySingletonObjects.containsKey(beanName)){            return earlySingletonObjects.get(beanName);        } else {            return null;        }    }}

    如上图所示,增加了一个二级缓存. 首先, 构建出instanceBean以后, 直接将其放入到二级缓存中. 这时只是一个纯净的bean, 里面还没有给属性赋值, 初始化. 在给属性赋值完成, 初始化完成以后, 将其放入到一级缓存中.

    我们判断缓存中是否有某个实例bean的时候, 先去一级缓存中判断是否有完整的bean, 如果没有, 就去二级缓存中判断有没有实例化过这个bean.

    总结 : 一级缓存和二级缓存的作用

    一级缓存: 解决循环依赖的问题二级缓存: 在创建实例bean和放入到一级缓存之间还有一段间隙. 如果在这之间从一级缓存拿实例, 肯定是返回null的. 为了避免这个问题, 增加了二级缓存.

    我们都知道spring中有一级缓存, 二级缓存, 三级缓存. 一级缓存和二级缓存的作用我们知道了, 那么三级缓存有什么用呢?

    6. 增加三级缓存

    三级缓存有什么作用呢? 这个问题众说纷纭, 有说代理, 有说AOP. 其实AOP的问题可以用二级缓存来解决. 下面就来看看AOP如何用二级缓存解决.

    创建AOP动态代理 (不是耦合的, 采用解耦的, 通过BeanPostProcessor bean的后置处理器来创建). 之前讲过, 如下图

    在初始化之后, 调用Bean的后置处理器去创建的AOP的动态代理

    6634d1c56254333d0ee04f5709ea2e34.png

    如上图. 我们在创建bean 的时候, 会有很多Bean的后置处理器BeanPostProcessor. 如果有AOP, 会在什么时候创建呢? 在初始化以后, 调用BeanPostProcessor创建动态代理.

    结合上面的代码, 我们想一想, 其实在初始化以后创建动态代理就晚了. 为什么呢? 因为, 如果有循环依赖, 在初始化之后才调用, 那就不是动态代理. 其实我们这时候应该在实例化之后, 放入到二级缓存之前调用

    面试题: 在创建bean的时候, 在哪里创建的动态代理, 这个应该怎么回答呢?很多人会说在初始化之后, 或者在实例化之后.其实更严谨的说, 有两种情况: 第一种是在初始化之后调用 . 第二种是出现了循环依赖, 会在实例化之后调用

    我们上面说的就是第二种情况. 也就是说,正常情况下是在初始化之后调用的, 但是如果有循环依赖, 就要在实例化之后调用了.

    下面来看看如何在二级缓存加动态代理.

    首先, 我们这里有循环依赖, 所以将动态代理放在实例化之后,

      /**     * 获取bean, 根据beanName获取     */    public static Object getBean(String beanName) throws Exception {        // 增加一个出口. 判断实体类是否已经被加载过了        Object singleton = getSingleton(beanName);        if (singleton != null) {            return singleton;        }        /**         * 第一步: 实例化         * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数         */        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);        Class> beanClass = beanDefinition.getBeanClass();        // 调用无参的构造函数进行实例化        Object instanceBean = beanClass.newInstance();        /**         * 创建AOP动态代理 (不是耦合的, 采用解耦的, 通过BeanPostProcessor bean的后置处理器得来的.  之前讲过,         * 在初始化之后, 调用Bean的后置处理器去创建的AOP的动态代理 )         */        instanceBean = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, "instanceA");        /**         * 第二步: 放入到二级缓存         */        earlySingletonObjects.put(beanName, instanceBean);        /**         *  第三步: 属性赋值         *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.         *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type          */        Field[] declaredFields = beanClass.getDeclaredFields();        for (Field declaredField: declaredFields) {            // 判断每一个属性是否有@Autowired注解            Autowired annotation = declaredField.getAnnotation(Autowired.class);            if (annotation != null) {                // 设置这个属性是可访问的                declaredField.setAccessible(true);                // 那么这个时候还要构建这个属性的bean.                /*                 * 获取属性的名字                 * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.                 * 我们这里模拟, 所以简单一些, 直接根据名字获取.                 */                String name = declaredField.getName();                /**                 * 这样, 在这里我们就拿到了 instanceB 的 bean                 */                Object fileObject = getBean(name);                // 为属性设置类型                declaredField.set(instanceBean, fileObject);            }        }        /**         * 第四步: 初始化         * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了         */      //   正常动态代理创建的时机                /**         * 第五步: 放入到一级缓存         */        singletonObjects.put(beanName, instanceBean);        return instanceBean;    }

    这里只是简单模拟了动态代理.

    我们知道动态代理有两个地方. 如果是普通类动态代理在初始化之后执行, 如果是循环依赖, 那么动态代理是在实例化之后.

    上面在实例化之后创建proxy的代码不完整, 为什么不完整呢, 因为没有判断是否是循环依赖.

    我们简单模拟一个动态代理的实现.

    public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {    /**     * 假设A被切点命中 需要创建代理  @PointCut("execution(* *..InstanceA.*(..))")     * @param bean the raw bean instance     * @param beanName the name of the bean     * @return     * @throws BeansException     */    @Override    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {        // 假设A被切点命中 需要创建代理  @PointCut("execution(* *..InstanceA.*(..))")        /**         * 这里, 我们简单直接判断bean是不是InstanceA实例, 如果是, 就创建动态代理.         * 这里没有去解析切点, 解析切点是AspectJ做的事.         */        if (bean instanceof InstanceA) {            JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean);            return jdkDynimcProxy.getProxy();        }        return bean;    }}

    这里直接判断, 如果bean是InstanceA的实例, 那么就调用bean的动态代理. 动态代理的简单逻辑就是: 解析切面, 然后创建类, 如果类不存在就新增, 如果存在则不在创建, 直接取出来返回.

    在来看看动态代理,放在实例化之后. 创建AOP, 但是, 在这里创建AOP动态代理的条件是循环依赖.

    问题1: 那么如何判断是循环依赖呢?

    二级缓存中bean不是null.

    如果一个类在创建的过程中, 会放入到二级缓存, 如果完全创建完了, 会放入到一级缓存, 然后删除二级缓存. 所以, 如果二级缓存中的bean只要存在, 就说明这个类是创建中, 出现了循环依赖.

    问题2: 什么时候判断呢?

    应该在getSingleton()判断是否是循环依赖的时候判断. 因为这时候我们刚好判断了二级缓存中bean是否为空.

    /**     * 判断是否是循环引用的出口.     * @param beanName     * @return     */    private static Object getSingleton(String beanName) {        // 先去一级缓存里拿,如果一级缓存没有拿到,去二级缓存里拿        if (singletonObjects.containsKey(beanName)) {            return singletonObjects.get(beanName);        } else if (earlySingletonObjects.containsKey(beanName)){            /**             * 第一次创建bean是正常的instanceBean. 他并不是循环依赖. 第二次进来判断, 这个bean已经存在了, 就说明是循环依赖了             * 这时候通过动态代理创建bean. 然后将这个bean在放入到二级缓存中覆盖原来的instanceBean.             */            Object obj = new JdkProxyBeanPostProcessor()                    .getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);            earlySingletonObjects.put(beanName, obj);            return earlySingletonObjects.get(beanName);        } else {            return null;        }    }

    这样我们在循环依赖的时候就完成了AOP的创建. 这是在二级缓存里创建的AOP,

    问题3: 那这是不是说就不需要三级缓存了呢?

    那么,来找问题. 这里有两个问题:

    问题1: 我们发现在创建动态代理的时候, 我们使用的bean的后置处理器JdkProxyBeanPostProcessor.这有点不太符合规则,      因为, spring在getBean()的时候并没有使用Bean的后置处理器, 而是在createBean()的时候才去使用的bean的后置处理器.问题2: 如果A是AOP, 他一直都是, 最开始创建的时候也应该是. 使用这种方法, 结果是第一次创建出来的bean不是AOP动态代理.

    对于第一个问题: 我们希望在实例化的时候创建AOP, 但是具体判断是在getSingleton()方法里判断. 这里通过三级缓存来实现. 三级缓存里面放的是一个接口定义的钩子方法. 方法的执行在后面调用的时候执行.

    对于第二个问题: 我们的二级缓存就不能直接保存instanceBean实例了, 增加一个参数, 用来标记当前这个类是一个正在创建中的类. 这样来判断循环依赖.

    下面先来看看创建的三个缓存和一个标识

      // 一级缓存    private static Map singletonObjects = new ConcurrentHashMap<>();    // 二级缓存: 为了将成熟的bean和纯净的bean分离. 避免读取到不完整的bean.    private static Map earlySingletonObjects = new ConcurrentHashMap<>();    // 三级缓存:    private static Map singletonFactories = new ConcurrentHashMap<>();    // 循环依赖的标识---当前正在创建的实例bean    private static Set singletonsCurrectlyInCreation = new HashSet<>();

    然后在来看看循环依赖的出口

    /**     * 判断是否是循环引用的出口.     * @param beanName     * @return     */    private static Object getSingleton(String beanName) {        //先去一级缓存里拿        Object bean = singletonObjects.get(beanName);        // 一级缓存中没有, 但是正在创建的bean标识中有, 说明是循环依赖        if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) {            bean = earlySingletonObjects.get(beanName);            // 如果二级缓存中没有, 就从三级缓存中拿            if (bean == null) {                // 从三级缓存中取                ObjectFactory objectFactory = singletonFactories.get(beanName);                if (objectFactory != null) {                    // 这里是真正创建动态代理的地方.                    Object obj = objectFactory.getObject();                    // 然后将其放入到二级缓存中. 因为如果有多次依赖, 就去二级缓存中判断. 已经有了就不在再次创建了                    earlySingletonObjects.put(beanName, obj);                }            }        }        return bean;    }

    这里的逻辑是, 先去一级缓存中拿, 一级缓存放的是成熟的bean, 也就是他已经完成了属性赋值和初始化. 如果一级缓存没有, 而正在创建中的类标识是true, 就说明这个类正在创建中, 这是一个循环依赖. 这个时候就去二级缓存中取数据, 二级缓存中的数据是何时放进去的呢, 是后面从三级缓存中创建动态代理后放进去的. 如果二级缓存为空, 说明没有创建过动态代理, 这时候在去三级缓存中拿, 然后创建动态代理. 创建完以后放入二级缓存中, 后面就不用再创建.

    完成的代码如下:

    package com.lxl.www.circulardependencies;import org.springframework.beans.BeansException;import org.springframework.beans.factory.annotation.Autowire;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.RootBeanDefinition;import java.lang.reflect.Field;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;public class MainStart {    private static Map beanDefinitionMap = new ConcurrentHashMap<>();    // 一级缓存    private static Map singletonObjects = new ConcurrentHashMap<>();    // 二级缓存: 为了将成熟的bean和纯净的bean分离. 避免读取到不完整的bean.    private static Map earlySingletonObjects = new ConcurrentHashMap<>();    // 三级缓存:    private static Map singletonFactories = new ConcurrentHashMap<>();    // 循环依赖的标识---当前正在创建的实例bean    private static Set singletonsCurrectlyInCreation = new HashSet<>();    /**     * 读取bean定义, 当然在spring中肯定是根据配置 动态扫描注册的     *     * InstanceA和InstanceB都有注解@Component, 所以, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中.     * 这里, 我们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中.     */    public static void loadBeanDefinitions(){        RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);        RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);        beanDefinitionMap.put("instanceA", aBeanDefinition);        beanDefinitionMap.put("instanceB", bBeanDefinition);    }    public static void main(String[] args) throws Exception {        // 第一步: 扫描配置类, 读取bean定义        loadBeanDefinitions();        // 第二步: 循环创建bean        for (String key: beanDefinitionMap.keySet()) {            // 第一次: key是instanceA, 所以先创建A类            getBean(key);        }        // 测试: 看是否能执行成功        InstanceA instanceA = (InstanceA) getBean("instanceA");        instanceA.say();    }    /**     * 获取bean, 根据beanName获取     */    public static Object getBean(String beanName) throws Exception {        // 增加一个出口. 判断实体类是否已经被加载过了        Object singleton = getSingleton(beanName);        if (singleton != null) {            return singleton;        }        // 标记bean正在创建        if (!singletonsCurrectlyInCreation.contains(beanName)) {            singletonsCurrectlyInCreation.add(beanName);        }        /**         * 第一步: 实例化         * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数         */        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);        Class> beanClass = beanDefinition.getBeanClass();        // 调用无参的构造函数进行实例化        Object instanceBean = beanClass.newInstance();        /**         * 第二步: 放入到三级缓存         * 每一次createBean都会将其放入到三级缓存中. getObject是一个钩子方法. 在这里不会被调用.         * 什么时候被调用呢?         * 在getSingleton()从三级缓存中取数据, 调用创建动态代理的时候         */        singletonFactories.put(beanName, new ObjectFactory() {            @Override            public Object getObject() throws BeansException {                return new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);            }        });        //earlySingletonObjects.put(beanName, instanceBean);        /**         *  第三步: 属性赋值         *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.         *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type         */        Field[] declaredFields = beanClass.getDeclaredFields();        for (Field declaredField: declaredFields) {            // 判断每一个属性是否有@Autowired注解            Autowired annotation = declaredField.getAnnotation(Autowired.class);            if (annotation != null) {                // 设置这个属性是可访问的                declaredField.setAccessible(true);                // 那么这个时候还要构建这个属性的bean.                /*                 * 获取属性的名字                 * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.                 * 我们这里模拟, 所以简单一些, 直接根据名字获取.                 */                String name = declaredField.getName();                /**                 * 这样, 在这里我们就拿到了 instanceB 的 bean                 */                Object fileObject = getBean(name);                // 为属性设置类型                declaredField.set(instanceBean, fileObject);            }        }        /**         * 第四步: 初始化         * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了         */        /**         * 第五步: 放入到一级缓存         *         * 在这里二级缓存存的是动态代理, 那么一级缓存肯定也要存动态代理的实例.         * 从二级缓存中取出实例, 放入到一级缓存中         */        if (earlySingletonObjects.containsKey(beanName)) {            instanceBean = earlySingletonObjects.get(beanName);        }        singletonObjects.put(beanName, instanceBean);        return instanceBean;    }    /**     * 判断是否是循环引用的出口.     * @param beanName     * @return     */    private static Object getSingleton(String beanName) {        //先去一级缓存里拿,        Object bean = singletonObjects.get(beanName);        // 一级缓存中没有, 但是正在创建的bean标识中有, 说明是循环依赖        if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) {            bean = earlySingletonObjects.get(beanName);            // 如果二级缓存中没有, 就从三级缓存中拿            if (bean == null) {                // 从三级缓存中取                ObjectFactory objectFactory = singletonFactories.get(beanName);                if (objectFactory != null) {                    // 这里是真正创建动态代理的地方.                    Object obj = objectFactory.getObject();                    // 然后将其放入到二级缓存中. 因为如果有多次依赖, 就去二级缓存中判断. 已经有了就不在再次创建了                    earlySingletonObjects.put(beanName, obj);                }            }        }        return bean;    }}

    来源:https://www.tuicool.com/articles/eq6zemn

    展开全文
  • 5.6.1什么是循环依赖、循环调用循环依赖:实例化bean是一个复杂的过程。循环依赖就是两个或者多个bean相互之间持有对方,比如TestA引用TestB,TestB引用TestA,则它们最终反映为一个环。循环调用:循环调用是方法...

    5.6.1什么是循环依赖、循环调用

    循环依赖:实例化bean是一个复杂的过程。循环依赖就是两个或者多个bean相互之间持有对方,比如TestA引用TestB,TestB引用TestA,则它们最终反映为一个环。

    循环调用:循环调用是方法之间的环调用。循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出。

    5.6.2Spring如何解决循环依赖

    首先我们定义循环依赖类、注入、测试方法

    packageCircularReference;public classTestA {privateTestB testB;publicTestA(){

    }publicTestA(TestB testB) {this.testB =testB;

    }publicTestB getTestB() {returntestB;

    }public voidsetTestB(TestB testB) {this.testB =testB;

    }

    }packageCircularReference;public classTestB {privateTestA testA;publicTestB(){

    }publicTestB(TestA testA) {this.testA =testA;

    }publicTestA getTestA() {returntestA;

    }public voidsetTestA(TestA testA) {this.testA =testA;

    }

    }

    /*** 构造器循环依赖*/@Testpublic voidconstructor(){try{new ClassPathXmlApplicationContext("/circularReference.xml");

    }catch(Exception e){

    e.printStackTrace();

    System.out.println(e.getCause());

    }

    }/*** setter循环依赖*/@Testpublic voidsetter(){

    ApplicationContext contex=

    new ClassPathXmlApplicationContext("/circularReference.xml");

    System.out.println(contex.getBean("testA"));

    }

    3种循环依赖

    1)构造器循环依赖(单例的)

    构造器注入的循环依赖,此依赖是无法解决的。只能抛出BeanCurrentlyInCreationEx-ception异常表示循环依赖。与bean实例化有关。

    代码中的实现方式:DefaultSingletonBeanRegistry中getSingleton(beanName-->,singletonFactory)方法的beforeSingletonCreation中的this.singletonsCurrently-InCreation.add(beanName)。5.4获取单例---大致逻辑---第3条。

    Spring容器将每一条正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因为如果在创建bean过程中发现自己已经在“当前创建bean池”里面时,抛出BeanCurrentlyInCreationEx-ception异常表示循环依赖。而对于创建完毕的bean将从“当前创建bean池”中移除。

    模拟执行:

    Spring容器创建“testA” bean,首先去“当前创建 bean 池” 查找是否当前 bean 正在创建,如果没发现,则继续准备其需妥的构造器参数“testB”,并将“testA”标识符放到“当前创建池”中。

    Spring容器创建“testB”bean,首先去“当前创建 bean池”查找是否当前 bean 正在创建 ,如没发现,则继续准备其需妥 的构造 器参数“ testA”,并将“ testB”标识符放到“当前创建bean池”。

    到此为止 Spring 容器要去创建“testA” bean,发现该 bean 标识符在“当前创建 bean 池”中,因为表示循环依赖,抛出 BeanCurrentlyInCreationException

    2)setter循环依赖(单例的)

    简单来说是通过一个默认构造器,并暴露一个FactoryBean用于返回一个创建中的bean(AbstractBeanFactory.doGetBean方法的getSingleton会用到),然后再进行setter设置。

    通过setter注入方式构成的循环依赖。对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入(默认构造器)但未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。

    代码中的实现方式:

    获取:AbstractBeanFactory-->doGetBean-->getSingleton

    protected Object getSingleton(String beanName, booleanallowEarlyReference) {//Quick check for existing instance without full singleton lock//检查缓存中是否存在实例

    Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null &&isSingletonCurrentlyInCreation(beanName)) {//缓存中没有实例但是又在创建中,那么可能是循环依赖,解决循环依赖的方式是//Spring创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光

    singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null &&allowEarlyReference) {//如果为空,则锁定全局变量并进行处理

    synchronized (this.singletonObjects) {//Consistent creation of early reference within full singleton lock

    singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {

    singletonObject= this.earlySingletonObjects.get(beanName);if (singletonObject == null) {//当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的//ObjectFactory初始化策略存储在singletonFactories

    ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {//调用预先设定的getObject方法

    singletonObject =singletonFactory.getObject();//记录在缓存中,earlySingletonObjects和singletonFactories互斥

    this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);

    }

    }

    }

    }

    }

    }returnsingletonObject;

    }

    存储:AbstractAutowireCapableBeanFactory-->createBean-->doCreateBean-->addSingletonFactory

    protected void addSingletonFactory(String beanName, ObjectFactory>singletonFactory) {

    Assert.notNull(singletonFactory,"Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);

    }

    }

    }

    3)prototype范围的依赖处理

    对于prototype作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存“prototype”作用域的bean。因此无法暴露一个创建中的bean。

    对于singleton作用域bean,可以通过“setAllowCircularReferences(false)”来禁止循环引用。

    博客:Spring中循环引用的处理

    https://www.iflym.com/index.php/code/201208280001.html

    展开全文
  • 5.6循环依赖实例化bean是一个非常复杂的过程,而其中最比较难以理解的就是对循环依赖的解决,不管之前读者有没有对循环依赖方面的研究,这里有必要先对此知识点稍作回顾。5.6.1什么是循环依赖循环依赖就是循环引用,...
  • 1.循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方,比如CircleA引用CircleB,Circle引用CircleC,CircleC引用CircleA,则它们最终反映了一个环。此处不是循环调用,循环调用是方法之间的循环调用。循环...
  • 循环依赖插件 与webpack捆绑时,检测具有循环依赖性的模块。 循环依赖关系在复杂软件中通常是必需的,循环依赖关系的存在并不总是意味着存在错误,但是在您认为存在错误的情况下,此模块可能会帮助您找到它。 ...
  • 前言当我们使用Spring 体系时,应用一个依赖的类只要加上@Autowired或者@Resource 就能直接使用了,但是正常很多情况下应用的类与被应用的存在相互依赖的关系,这样就会出现循环依赖,Spring是怎样解决循环依赖的?循环...
  • Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题。 其实笔者本人对这类框架源码题还是持一定的怀疑态度的。 如果笔者作为面试官,可能会问一些诸如“如果注入的属性为null,你会从哪几个方向去排查”...
  • spring针对循环依赖问题 不能完全解决 对于不能解决的只能检测到并抛出异常1.spring针对构造器方法的 单实例对象和原型对象是无法解决循环依赖问题的先说结论,针对单例对象 getSingleton方法中 有个...
  • 循环依赖:Spring 循环依赖有三种情况:构造器的循环依赖,这种依赖 Spring 无法处理,直接抛出 BeanCurrentlyInCreationException 异常单例模式下的 setter 循环依赖,可以通过三级缓存处理非单例循环依赖,无法...
  • Spring-bean的循环依赖以及解决方式

    万次阅读 多人点赞 2017-09-12 08:18:21
    本文主要是分析Spring bean的循环依赖,以及Spring的解决方式。 通过这种解决方式,我们可以应用在我们实际开发项目中。 什么是循环依赖? 怎么检测循环依赖 Spring怎么解决循环依赖 Spring对于循环依赖无法...
  • 什么是循环依赖? 顾名思义,循环依赖就是A依赖B,B又依赖A,两者之间的依赖关系形成了一个圆环,通常是由于不正确的编码所导致。Spring只能解决属性循环依赖问题,不能解决构造函数循环依赖问题,因为这个问题无解...
  • 循环依赖一直都是spring一个老生常谈的问题,为了加深影响特此整理了一下spring是如何解决循环依赖的。什么是循环依赖?java中循环依赖分为两类,一种是通过构造器传入实例化参数的时候两个对象相互依赖,另一种是则...
  • 手写spring循环依赖的整个过程2. spring怎么解决循环依赖3. 为什么要二级缓存和三级缓存4. spring有没有解决构造函数的循环依赖5. spring有没有解决多例下的循环依赖.一. 什么是循环依赖?如下图所示: A类依赖了B类,...
  • 蓝色“深入原理”,关注并“设为星标”技术干货... Spring能解决的循环依赖原理(三级缓存)一、Spring 循环依赖可能出现的三种方式第一种:构造器参数循环依赖第二种:setter方式单例,默认方式第三种:setter方式原...
  • ​引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。下面说一下Spring是如果解决循环依赖的。第一种:构造器参数...
  • 用手写循环依赖的方式来,了解Spring循环依赖
  • Spring循环依赖

    2021-02-28 17:35:13
    Spring循环依赖 @Autowired依赖 构造函数依赖
  • spring循环依赖

    2020-07-14 00:29:17
    1、什么是循环依赖 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。 2、Spring中循环依赖场景 构造器的循环依赖 (scope=“singel”) 属性的循环依赖 (scope=“singel”) ...
  • 最后找到原因时循环依赖导致的! 循环依赖其实就是循环引用(circular reference),也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图: 下面是我在写代码...
  • 循环依赖尤其糟糕。循环依赖存在与多种实体之间,尤其是类、包、模块之间。当两个类相互引用时,就会出现循环依赖。下面摘抄书中的一个例子。如图1.1所示,有Customer和Bill两个类。在本例中,Customer有一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,366
精华内容 4,146
关键字:

循环依赖