1. spring对bean的实例化一般有两种
1.实时初始化bean
2.延迟初始化bean
注意,这里的“延迟初始化”中的“初始化”实际上是习惯性叫法,正确理解应该是“延迟实例化”。
2. bean实时初始化
在容器启动过程中被创建组装好的bean,称为实时初始化的bean。
spring中默认定义的bean都是实时初始化的bean,这些bean默认都是单例的,在容器启动过程中会被创建好,然后放在spring容器中以供使用。
实时初始化bean的有一些优点:
1.更早发下bean定义的错误:实时初始化的bean如果定义有问题,会在容器启动过程中会抛出异常,让开发者快速发现问题。
2.查找bean更快:容器启动完毕之后,实时初始化的bean已经完全创建好了,此时被缓存在spring容器中,当我们需要使用的时候,容器直接返回就可以了,速度是非常快的。
3. 案例-默认单例bean的实时初始化
zeh.myspring.spring1xml.code10lazyinit.ActualTimeBean
package zeh.myspring.spring1xml.code10lazyinit;
// 实时初始化的单例bean
public class ActualTimeBean {
// 一会我们在spring中创建这个对象,构造函数中会输出一段话,这段话会在spring容器创建过程中输出。
public ActualTimeBean() {
System.out.println("我是实时初始化的bean!");
}
}
这个类在构造器中输出了一句话,用于我们后续的验证。
如果在spring容器启动后这句话就输出了,说明在spring容器启动过程中这个bean就已经被创建了。
否则,说明没有创建。
定义spring配置文件:
springxml/code10-applicationContext-actual-time.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 实时初始化的单例bean -->
<bean id="actualTimeBean" class="zeh.myspring.spring1xml.code10lazyinit.ActualTimeBean"/>
</beans>
测试:
zeh.myspring.spring1xml.code10lazyinit.RunLazyInitJunit
package zeh.myspring.spring1xml.code10lazyinit;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import zeh.myspring.spring1xml.code00common.LoadSpringConfigFactory;
public class RunLazyInitJunit {
// 测试实时初始化的bean。
@Test
public void testActualTimeBean() {
// 从结果可以看到:默认情况下,bean是单例的,是实时初始化的,在spring容器启动过程中就预加载了bean。
System.out.println("spring容器启动中...");
// 启动spring容器
LoadSpringConfigFactory.getApplicationContext("springxml/code10-applicationContext-actual-time.xml");
System.out.println("spring容器启动完毕...");
}
}
输出日志:
spring容器启动中...
我是实时初始化的bean!
spring容器启动完毕...
从日志可以看出,容器启动前后,有“我是实时初始化的bean!”这句日志输出,说明actualTimeBean这个bean是在容器启动过程中就被创建好的。
注意:
bean默认是实时初始化的,可以通过bean元素的lazy-init="true"将bean定义为延迟初始化。
bean默认是单例的,只有单例bean才有实时初始化和延迟初始化之分,多例bean都是等客户端去容器中查找的时候才进行延迟初始化的,要不然怎么能多例呢。
也就是说多例bean默认都是延迟初始化的,对多例bean设置lazy-init="true"或者lazy-init="false"毫无意义。
4. 案例-延迟初始化的bean
实时初始化的bean都会在容器启动过程中创建好。
如果程序中定义的bean非常多,并且有些bean创建的过程中比较耗时的时候,会导致系统消耗的资源比较多,并且会让整个启动时间比较长。
这个我估计大家都是有感受的,使用spring开发的系统比较大的时候,整个系统启动耗时是比较长的,基本上多数时间都是在创建和组装bean。
spring对这些问题也提供了解决方案: bean延迟初始化。
所谓延迟初始化,就是和实时初始化刚好相反,延迟初始化的bean在容器启动过程中不会创建,而是需要使用的时候才会去创建,先说一下bean什么时候会被使用:
(1)被其他bean作为依赖进行注入的时候,比如通过property元素的ref属性进行引用,通过构造器注入、通过set注入、通过自动注入,这些都会导致被依赖bean的创建。
(2)开发者自己写代码向容器中查找bean的时候,如调用容器的getBean方法获取bean。
上面这2种情况会导致延迟初始化的bean被创建。
延迟初始化bean的spring配置: 在bean定义的时候通过lazy-init属性来配置bean是否是延迟加载,true:延迟初始化,false:实时初始化:
<bean lazy-init="是否是延迟初始化" />
4.1 案例1
zeh.myspring.spring1xml.code10lazyinit.LazyInitBean:
package zeh.myspring.spring1xml.code10lazyinit;
// 延迟初始化的单例bean
public class LazyInitBean {
public LazyInitBean() {
System.out.println("我是延迟初始化的bean!");
}
}
spring配置:
springxml/code10-applicationContext-lazy-init.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 延迟初始化的单例bean -->
<!-- 在bean定义的时候通过lazy-init属性来配置bean是否是延迟加载,true:延迟初始化,false:实时初始化-->
<bean id="lazyInitBean" class="zeh.myspring.spring1xml.code10lazyinit.LazyInitBean" lazy-init="true"/>
</beans>
注意上面的lazy-init=”true”表示定义的这个bean是延迟初始化的bean。
测试:在上面的测试类中加一个方法:
@Test
public void testLazyInitBean() {
System.out.println("spring容器启动中...");
ApplicationContext context = LoadSpringConfigFactory.getApplicationContext("springxml/code10-applicationContext-lazy-init.xml");
System.out.println("spring容器启动完毕...");
System.out.println("从容器中开始查找LazyInitBean");
// 代码结合输出可以看出来,LazyInitBean在容器启动过程中并没有创建,当我们调用context.getBean方法的时候,LazyInitBean才被创建的。
LazyInitBean lazyInitBean = context.getBean(LazyInitBean.class);
System.out.println("LazyInitBean:" + lazyInitBean);
}
日志:
spring容器启动中...
spring容器启动完毕...
从容器中开始查找LazyInitBean
我是延迟初始化的bean!
LazyInitBean:zeh.myspring.spring1xml.code10lazyinit.LazyInitBean@2b552920
从结果可以看出:
LazyInitBean在容器启动过程中并没有创建,当我们调用context.getBean方法的时候,LazyInitBean才被主动创建的。
4.2 案例2
上面这种方式是我们主动从容器中获取bean的时候,延迟初始化的bean才被容器创建的。
下面我们再来看一下当延迟初始化的bean被其他实时初始化的bean依赖的时候,是什么时候创建的。
zeh.myspring.spring1xml.code10lazyinit.ActualTimeDependencyLazyBean
package zeh.myspring.spring1xml.code10lazyinit;
// 实时初始化的bean依赖延迟初始化的bean
public class ActualTimeDependencyLazyBean {
public ActualTimeDependencyLazyBean() {
System.out.println("ActualTimeDependencyLazyBean实例化!");
}
private LazyInitBean lazyInitBean;
public LazyInitBean getLazyInitBean() {
return lazyInitBean;
}
public void setLazyInitBean(LazyInitBean lazyInitBean) {
this.lazyInitBean = lazyInitBean;
System.out.println("ActualTimeDependencyLazyBean.setLazyInitBean方法!");
}
}
spring配置:
springxml/code10-applicationContext-actual-depend-lazy.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 延迟初始化的单例bean -->
<!-- 在bean定义的时候通过lazy-init属性来配置bean是否是延迟加载,true:延迟初始化,false:实时初始化-->
<bean id="lazyInitBean" class="zeh.myspring.spring1xml.code10lazyinit.LazyInitBean" lazy-init="true"/>
<!-- 实时初始化的单例bean依赖延迟初始化的单例bean-->
<!-- actualTimeDependencyLazyBean:通过property元素来注入lazyInitBean。-->
<!-- actualTimeDependencyLazyBean中没有指定lazy-init,默认为false,表示是实时创建的bean,会在容器创建过程中被初始化。-->
<bean id="actualTimeDependencyLazyBean" class="zeh.myspring.spring1xml.code10lazyinit.ActualTimeDependencyLazyBean">
<property name="lazyInitBean" ref="lazyInitBean"/>
</bean>
</beans>
测试:
// 上面的方式是我们主动从容器中获取bean的时候,延迟初始化的bean才被容器创建的。
// 下面我们再来看一下当延迟初始化的bean被其他实时初始化的bean依赖的时候,是什么时候创建的。
@Test
public void testActualTimeDependencyLazyBean(){
System.out.println("spring容器启动中...");
// 启动spring容器
LoadSpringConfigFactory.getApplicationContext("springxml/code10-applicationContext-actual-depend-lazy.xml");
System.out.println("spring容器启动完毕...");
}
日志:
spring容器启动中...
ActualTimeDependencyLazyBean实例化!
我是延迟初始化的bean!
ActualTimeDependencyLazyBean.setLazyInitBean方法!
spring容器启动完毕...
从容器中可以看到,xml中定义的2个bean都在容器启动过程中被创建好了。
有些朋友比较迷茫,lazyInitBean的lazy-init为true,怎么也在容器启动过程中被创建呢?
由于actualTimeDependencyLazyBean为实时初始化的bean,而这个bean在创建过程中需要用到lazyInitBean。
此时容器会去查找lazyInitBean这个bean,然后会进行初始化,所以这2个bean都在容器启动过程中被创建的。
5. 总结
延迟初始化的bean无法在程序启动过程中迅速发现bean定义的问题,第一次获取的时候可能耗时会比较长。在实际工作中用的比较少,作为了解,以后碰到的时候会有个印象。
实时初始化和延迟初始化只针对单例bean。多例bean设置延迟配置无效,默认只能是延迟初始化的。
文档信息
- 本文作者:Marshall