Spring中管理Bean依赖注入之后和Bean销毁之前的行为

简介:

    对于Singleton作用域的Bean,Spring容器将会跟踪它们的生命周期,容器知道何时实例化结束、何时销毁。Spring可以管理Bean在实例化结束之后和Bean销毁之前的行为。

Bean依赖关系注入之后的行为:

    Spring提供了两种方式在Bean全部属性设置成功后执行特定的行为:

  • 在Spring配置文件中使用init-method属性:这个属性指定某个方法在Bean全部依赖关系设置结束后自动执行。这个方法写在Bean里面。使用这种方法不需要将代码与Spring耦合在一起,代码污染小,推荐使用。

  • 让Bean实现InitializingBean接口:该接口提供了一个afterPropertiesSet() throwsException方法,在Bean里面实现它。

    Spring容器会在为该Bean注入依赖关系后,调用该Bean实现的afterPropertiesSet方法。先看例子:

?
1
2
3
public  interface  Animal {
     public  void  eatFood();
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public  class  Dog  implements  Animal, InitializingBean {
     private  Food food;
     public  Dog() {
         System.out.println( "Spring实例化主调Bean...Dog实例" );
     }
     public  void  setFood(Food food) {
         System.out.println( "Spring执行依赖关系注入..." );
         this .food = food;
     }
     
     //Animal的方法
     @Override
     public  void  eatFood() {
         System.out.println(food.getName() +  "真好吃" );
     }
     
     //自定义的用于在Spring中配置的当Bean初始化完成时调用的方法
     public  void  init() {
         System.out.println( "正在执行初始化:init方法..." );
     }
     
     //实现InitializingBean接口中的方法
     @Override
     public  void  afterPropertiesSet()  throws  Exception {
         System.out.println( "正在执行初始化:afterPropertiesSet方法..." );
     }
}

?
1
2
3
4
5
6
7
8
9
10
11
12
public  class  Food {
     private  String name;
     public  void  setName(String name) {
         this .name = name;
     }
     public  String getName() {
         return  name;
     }
     public  Food() {
         System.out.println( "Spring实例化依赖Bean..." );
     }
}

    上面的程序中定义了一个普通的init方法,实际上这个方法名是任意的,并不一定叫init,Spring也不会对这个init方法进行任何特别的处理。只是接下来会在Spring配置文件中使用init-method属性指定该方法是一个“生命周期”方法。

    增加init-method="init" 来指定init方法应在Bean的全部属性设置结束后自动执行,如果它不实现InitializingBean接口,上面的Dog类没有实现任何Spring接口,只是增加了一个普通的init方法。它依然是一个普通的Java文件,代码没有污染。下面是Spring配置文件:

?
1
2
3
4
5
6
7
<!-- 使用init-method="init"来指定Bean的全部属性设置结束后执行的方法 -->
< bean  id = "dog"  class = "com.abc.Dog"  init-method = "init" >
     < property  name = "food"  ref = "food"  />
</ bean >
< bean  id = "food"  class = "com.abc.Food" >
     < property  name = "name"  value = "骨头"  />
</ bean >

    使用主程序获取、并调用Dog类的eatFood方法:

?
1
2
3
4
5
6
7
public  class  Test {
     public  static  void  main(String args[]) {
         ApplicationContext context =  new  ClassPathXmlApplicationContext( "applicationContext.xml" );
         Dog d = context.getBean( "dog" , Dog. class );
         d.eatFood();
     }
}

    输出结果:

?
1
2
3
4
5
6
Spring实例化依赖Bean...Food实例
Spring实例化主调Bean...Dog实例
Spring执行依赖关系注入...
正在执行初始化:afterPropertiesSet方法...
正在执行初始化:init方法...
骨头真好吃

    通过上面的例子可以看出:当Spring将Food注入到Dog之后——也就是完成依赖之后,程序先调用afterPropertiesSet方法进行初始化,在调用init-method中指定的方法进行初始化。

    对于实现InitializingBean接口的Bean,无需使用init-method属性来指定初始化方法,配置该Bean实例与普通Bean实例完全一样,Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并决定是否需要执行生命周期方法。Spring在为Bean完成注入所有依赖关系后,会自动调用该Bean实现的afterProperties方法。但InitializingBean接口污染了代码,是侵入式设计,因此不推荐使用。

    另外,从上面的执行结果可以看出,如果既实现了InitializingBean接口又使用了init-method来指定初始化方法,那么两个初始化方法都会被执行,先执行接口中的afterProperties方法,再执行自定义的初始化方法。


Bean销毁之前的行为:

    与定制初始化行为相似,Spring也提供了两种方式定制在Bean销毁之前的特定行为:

  • 使用destroy-method属性:指定某个方法在Bean销毁之前被自动执行。使用这种方法,不需要将代码与Spring的接口耦合在一起,代码污染小,推荐使用。

  • 实现DisposableBean接口:该接口提供了一个destroy() throws Exception的方法。在Bean里面实现它,这个方法将在Bean销毁之前被Spring调用。

    例子与前文相似,这里不赘述。

    singleton作用域的Bean通常会随着容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则需要在JVM里面注册一个关闭钩子(shutdown hook),这样就可以保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。例如:

?
1
2
3
4
5
6
7
8
9
public  class  Test {
     public  static  void  main(String args[]) {
         ApplicationContext context =  new  ClassPathXmlApplicationContext( "applicationContext.xml" );
         Dog d = context.getBean( "dog" , Dog. class );
         d.eatFood();
         //为Spring容器注册关闭钩子
         context.registerShutdownHook();
     }
}

    

    除此之外,如果Spring容器中很多Bean都需要指定特定的生命周期行为,则可以考虑使用<beans>的default-init-method属性和default-destroy-method属性,这两个属性的作用类似于<bean>的init-method和destroy-method属性的作用。但由于前两个属性是<beans>标签的,因此对标签中的所有Bean都有效。即:如果<beans>标签中的Bean配置了default-init-method="init",那么如果<beans>标签中的Bean配置了init方法,则该方法会被自动调用。

    下图显示了Spring容器中Bean实例完整的生命周期行为:

202035_BkTC_1434710.jpg

    需要指出的是,当Bean实现了ApplicationAware、BeanNameAware接口之后,Spring容器会在该Bean初始化完成之后——也就是init-method属性指定的方法(如果有)之后,再来回调setApplicationContext(ApplicationContext context)和setBeanName(String beanName)方法。

目录
相关文章
|
18天前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
30 1
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
44 0
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
2天前
|
Java 数据库连接 开发者
浅谈Spring的Bean生命周期
浅谈Spring的Bean生命周期
10 1
|
7天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
17 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
17天前
|
XML Java 程序员
作为Java程序员还不知道Spring中Bean创建过程和作用?
作为Java程序员还不知道Spring中Bean创建过程和作用?
12 0
|
22天前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
17 0
|
1月前
|
Java Spring
Spring5深入浅出篇:bean的生命周期
Spring5深入浅出篇:bean的生命周期
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)