spring 之 lazy-init Autowired depends-on

时间:2022-04-13 15:07:32

1 lazy-init

lazy-init是延迟初始化的意思。

spring中容器都是尽早的创建和配置所有的单例bean,因此当容器在启动时,就会去配置和创建单例bean。  默认情况下 beans 的lazy-init 是没有配置的,就相当于是:

default-lazy-init="false"

 bean 的 lazy-init 默认继承于 beans 的配置。 也就是说, 不配置任何lazy-init 的情况下: lazy-init = false 

这样做的好处是在程序刚运行时就可以将配置的错误或者环境问题立刻暴露出来。当然,坏处就是启动时,因为要初始化所有的单例bean,系统开销会很大,启动过程比较慢。

如果不想单例bean提前实例化,可以设置lazy-initialized延迟加载,只有在第一次请求的时候采取初始化,而不是在启动容器时初始化。

如果一个非延迟的单例bean依赖了标记了lazy-init的bean,那么这个标记了lazy-init的bean也会在容器启动时被创建(延迟初始化失效)。 
设置为延迟加载的bean一旦注入给不延迟的单例bean,就意味着它并不会被延迟加载了。

可以看到代码中对所有注册的bean,即this.beanDefinitionNames,对于每个bean都会做如下判断,如果成立就会执行依赖注入:

容器的初始化是在AbstractApplicationContext的refresh()方法中执行的,如下代码对lazy-init进行了处理:

  1. finishBeanFactoryInitialization(beanFactory);

跟踪下去可以找到真正的读取lazy-init属性进行懒加载相关处理的地方

  1. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit())

可以看出,只有单例的bean才有可能在容器初始化的时候就完成依赖注入,当lazy-init属性不配置(默认值)或者配置为false的时候,上述if就会成立,当然这里默认不配置abstract属性,所以它默认也是false。if成立,就会执行getBean从而进行依赖注入,这样在容器初始化的过程中就已经实例化了Bean,当真正的请求bean的时候,其实只是从缓存中读取而已。

而如果lazy-init属性配置为true,那么就会进行懒加载了,这样在容器初始化的过程中不会进行依赖注入,只有当第一个getBean的时候才会实例化Bean。

2 lazy-init & depends-on

    <!-- 移除 boss Bean 的属性注入配置的信息 -->
<bean id="boss" class="com.baobaotao.Boss" lazy-init="true"/> <!-- depends-on 测试 ,去掉 lazy-init配置后,也就是设置为 fasle, 那么 -->
<bean id="man" class="com.baobaotao.Man" depends-on="boss" lazy-init="true"/>

depends-on 是表明一种初始化的先后顺序,它 和 直接 直接的属性依赖(比如 Man 拥有一个 boss 属性)还是不一样的。 但是表现是差不多的。

具体来说, 就上面的例子, 如果 man 是延迟初始化的, 那么 boss 是否延迟初始化的都不要紧。 但是 如果 mans 是立即初始化的,那么 boss 也会立即被要求初始化, 即使 boss 设置了 lazy-init="true"。 如果boss 无法autowire 某些属性,那么容器就会抛出异常, man也就无法再完成初始化了!

为什么会这样呢? 观察源码可见:

AbstractBeanFactory.doGetBean 方法:

                final RootBeanDefinition ex1 = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(ex1, beanName, args);
String[] dependsOn = ex1.getDependsOn();
String[] scopeName;
if(dependsOn != null) {  // 这里开始对 bean 所依赖的其他bean 进行处理
scopeName = dependsOn;
int scope = dependsOn.length; for(int ex2 = ; ex2 < scope; ++ex2) {
String dep = scopeName[ex2];
if(this.isDependent(beanName, dep)) {
throw new BeanCreationException(ex1.getResourceDescription(), beanName, "Circular depends-on relationship between \'" + beanName + "\' and \'" + dep + "\'");
} this.registerDependentBean(dep, beanName);
this.getBean(dep);
}
}

另外, 从异常堆栈中, 可见其中存在getBean 和 doGetBean 方法的反复的相互调用,而这个正是 spring 在处理 depends-on 依赖:

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:) 这里出现了反复相互调用
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:)

3 lazy-init & Autowired 注解

lazy-init 和 Autowired  其实并没有明确的关系。 autowired 负责注入, 而lazy-init 关系到 初始化。 仅有的一点关系是: autowired 注入的 bean 必须是已经初始化成功了的bean。 换句话说, autowired 就相当于调用了 getBean,那么不管lazy-init如何设置, 被autowire需要的被依赖的那个bean 必须初始化。

如果一个bean 需要autowire 注入 另一个无法的bean,那么但是,这个bean 不要求立即初始化, 那么这个autowire 潜在的错误是不会暴露出来的。 也就是说, 推迟了可能出现的异常。

参考:

http://blog.csdn.net/qq30211478/article/details/77847677?locationNum=4&fps=1

http://blog.csdn.net/u011734144/article/details/72632327?locationNum=8&fps=1  : Spring源码分析之lazy-init属性的配置

lazy-init 有3个选项, true, false, default