1. 引言

本篇博客主要学习和联系Spring框架中bean的创建和销毁顺序,以及如何自定义bean的生命周期。


使用vscode部署Spring项目过程略。

2. 无依赖注入的bean的创建和销毁

2.1 创建两个独立的类

2.1.1 类LiwanLiangImpl

创建接口LiwanLiang.java

package com.liwl.dev;

import org.springframework.beans.factory.DisposableBean;

public interface LiwanLiang extends DisposableBean{
    
}

DisposableBean接口

创建实现类LiwanLiangImpl.java

package com.liwl.dev;

public class LiwanLiangImpl implements LiwanLiang{
    
    private String name;
    private int age;

    public LiwanLiangImpl(){
        System.out.println("调用LiwanLiangImpl bean 无参构造方法");
    }

    public LiwanLiangImpl(String name,int age,School school){
        System.out.println("调用LiwanLiangImpl bean 有参构造方法");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name:" + name +","+ "age:"+age;
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("LiwanLiangImpl bean 销毁");
    }
}

2.1.2 类CompanyImpl

package com.liwl.dev;

import org.springframework.beans.factory.DisposableBean;

public interface Company extends DisposableBean {
    
}
package com.liwl.dev;

public class CompanyImpl implements Company {

    private String name;
    private String address;

    public CompanyImpl(){
        System.out.println("调用CompanyImpl bean 无参构造方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String company) {
        this.name = company;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return return "[Company bean]" + ",name:" + name + ",address:" + address;
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("CompanyImpl bean 销毁");
        
    }
}

2.2 运行测试

修改springdi.xml

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="liwanliang.xml"/>
    <import resource="company.xml"/>

</beans>

修改liwanliang.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" autowire="byName">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>
</beans>

修改company.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

</beans>

修改LiwlTest.java

package com.liwl.dev;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LiwlTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springdi.xml");
        LiwanLiangImpl liwl001 = (LiwanLiangImpl) context.getBean("liwl001");
        CompanyImpl work = (CompanyImpl) context.getBean("company");
        System.out.println(liwl001);
        System.out.println(work);
        context.close();
    }
}

运行结果

调用LiwanLiangImpl bean 无参构造方法
调用CompanyImpl bean 无参构造方法
[LiwanLiangImpl bean],name:liwl001,age:30
[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号
CompanyImpl bean 销毁
LiwanLiangImpl bean 销毁

这个结果看到:

Spring先创建LiwanLiangImpl的bean,然后创建CompanyImpl的bean。销毁bean的过程,先销毁CompanyImpl bean,后销毁LiwanLiangImpl bean。

调整spingdi.xml里面内容如下

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="company.xml"/>
    <import resource="liwanliang.xml"/>

</beans>

运行结果

调用CompanyImpl bean 无参构造方法
调用LiwanLiangImpl bean 无参构造方法
[LiwanLiangImpl bean],name:liwl001,age:30
[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号
LiwanLiangImpl bean 销毁
CompanyImpl bean 销毁

从上面的配置和运行结果,可得出结论:

bean的创建顺序与xml中定义bean的一致,而销毁的顺序与xml定义bean的顺序相反

3. 通过构造方法注入的bean的创建和销毁

本节让类LiwanLiangImpl依赖SchoolImpl,SchoolImpl依赖CompanyImpl,通过测试验证Spring创建和销毁bean时的顺序

3.1 创建依赖类

3.1.1 类LiwanLiangImpl

package com.liwl.dev;

public class LiwanLiangImpl implements LiwanLiang{
    
    private String name;
    private int age;
    private School school;

    public LiwanLiangImpl(){
        System.out.println("调用LiwanLiangImpl bean 无参构造方法");
    }

    public LiwanLiangImpl(String name,int age,School school){
        System.out.println("调用LiwanLiangImpl bean 有参构造方法");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "[LiwanLiangImpl bean]" + ",name:" + name + ",age:" + age + ",school:" + school;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LiwanLiangImpl bean 销毁");
    }
}

3.1.2 类SchoolImpl

package com.liwl.dev;

public class SchoolImpl implements School{

    private String name;
    private String address;

    public SchoolImpl(){
        System.out.println("调用SchoolImpl bean 无参构造方法");
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "[SchoolImpl bean]" + ",name:" + name + ",address:" + address;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("SchoolImpl bean 销毁");
    }
}

3.2 运行测试

修改spring.xml

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="company.xml"/>
    <import resource="school.xml"/>
    <import resource="liwanliang.xml"/>

</beans>

修改LiwlTest.java

package com.liwl.dev;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LiwlTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springdi.xml");
        LiwanLiangImpl liwl001 = (LiwanLiangImpl) context.getBean("liwl001");
        System.out.println(liwl001);
        context.close();
    }
}

运行结果

调用CompanyImpl bean 无参构造方法
调用SchoolImpl bean 有参构造方法
调用LiwanLiangImpl bean 有参构造方法
[LiwanLiangImpl bean],name:liwanliang-xml,age:30,school:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号
LiwanLiangImpl bean 销毁
SchoolImpl bean 销毁
CompanyImpl bean 销毁

修改spring.xml

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="liwanliang.xml"/>
    <import resource="school.xml"/>
    <import resource="company.xml"/>

</beans>

运行结果

调用CompanyImpl bean 无参构造方法
调用SchoolImpl bean 有参构造方法
调用LiwanLiangImpl bean 有参构造方法
[LiwanLiangImpl bean],name:liwanliang-xml,age:30,school:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号
LiwanLiangImpl bean 销毁
SchoolImpl bean 销毁
CompanyImpl bean 销毁

结论:

  • 通过构造方法注入时的bean,与xml配置顺序无关
  • bean对象的创建顺序和bean的依赖顺序一致。school的创建company,所以company先创建。liwanliang依赖school,所以school先创建
  • bean对象的销毁顺序和bean创建的顺序相反。最先创建的company最后销毁,最后创建的liwanliang最先销毁

4. 通过depend-on干预bean的创建和销毁顺序

通过xml配置,可以设置无依赖bean的创建顺序。通过强依赖,也可以调整bean的创建顺序。

对于大量不存在强依赖关系的bean,如果想要调整bean的创建和销毁顺序,如果要调整xml或者强依赖,这样的操作不够优雅。

Spring提供了depend-on的方式来解决这样的问题。

depend-on的使用方式

<bean id="bean1" class="类全名" depend-on="bean2,bean3,bean4" />

即通过depend on标签设置当前bean1的依赖,多个依赖用逗号隔开。

此时无论bean2,bean3,bean4在任何地方,都可以确保bean1在创建之前,先把bean2,bean3,bean4创建好。

bean1可能会依赖于bean2,bean3,bean4中的一些资源或者其他功能,但又没有强制去bean1类中通过属性定义去依赖bean2,bean3,bean4

本节让类LiwanLiangImpl依赖SchoolImpl,CompanyImpl,通过测试验证depend-on对创建和销毁bean时的顺序干预

4.1 修改LiwanLiangImpl

package com.liwl.dev;

public class LiwanLiangImpl implements LiwanLiang{
    
    private String name;
    private int age;
    private School school;
    private Company company;

    public LiwanLiangImpl(){
        System.out.println("调用LiwanLiangImpl bean 无参构造方法");
    }

    public LiwanLiangImpl(String name,int age,School school){
        System.out.println("调用LiwanLiangImpl bean 有参构造方法");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


    @Override
    public String toString() {
        return "[LiwanLiangImpl bean]" + ",name:" + name + ",age:" + age + ",school:" + school + ",company:" + company;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LiwanLiangImpl bean 销毁");
    }
}

4.2 运行测试

修改sprindi.xml

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byName">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

    <bean id="school" class="com.liwl.dev.SchoolImpl">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>


</beans>

运行结果如下

调用SchoolImpl bean 无参构造方法
调用CompanyImpl bean 无参构造方法
调用LiwanLiangImpl bean 无参构造方法
[LiwanLiangImpl bean],name:liwl,age:30,school:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号,company:[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号
LiwanLiangImpl bean 销毁
CompanyImpl bean 销毁
SchoolImpl bean 销毁

修改springdi.xml

<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.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="school" class="com.liwl.dev.SchoolImpl">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byName">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

</beans>

结论:SchoolImpl和CompanyImpl被LiwanLiangImpl依赖,他们先于LiwanLiangImpl创建,而销毁则是晚于LiwanLiangImpl。

5. 总结

  • bean的创建顺序有3种:
  • 无依赖的bean,bean的创建跟xml定义的顺序有关
  • 有依赖的bean,先创建bean 依赖的其他bean
  • 大量不知道是否有依赖的bean,通过depend-on指定依赖关系,depend-on指定的bean先创建
  • bean的销毁顺序只有1种:
  • 先创建的后销毁,后创建的先销毁