depend-on这玩意到底是干嘛的?

2022/01/09 spring专题 共 7265 字,约 21 分钟
闷骚的程序员

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

对比后可以得出如下结论:

  1. bean对象的创建顺序和bean xml中定义的顺序一致
  2. 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

从输出结果得出如下结论:

  1. bean对象的创建顺序和bean依赖的对象一致,比如bean3依赖bean2,bean2依赖bean1,则先创建bean1,再创建bean2,最后创建bean3.
  2. 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. 总结

  1. 无依赖的bean的创建顺序和定义的顺序一致,销毁顺序刚好相反。
  2. 通过构造器强依赖的bean,会先创建构造器参数中对应的bean,然后才会创建当前bean,销毁顺序刚好相反。
  3. depend-on可以指定当前bean依赖的bean,通过这个可以确保depend-on指定的bean在当前bean创建之前先创建好,销毁顺序刚好相反。
  4. bean的销毁顺序和bean创建顺序相反。
  5. depend-on最好使用在本身无依赖关系的bean之间,如果bean本身通过构造方法或者setter方法之间产生了强依赖,则不应该再使用depend-on去手动指定bean之间的依赖关系。

文档信息

Search

    Table of Contents