Spring Bean 循环依赖

什么是循环依赖

bean 之间相互依赖,形成了一个闭环。

A 依赖于 B、B 依赖于 C、C 依赖于 A。

图示例

代码示例

public class A{
    B b;
}
public class B{
    C c;
}
public class C{
    A a;
}

循环依赖怎么检测

检测循环依赖比较简单,使用一个列表来记录正在创建中的 Bean,Bean 创建之前,先去记录中看一下 自己是否已经在列表中了,如果在,说明存在循环依赖,如果不在,则将其加入到这个列表,Bean 完毕之后,将其再从这个列表中移除。

在 Spring 创建单例 Bean 的时候,会调用下面的方法

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

singletonsCurrentlyInCreation 用来记录目前正在创建中的 Bean 名称列表,如果 !this.singletonsCurrentlyInCreation.add(beanName) 返回的 false 那么说明了当前 BeanName 已经在当前列表中了,所以到抛出异常 BeanCurrentlyInCreationException。这个异常的构造器是

public BeanCurrentlyInCreationException(String beanName) {
    super(beanName,
          "Requested bean is currently in creation: Is there an unresolvable circular reference?");
}

上面是单例 bean 检测循环依赖处理的源码,再来看看非单例 bean 如何处理循环依赖。

以 prototype 情况为例,源码位于

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 方法中。

// 检查正在创建的 bean 列表中是否存在 beanName,如果存在,说明存在循环依赖, 抛出循环依赖的异常
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}
if (mbd.isPrototype()) {.
    Object prototypeInstance = null;
    try {
        // 将 beanName 放入正在创建的列表中
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        // 将 beanName 从正在创建的列表中移除
        afterPrototypeCreation(beanName);
    }
}

Spring 中解决循环依赖方案

在 Spring 创建 Bean 主要的几个步骤

  1. 实例化 Bean,即调用构造器创建 Bean 实例。

  2. 注入 Bean,比如通过 Set 方式、@Autowired 注解的方式注入依赖 Bean。

  3. Bean 的初始化,比如调用 init 的方法。

从上述的 Bean 的创建步骤中可以看出可能出现循环依赖步骤是 注入 Bean 和在 构造器注入

构造器的依赖注入实例

@Component
public class ServiceA {
    private ServiceB serviceB;
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
@Component
public class ServiceB {
    private ServiceA serviceA;
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

实例化 ServiceA 的时候,需要有 serviceB,而实例化 ServiceB 的时候需要有 serviceA,构造器循环依赖是无法解决的

Set 注入方式

@Component
public class ServiceA {
    private ServiceB serviceB;
    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
@Component
public class ServiceB {
    private ServiceA serviceA;
    @Autowired
    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

由于单例 bean 在 spring 容器中只存在一个,所以 spring 容器中肯定是有一个缓存来存放所有已创建好的单例 bean;获取单例 bean 之前,可以先去缓存中找,找到了直接返回,找不到的情况下再去创建,创建完毕之后再将其丢到缓存中,可以使用一个 map 来存储单例 bean。

Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

下面来看一下 Spring 中 set 方法创建上面 2 个 Bean 的过程

1.spring 轮询准备创建 2 个 bean:serviceA 和 serviceB
2.spring 容器发现 singletonObjects 中没有 serviceA
3.调用 serviceA 的构造器创建 serviceA 实例
4.serviceA 准备注入依赖的对象,发现需要通过 setServiceB 注入 serviceB
5.serviceA 向 spring 容器查找 serviceB
6.spring 容器发现 singletonObjects 中没有 serviceB
7.调用 serviceB 的构造器创建 serviceB 实例
8.serviceB 准备注入依赖的对象,发现需要通过 setServiceA 注入 serviceA
9.serviceB 向 spring 容器查找 serviceA
10.此时又进入步骤 2 了

Spring 中的解决方案

三级缓存

// 第一级缓存:单例bean的缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 第二级缓存:早期暴露的bean的缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 第三级缓存:单例bean工厂的缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  1. 创建 ServiceA 时候会调用下面的方法

  • org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

Object sharedInstance = getSingleton(beanName);
// 查看缓存中是否已经存在这个 Bean 了
if (sharedInstance != null && args == null) {
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
    // 缓存中不存在,准备创建这个 Bean
    if (mbd.isSingleton()) {
        // 进入单例 Bean 创建过程
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // 显式地从单例缓存中移除实例:
                // 它可能已经被创建过程热切地放在那里,以允许循环引用解析。
                // 删除所有接收到临时引用的 bean。
                destroySingleton(beanName);
                throw ex;
            }
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

接下来详细看一下 Spring 如何判断 Bean 是否在缓存中。

//allowEarlyReference:
// 是否允许从三级缓存 singletonFactories 中通过 getObject 拿到 bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 快速检查没有完全单例锁的现有实例
    // 一级缓存中查找
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 从二级缓存中查找
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 二级缓存没有找到并开启三级缓存开始向三级缓存中查找
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // 在完整的单例锁中一致地创建早期引用
                // 再重新查询一级缓存和二级缓存
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    // 重新查找没有找到进入三级缓存
                    if (singletonObject == null) {
                        // 三级缓存返回的是一个工厂,通过工厂来获取创建 bean
                        ObjectFactory<?> singletonFactory = 					 this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 三级缓存返回的是一个工厂,通过工厂来获取创建 bean
                            singletonObject = singletonFactory.getObject();
                            // 将创建好的 bean 丢到二级缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 从三级缓存移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

刚开始,3 个缓存中肯定是找不到的,会返回 null,接着会执行下面代码准备创建 serviceA。

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> { 
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    });
}

接下来看一下 getSingleton 方法代码比较多。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 单例 Bean 创建之前调用, 将其加入正在创建的列表中。
            // 上面有提到过,主要用来检查循环依赖使用
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            try {
                // 调用工厂创建 Bean
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }finally {
                // 单例 Bean 创建之前调用, 主要是将其从正在创建的列表中移除。
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 将创建完成的单例 Bean 加入到缓存中
                addSingleton(beanName, singletonObject);
            }
        }
    }
    return singletonObject;
}

return createBean(beanName, mbd, args); 最终会调用这个方法

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

其内部主要代码如下:

BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
    // 通过反射调用构造器实例化 ServiceA
    instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 变量bean: 表示刚刚同构造器创建好的 Bean 实例
Object bean = instanceWrapper.getWrappedInstance();
// 判断是否需要暴露早期的 bean,条件为
// (是否是单例 bean && 当前容器允许循环依赖 && bean 名称存在于正在创建的 bean 名称清单中)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                  isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    // 若 earlySingletonExposure 为 true,通过下面代码将早期的 bean 暴露出去
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

什么是早期 bean ?

刚刚实例化好的 bean 就是早期的 bean,此时 bean 还未进行属性填充,初始化等操作

通过 addSingletonFactory 用于将早期的 bean 暴露出去,主要是将其丢到第 3 级缓存中,代码如下:

protected void addSingletonFactory(String beanName, ObjectFactory<?>
                                   singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        // 第 1 级缓存中不存在 bean
        if (!this.singletonObjects.containsKey(beanName)) {
            // 将其加入第 3 级缓存中
            this.singletonFactories.put(beanName, singletonFactory);
            // 后面的 2 行代码不用关注
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

上面的方法执行之后,serviceA 就被丢到第 3 级的缓存中了。

后续的过程 serviceA 开始注入依赖的对象,发现需要注入 serviceB,会从容器中获取 serviceB,而 serviceB 的获取又会走上面同样的过程实例化 serviceB,然后将 serviceB 提前暴露出去,然后 serviceB 开 始注入依赖的对象,serviceB 发现自己需要注入 serviceA,此时去容器中找 serviceA,找 serviceA 会先去 缓存中找,会执行 getSingleton("serviceA",true) ,此时会走下面代码:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1.先从一级缓存中找
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2.从二级缓存中找
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3.二级缓存中没找到 && allowEarlyReference为true的情况下,从三级缓存中
                找
                    ObjectFactory<?> singletonFactory =
                    this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 三级缓存返回的是一个工厂,通过工厂来获取创建bean
                    singletonObject = singletonFactory.getObject();
                    // 将创建好的bean丢到二级缓存中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 从三级缓存移除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

上面的方法走完之后,serviceA 会被放入二级缓存 earlySingletonObjects 中,会将 serviceA 返回,此 时 serviceB 中的 serviceA 注入成功,serviceB 继续完成创建,然后将自己返回给 serviceA,此时 serviceA 通过 set 方法将 serviceB 注入。 serviceA 创建完毕之后,会调用 addSingleton 方法将其加入到缓存中,这块代码如下:

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 将 bean 放入第 1 级缓存中
        this.singletonObjects.put(beanName, singletonObject);
        // 将其从第 3 级缓存中移除
        this.singletonFactories.remove(beanName);
        // 将其从第 2 级缓存中移除
        this.earlySingletonObjects.remove(beanName);
    }
}
1. 从容器中获取 serviceA
2. 容器尝试从 3 个缓存中找 serviceA,找不到
3. 准备创建 serviceA
4. 调用 serviceA 的构造器创建 serviceA,得到 serviceA 实例,此时 serviceA 还未填充属性,未进行其他
任何初始化的操作
5. 将早期的 serviceA 暴露出去:即将其丢到第 3 级缓存 singletonFactories 中
6. serviceA 准备填充属性,发现需要注入 serviceB,然后向容器获取 serviceB
7. 容器尝试从 3 个缓存中找 serviceB,找不到
8. 准备创建 serviceB
9. 调用 serviceB 的构造器创建 serviceB,得到 serviceB 实例,此时 serviceB 还未填充属性,未进行其他
任何初始化的操作
10. 将早期的 serviceB 暴露出去:即将其丢到第 3 级缓存 singletonFactories 中
11.serviceB 准备填充属性,发现需要注入 serviceA,然后向容器获取 serviceA
12. 容器尝试从 3 个缓存中找 serviceA,发现此时 serviceA 位于第 3 级缓存中,经过处理之后,serviceA 会
从第 3 级缓存中移除,然后会存到第 2 级缓存中,然后将其返回给 serviceB,此时 serviceA 通过 serviceB 中
的 setServiceA 方法被注入到 serviceB 中
13.serviceB 继续执行后续的一些操作,最后完成创建工作,然后会调用 addSingleton 方法,将自己丢到
第 1 级缓存中,并将自己从第 2 和第 3 级缓存中移除
14.serviceB 将自己返回给 serviceA
15.serviceA 通过 setServiceB 方法将 serviceB 注入进去
16.serviceB 继续执行后续的一些操作,最后完成创建工作 , 然后会调用 addSingleton 方法,将自己丢到第1 级缓存中,并将自己从第 2 和第 3 级缓存中移除

循环依赖无法解决的情况

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

那就会有下面几种情况需要注意。

是以 2 个 bean 相互依赖为例:serviceA 和 serviceB

情况 1

条件

  • serviceA: 多例

  • serviceB: 多例

结果

此时不管是任何方式都是无法解决循环依赖的问题,最终都会报错,因为每次去获取依赖的 bean 都会重新创建。

情况 2

条件

  • serviceA:单例

  • serviceB:多例

结果

若使用构造器的方式相互注入,是无法完成注入操作的,会报错。

为什么需要用 3级缓存

如果只使用 2 级缓存,直接将刚实例化好的 bean 暴露给二级缓存出是否可以解决问题呢?

原因

这样做是可以解决:早期暴露给其他依赖者的 bean 和最终暴露的 bean 不一致的问题。

若将刚刚实例化好的 bean 直接丢到二级缓存中暴露出去,如果后期这个 bean 对象被更改了,比如可能在上面加了一些拦截器,将其包装为一个代理了,那么暴露出去的 bean 和最终的这个 bean 就不一样的,将自己暴露出去的时候是一个原始对象,而自己最终却是一个代理对象,最终会导致被暴露出去的和最终的 bean 不是同一个 bean 的,将产生意向不到的效果,而三级缓存就可以发现这个问题,会报错。

实验

java Service1

@Component
public class Service1 {
    public void m1() {
        System.out.println("Service1 m1()");
    }
    @Autowired
    private Service2 service2;
}
Service2

@Component
public class Service2 {
    public void m1() {
        System.out.println("Service2 m1()");
    }
    @Autowired
    private Service1 service1;
}

在 service1 上面加个拦截器,要求在调用 service1 的任何方法之前需要先输出一行日志。

java Service1 拦截器

@Component
public class MethodBeforeInterceptor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean,
                                 String beanName) throws BeansException {
        if ("service1".equals(beanName)) {
            // 代理创建工厂,需传入被代理的目标对象
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            // 添加一个方法前置通知,会在方法执行之前调用通知中的 before 方法
            proxyFactory.addAdvice((MethodBeforeAdvice) (method, args, target) -> {
                System.out.println("你好, service1");
            });
            // 返回代理对象
            return proxyFactory.getProxy();
        }
        return bean;

    }
}

配置类

@ComponentScan
public class MainConfig { }

测试方法

public class BeanCircleTestTest {
    @Test
    void test() {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext();
        context.register(MainConfig.class);
        context.refresh();
    }
}

输出

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'service1': Bean with name 'service1' has been injected into other beans [service2] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
if (earlySingletonExposure) {
    // 调用 getSingleton(beanName, false) 方法,这个方法用来从 3 个级别的缓存中获取 bean,但是注
    // 意了,这个地方第二个参数是 false,此时只会尝试从第 1 级和第 2 级缓存中获取 bean,如果能够获取
    // 到,说明了什么?说明了第 2 级缓存中已经有这个 bean 了,而什么情况下第 2 级缓存中会有 bean?说明
    // 这个 bean 从第 3 级缓存中已经被别人获取过,然后从第 3 级缓存移到了第 2 级缓存中,说明这个早期的
    // bean 被别人通过 getSingleton(beanName, true) 获取过
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        // 这个地方用来判断早期暴露的 bean 和最终 spring 容器对这个 bean 走完创建过程之后是否还是同一
        // 个 bean,上面我们的 service1 被代理了,所以这个地方会返回 false,此时会走到
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        // allowRawInjectionDespiteWrapping 这个参数用来控制是否允许循环依赖的情况下,
        // 早期暴露给被人使用的 bean 在后期是否可以被包装
        // 通俗点理解就是:是否允许早期给别人使用的 bean 和最终 bean 不一致的情况,
        // 这个值默认是 false,表示不允许,也就是说你暴露给别人的 bean 和你最终的 bean
        // 需要是一致的,你给别人的是 1,你后面不能将其修改成 2 了啊,不一样了,你给我用个 3。
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            if (!actualDependentBeans.isEmpty()) {
                // 
                throw new BeanCurrentlyInCreationException(beanName,"Bean with name...“);
            }
        }
    }
}

而上面代码注入到 service2 中的 service1 是早期的 service1,而最终 spring 容器中的 service1 变成一个代理对象了,早期的和最终的不一致了,而 allowRawInjectionDespiteWrapping 又是 false,所以报异常了。

那么如何解决这个问题:

很简单,将 allowRawInjectionDespiteWrapping 设置为 true 就可以了,下面改一下代码如下:

@Test
void test() {
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext();
    context.addBeanFactoryPostProcessor(beanFactory -> {
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactor.setAllowRawInjectionDespiteWrapping(true);
        }
    });
    context.register(MainConfig.class);
    context.refresh();
}

上面代码中将 allowRawInjectionDespiteWrapping 设置为true了,是通过一个 BeanFactoryPostProcessor 来实现的。

现在看一下 Service1 和 Service2 中的 Bean。

你好, service1
com.example.springdemo.beancircule.Service1@387a8303
com.example.springdemo.beancircule.Service2@28cda624
你好, service1
com.example.springdemo.beancircule.Service2@28cda624
com.example.springdemo.beancircule.Service1@387a8303
Service1 m1()

可以发现 Service2 是同一个,而 Service1 并不是同一个,最后一行调用 service2.getService1().m1(); 输出的可以发现在 Service2 中的 Service1 并没有触发拦截。

继续看暴露早期 bean 的源码,注意了下面是重点:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

注意有个 getEarlyBeanReference 方法,来看一下这个方法是干什么的,源码如下:

protected Object getEarlyBeanReference(String beanName,
                                       RootBeanDefinition mbd,Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp =
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject,beanName);
            }
        }
    }
    return exposedObject;
}

从 3 级缓存中获取 bean 的时候,会调用上面这个方法来获取 bean,这个方法内部会看一下容器中是否有 SmartInstantiationAwareBeanPostProcessor 这种处理器,然后会依次调用这种处理器中的 getEarlyBeanReference 方法,那么思路来了,我们可以自定义一个 SmartInstantiationAwareBeanPostProcessor ,然后在其 getEarlyBeanReference 中来创建代理 不就可以了,聪明,我们来试试,将 MethodBeforeInterceptor 代码改成下面这样:

@Component
public class MethodBeforeInterceptor2
        implements SmartInstantiationAwareBeanPostProcessor {
    @Override
    public Object getEarlyBeanReference(Object bean,
                        String beanName) throws BeansException {
        if ("service1".equals(beanName)) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice((MethodBeforeAdvice) (method, args, target) -> {
                System.out.println("你好,service1");
            });
            return proxyFactory.getProxy();
        }
        return bean;
    }
}

测试方法

@Test
void test2() {
    AnnotationConfigApplicationContext context = 
        new AnnotationConfigApplicationContext(MainConfig.class);
    Service1 service1 = context.getBean(Service1.class);
    Service2 service2 = context.getBean(Service2.class);
    service1.m1();
    service2.m1();
    System.out.println(service1);
    System.out.println(service2.getService1());
    System.out.println(service2.getService1() == service1);
}

输出

你好,service1
Service1 m1()
Service2 m1()
你好,service1
com.example.springdemo.beancircule.Service1@38145825
你好,service1
com.example.springdemo.beancircule.Service1@38145825
true

单例 bean 解决了循环依赖,还存在什么问题?

循环依赖的情况下,由于注入的是早期的 bean,此时早期的 bean 中还未被填充属性,初始化等各种操作,也就是说此时 bean 并没有被完全初始化完毕,此时若直接拿去使用,可能存在有问题的风险。