美文网首页工作生活
spring IOC之循环依赖

spring IOC之循环依赖

作者: nhhnhh | 来源:发表于2019-07-01 10:49 被阅读0次

使用过spring的人,肯定知道spring的IOC,DI。那么在我们使用的过程中,可能会出现比如我一个A类,里面注入了B类,而B类,里面又注入了A类
如下图所示:

@Component
public class DemoA {
    @Autowired
    DemoB demoB;

    public DemoA() {
    }
}
@Component
public class DemoB {
    @Autowired
    DemoA demoA;

    public DemoB() {
    }
}

后来发现项目是正常启动的。
但是如果我们在构造函数里面去进行循环依赖呢

@Component
public class DemoA {

    public DemoA(DemoB demoB) {
    }

}
@Component
public class DemoB {

    public DemoB(DemoA demoA) {
    }


}

启动时就会报循环引用的问题

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoA' defined in file [/Users/nanjunbo/Documents/Ideaproject/demoSpring/target/classes/demoIoc/DemoA.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoB' defined in file [/Users/nanjunbo/Documents/Ideaproject/demoSpring/target/classes/demoIoc/DemoB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'demoA': Requested bean is currently in creation: Is there an unresolvable circular reference?

那这到底是为什么呢?这里就需要知道spring创建bean的时候到底做了什么。
1.首先我们需要知道bean到底是什么?根据spring官方文档的描述,bean是一个由Spring IoC容器实例化、组装和管理的对象。那么原生java对象,跟你用spring来创建一个bean到底有什么区别。其实在创建bean的过程中,对java的原生对做了一系列的处理,扩展。这个就是bean的生命周期。
现在我们从代码看看spring的生命周期。
先看一下测试类:

public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext abstractApplicationContext = new AnnotationConfigApplicationContext();
        abstractApplicationContext.register(DemoConfig.class);
        abstractApplicationContext.refresh();
        System.out.println(abstractApplicationContext.getBean("demoA"));

    }
}

可以看到将测试的配置文件注册进容器中,然后刷新容器,就可以获取bean了。
那refresh到底做了什么呢?

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //使用对象锁,对spring容器的refresh跟destroy进行加锁
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //准备刷新容器前的环境
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //对beanFactory进行赋值
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //提供一个修改beanFactory的入口
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //同样是对beanFactory进行修改
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //注册bean创建时的拦截器
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //初始化MessageSource
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化事件的上下文广播
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //空方法,可以自己定义想处理什么
                onRefresh();

                // Check for listener beans and register them.
                //注册监听器
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //(重要)完成bean的初始换
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
public void preInstantiateSingletons() throws BeansException {
        if (logger.isTraceEnabled()) {
            logger.trace("Pre-instantiating singletons in " + this);
        }

        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            //把非RootBeanDefinition的merge成RootBeanDefinition
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            //判断是否需要实例化bean
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    //获取factoryBean
                    Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                    if (bean instanceof FactoryBean) {
                        final FactoryBean<?> factory = (FactoryBean<?>) bean;
                        boolean isEagerInit;
                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                            isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                                            ((SmartFactoryBean<?>) factory)::isEagerInit,
                                    getAccessControlContext());
                        }
                        else {
                            isEagerInit = (factory instanceof SmartFactoryBean &&
                                    ((SmartFactoryBean<?>) factory).isEagerInit());
                        }
                        if (isEagerInit) {
                            getBean(beanName);
                        }
                    }
                }
                else {
                    //获取普通Bean(核心方法)
                    getBean(beanName);
                }
            }
        }

        // Trigger post-initialization callback for all applicable beans...
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                        smartSingleton.afterSingletonsInstantiated();
                        return null;
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

然后主要看doGetBean的方法,然后有一段doCreateBean的方法。
初始化bean的时候,将其放入singletonFactories中,然后调用populateBean方法,来注入属性,如果这时候发生了依赖,比如文章开头的示例,初始化A的时候,注入了B,然后再初始化B,发现又注入了A。这时候初始化B,然后调用populateBean方法时,然后又去getBean(A),去getBean的时候,从singletonFactories中获取,然后就发现了有A的存在,不用再去实例化A了
这里只是大概写了一下,spring的源码太多了,太吊了。只要对大概的流程知道就行了。每一句每一句分析需要消耗大量的时间,不过对个人的成长很有用

相关文章

  • spring IOC之循环依赖

    使用过spring的人,肯定知道spring的IOC,DI。那么在我们使用的过程中,可能会出现比如我一个A类,里面...

  • spring循环依赖的解决方案

    spring循环依赖的解决方案 Spring IOC循环依赖解决方案分析 这里Spring主要用了三层缓存来完成对...

  • 2020-05-16

    Spring IOC 容器源码分析 - 循环依赖的解决办法 本文,我们来看一下 Spring 是如何解决循环依赖问...

  • spring4 IOC循环依赖问题

    1. 情况1:setter循环依赖 2. 情况2:构造器循环依赖 spring4 IOC获取单例对象方式 循环依赖...

  • Spring中的循环依赖

    循环依赖   Spring使用依赖注入(DI)来实现控制反转(IoC),因而不可避免的会存在循环依赖的情况:当容器...

  • spring-IOC 创建bean

    spring-IOC 创建bean 循环依赖 在创建bean的时候会存在依赖注入的情况,即A依赖B,B又依赖A。在...

  • Spring学习之依赖注入

    Spring学习之依赖注入 依赖注入的基本概念 依赖注入(Dependecy Injection),也称为IoC(...

  • 四、spring ioc之bean循环依赖

    本文是直接摘抄《Spring源码深度解析》5.6节循环依赖,首先是加深自己的理解,其次是方便查阅。 什么是循环依赖...

  • 2018-09-27 Spring

    Spring IOC 容器 Spring IOC的理解 依赖注入(dependence injection):通过...

  • spring之IOC

    Spring之IOC(DI) 控制反转 Inversion of Control依赖注入 Dependency I...

网友评论

    本文标题:spring IOC之循环依赖

    本文链接:https://www.haomeiwen.com/subject/cpdpcctx.html