1. 什么是depend-on?
depend-on用来设置bean之前的依赖关系,从而干预bean的创建和销毁顺序。
2. 无依赖bean的创建和销毁顺序
先观察没有任何依赖关系的bean的创建和销毁顺序。
下面的xml中定义了3个bean,bean之前没有任何依赖关系:
<bean id="bean3" class="test.NormalBean.Bean3"/>
<bean id="bean2" class="test.NormalBean.Bean2"/>
<bean id="bean1" class="test.NormalBean.Bean1"/>
注意上面xml中bean的定义顺序是:
bean3,bean2,bean1
对应的java代码如下:
package test;
import org.springframework.beans.factory.DisposableBean;
// 没有任何依赖关系的bean之间的创建和销毁顺序
public class NormalBean {
public static class Bean1 implements DisposableBean {
public Bean1() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean2 implements DisposableBean {
public Bean2() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean3 implements DisposableBean {
public Bean3() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
}
上面代码中使用到了DisposableBean接口。
这是spring容器提供的一个钩子接口,这个接口有一个destroy方法,我们自定义的bean可以实现这个接口,当我们调用spring容器的close方法关闭容器时,spring会调用勇气中所有bean的destroy方法,用来做一些清理的工作。
后面会详细介绍该接口。
上面几个内部Bean的构造方法和destroy方法随着目标bean被spring实例化和spring容器销毁,都会执行对应的输出语句。
最后我们搞一个测试程序,来验证当spring容器启动和销毁时,上述3个无依赖bean的创建和销毁的顺序:
package test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
/**
* 无依赖的bean创建和销毁顺序
*/
@Test
public void testDependOn() {
System.out.println("容器启动中...");
String beanXml = "classpath:/test/applicationContext.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
System.out.println("容器启动完毕,准备关闭spring容器!");
// 关闭spring容器
context.close();
System.out.println("spring容器已关闭!");
}
}
运行上面的testDependOn方法,得到如下输出:
容器启动中...
class test.NormalBean$Bean3 constructor!
class test.NormalBean$Bean2 constructor!
class test.NormalBean$Bean1 constructor!
容器启动完毕,准备关闭spring容器!
class test.NormalBean$Bean1 destroy!
class test.NormalBean$Bean2 destroy!
class test.NormalBean$Bean3 destroy!
spring容器已关闭!
通过上面无依赖的3个bean的定义结合输出结果,可以对比一下:
bean定义顺序:bean3,bean2,bean1
bean创建顺序:bean3,bean2,bean1
bean销毁顺序:bean1,bean2,bean3
对比后可以得出如下结论:
- bean对象的创建顺序和bean xml中定义的顺序一致
- bean销毁的顺序和bean xml中定义的顺序相反
3. 通过构造器进行强依赖的bean的创建和销毁顺序
改造下上面的NormalBean这个class,通过构造方法注入的方式使得bean之间产生强依赖关系。
让bean2依赖bean1,bean3依赖bean2。
package test;
import org.springframework.beans.factory.DisposableBean;
// 没通过构造方法强依赖的bean之间的创建和销毁顺序
public class NormalBean {
public static class Bean1 implements DisposableBean {
public Bean1() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean2 implements DisposableBean {
private Bean1 bean1;
// 创建bean2时通过构造器传入一个bean1对象啊,此时bean2对bean1产生了强依赖。
public Bean2(Bean1 bean1) {
this.bean1 = bean1;
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean3 implements DisposableBean {
private Bean2 bean2;
// 同样,通过构造器在创建bean3对象时注入一个bean2对象,此时bean3强依赖bean2.
public Bean3(Bean2 bean2) {
this.bean2 = bean2;
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
}
对应的xml配置修改如下:
<bean id="bean3" class="test.NormalBean.Bean3">
<constructor-arg index="0" ref="bean2"/>
</bean>
<bean id="bean2" class="test.NormalBean.Bean2">
<constructor-arg index="0" ref="bean1"/>
</bean>
<bean id="bean1" class="test.NormalBean.Bean1"/>
上述xml中bean的配置顺序是:
bean3,bean2,bean1.
其中bean3依赖bean2,bean2依赖bean1.
强依赖关系是:
bean3->bean2>bean1.
执行测试用例,结果如下:
容器启动中...
class test.NormalBean$Bean1 constructor!
class test.NormalBean$Bean2 constructor!
class test.NormalBean$Bean3 constructor!
容器启动完毕,准备关闭spring容器!
class test.NormalBean$Bean3 destroy!
class test.NormalBean$Bean2 destroy!
class test.NormalBean$Bean1 destroy!
spring容器已关闭!
得出如下结果:
bean定义顺序:bean3,bean2,bean1
bean创建顺序:bean1,bean2,bean3
bean销毁顺序:bean3,bean2,bean1
从输出结果得出如下结论:
- bean对象的创建顺序和bean依赖的对象一致,比如bean3依赖bean2,bean2依赖bean1,则先创建bean1,再创建bean2,最后创建bean3.
- bean销毁的顺序和bean创建的顺序相反。
4. 通过setter注入依赖的bean的创建和销毁
修改NormalBean,通过setter注入依赖:
package test;
import org.springframework.beans.factory.DisposableBean;
// 通过setter方法强依赖的bean之间的创建和销毁顺序
public class NormalBean {
public static class Bean1 implements DisposableBean {
public Bean1() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean2 implements DisposableBean {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean2() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
public static class Bean3 implements DisposableBean {
private Bean2 bean2;
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}
public Bean3() {
System.out.println(this.getClass() + " constructor!");
}
@Override
public void destroy() throws Exception {
System.out.println(this.getClass() + " destroy!");
}
}
}
对应的xml修改如下:
<bean id="bean3" class="test.NormalBean.Bean3">
<property name="bean2" ref="bean2"/>
</bean>
<bean id="bean2" class="test.NormalBean.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
<bean id="bean1" class="test.NormalBean.Bean1"/>
执行测试用例,结果如下:
容器启动中...
class test.NormalBean$Bean3 constructor!
class test.NormalBean$Bean2 constructor!
class test.NormalBean$Bean1 constructor!
容器启动完毕,准备关闭spring容器!
class test.NormalBean$Bean3 destroy!
class test.NormalBean$Bean2 destroy!
class test.NormalBean$Bean1 destroy!
spring容器已关闭!
得出如下结果:
bean定义顺序:bean3,bean2,bean1
bean创建顺序:bean3,bean2,bean1
bean销毁顺序:bean3,bean2,bean1
这是为什么呢?
4. 通过depend-on干预bean创建和销毁顺序
对于没有任何依赖关系的bean之间,通过定义的顺序确实可以干预bean的创建顺序,通过强依赖也可以干预bean的创建顺序。
如果xml中定义的bean特别多,而有些bean之间也没有强依赖关系,此时如果想要调整bean的创建顺序,就必须调整xml的顺序结构,或者修改java代码去加强依赖关系,这样非常不友好。
spring中通过depend-on来解决这些问题,在不调整bean文件顺序和加强依赖的情况下,可以通过depend-on属性来配置当前的bean依赖于哪些bean,那么可以保证depend-on指定的bean在当前bean创建之前先创建好,销毁的时候在当前bean销毁之后再销毁。
depend-on使用方式:
<bean id="bean1" class="xxxx" depends-on="bean2,bean3;bean4 bean5">
depend-on:设置当前bean依赖的bean名称,可以指定多个,多个bean之间用”,;空格”进行分隔
上面不管bean2,bean3,bean4,bean5在任何地方定义,都可以确保在bean1创建之前会先将bean2,bean3,bean4,bean5创建好,表示bean1依赖于这4个bean。
可能bean1需要用到bean2,bean3,bean4,bean5中生成的一些资源或者用到其他的功能等,但是又没有强制在bean1中通过属性定义强依赖的方式去依赖bean2,bean3,bean4,bean5。
销毁时也会先销毁当前bean,再去销毁被依赖的bean,即先销毁bean1,再去销毁depend-on依赖的bean。
修改最开始无依赖的bean的xml:
<bean id="bean3" class="test.NormalBean.Bean3" depends-on="bean2">
</bean>
<bean id="bean2" class="test.NormalBean.Bean2" depends-on="bean1">
</bean>
<bean id="bean1" class="test.NormalBean.Bean1"/>
执行测试用例,测试结果如下:
容器启动中...
class test.NormalBean$Bean1 constructor!
class test.NormalBean$Bean2 constructor!
class test.NormalBean$Bean3 constructor!
容器启动完毕,准备关闭spring容器!
class test.NormalBean$Bean3 destroy!
class test.NormalBean$Bean2 destroy!
class test.NormalBean$Bean1 destroy!
spring容器已关闭!
得出如下结果:
bean定义顺序:bean3,bean2,bean1
bean创建顺序:bean1,bean2,bean3
bean销毁顺序:bean3,bean2,bean1
5. 总结
- 无依赖的bean的创建顺序和定义的顺序一致,销毁顺序刚好相反。
- 通过构造器强依赖的bean,会先创建构造器参数中对应的bean,然后才会创建当前bean,销毁顺序刚好相反。
- depend-on可以指定当前bean依赖的bean,通过这个可以确保depend-on指定的bean在当前bean创建之前先创建好,销毁顺序刚好相反。
- bean的销毁顺序和bean创建顺序相反。
- depend-on最好使用在本身无依赖关系的bean之间,如果bean本身通过构造方法或者setter方法之间产生了强依赖,则不应该再使用depend-on去手动指定bean之间的依赖关系。
文档信息
- 本文作者:Marshall