写在前面
在 [String 注解驱动开发专题] 中, 前面的文章我们主要讲了有关于如何向 Spring 容器中注册 bean 的知识, 大家可以到 [String 注解驱动开发专题] 中系统学习. 接下来, 我们继续肝 Spring, 只不过从本篇文章开始, 我们就进入 Spring 容器中有关 Bean 的生命周期的学习.
项目工程源码已经提交到 GitHub: https://github.com/sunshinelyz/spring-annotation
Bean 的生命周期
通常意义上讲的 bean 的名称周期, 指的是 bean 从创建到初始化, 经过一系列的流程, 最终销毁的过程. 只不过, 在 Spring 中, bean 的生命周期是由 Spring 容器来管理的. 在 Spring 中, 我们可以自己来指定 bean 的初始化和销毁的方法. 当我们指定了 bean 的初始化和销毁方法时, 当容器在 bean 进行到当前生命周期的阶段时, 会自动调用我们自定义的初始化和销毁方法.
如何定义初始化和销毁方法?
我们已经知道了由 Spring 管理 bean 的生命周期时, 我们可以指定 bean 的初始化和销毁方法, 那具体该如何定义这些初始化和销毁方法呢? 接下来, 我们就介绍第一种定义初始化和销毁方法的方式: 通过 @Bean 注解指定初始化和销毁方法.
如果是使用 xml 文件的方式配置 bean 的话, 可以在标签中指定 bean 的初始化和销毁方法, 如下所示.
- <bean id = "person" class="io.mykit.spring.plugins.register.bean.Person" init-method="init" destroy-method="destroy">
- <property name="name" value="binghe"></property>
- <property name="age" value="18"></property>
- </bean>
这里, 需要注意的是, 在我们写的 Person 类中, 需要存在 init() 方法和 destroy() 方法. 而且 Spring 中规定, 这里的 init() 方法和 destroy() 方法必须是无参方法, 但可以抛异常.
如果我们使用注解的方式, 该如何实现指定 bean 的初始化和销毁方法呢? 接下来, 我们就一起来搞定它!!
首先, 创建一个名称为 Student 的类, 这个类的实现比较简单, 如下所示.
- package io.mykit.spring.plugins.register.bean;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 测试 bean 的初始化和销毁方法
- */
- public class Student {
- public Student(){
- System.out.println("Student 类的构造方法");
- }
- public void init(){
- System.out.println("初始化 Student 对象");
- }
- public void destroy(){
- System.out.println("销毁 Student 对象");
- }
- }
接下来, 我们将 Student 类对象通过注解的方式注册到 Spring 容器中, 具体的做法就是新建一个 LifeCircleConfig 类作为 Spring 的配置类, 将 Student 类对象通过 LifeCircleConfig 类注册到 Spring 容器中, LifeCircleConfig 类的代码如下所示.
- package io.mykit.spring.plugins.register.config;
- import io.mykit.spring.plugins.register.bean.Student;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- /**
- * @author binghe
- * @version 1.0.0
- * @description Bean 的生命周期
- */
- @Configuration
- public class LifeCircleConfig {
- @Bean
- public Student student(){
- return new Student();
- }
- }
接下来, 我们就新建一个 BeanLifeCircleTest 类来测试容器中的 Student 对象, BeanLifeCircleTest 类的部分代码如下所示.
- package io.mykit.spring.test;
- import io.mykit.spring.plugins.register.config.LifeCircleConfig;
- import org.junit.Test;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 测试 bean 的生命周期
- */
- public class BeanLifeCircleTest {
- @Test
- public void testBeanLifeCircle01(){
- // 创建 IoC 容器
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
- System.out.println("容器创建完成...");
- }
- }
在前面的文章中, 我们说过: 对于单实例 bean 对象来说, 在 Spring 容器创建完成后, 就会对单实例 bean 进行实例化. 那么, 我们先来运行下 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法, 输出的结果信息如下所示.
Student 类的构造方法
容器创建完成...
可以看到, 在 Spring 容器创建完成时, 自动调用单实例 bean 的构造方法, 对单实例 bean 进行了实例化操作.
总之: 对于单实例 bean 来说, 在 Spring 容器启动的时候创建对象; 对于多实例 bean 来说, 在每次获取 bean 的时候创建对象.
现在, 我们在 Student 类中指定了 init() 方法和 destroy() 方法, 那么, 如何让 Spring 容器知道 Student 类中的 init() 方法是用来执行对象的初始化操作, 而 destroy() 方法是用来执行对象的销毁操作呢? 如果是使用 xml 文件配置的话, 我们可以使用如下配置来实现.
<bean id="student" class="io.mykit.spring.plugins.register.bean.Student" init-method="init" destroy-method="destroy"></bean>
如果我们在 @Bean 注解中该如何实现呢? 其实就更简单了, 我们来看下 @Bean 注解的源码, 如下所示.
- package org.springframework.context.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import org.springframework.beans.factory.annotation.Autowire;
- import org.springframework.beans.factory.support.AbstractBeanDefinition;
- import org.springframework.core.annotation.AliasFor;
- @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface Bean {
- @AliasFor("name")
- String[] value() default {};
- @AliasFor("value")
- String[] name() default {};
- @Deprecated
- Autowire autowire() default Autowire.NO;
- boolean autowireCandidate() default true;
- String initMethod() default "";
- String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
- }
看到 @Bean 注解的源码, 相信小伙伴们会有种豁然开朗的感觉: 没错, 就是使用 @Bean 注解的 initMethod 属性和 destroyMethod 属性来指定 bean 的初始化方法和销毁方法.
所以, 我们在 LifeCircleConfig 类中的 @Bean 注解中指定 initMethod 属性和 destroyMethod 属性, 如下所示.
- @Bean(initMethod = "init", destroyMethod = "destroy")
- public Student student(){
- return new Student();
- }
此时, 我们再来运行 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法, 输出的结果信息如下所示.
Student 类的构造方法
初始化 Student 对象
容器创建完成...
从输出结果可以看出, 在 Spring 容器中, 先是调用了 Student 类的构造方法来创建 Student 对象, 接下来调用了 Student 对象的 init() 方法来进行初始化.
那小伙伴们可能会问, 运行上面的代码没有打印出 bean 的销毁方法中的信息啊, 那什么时候执行 bean 的销毁方法呢? 这个问题问的很好, bean 的销毁方法是在容器关闭的时候调用的.
接下来, 我们在 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法中, 添加关闭容器的代码, 如下所示.
- @Test
- public void testBeanLifeCircle01(){
- // 创建 IoC 容器
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
- System.out.println("容器创建完成...");
- context.close();
- }
我们再来运行 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法, 输出的结果信息如下所示.
Student 类的构造方法
初始化 Student 对象
容器创建完成...
销毁 Student 对象
可以看到, 此时输出了对象的销毁方法中的信息, 说明执行了对象的销毁方法.
指定初始化和销毁方法的使用场景
一个典型的使用场景就是对于数据源的管理. 例如, 在配置数据源时, 在初始化的时候, 对很多的数据源的属性进行赋值操作; 在销毁的时候, 我们需要对数据源的连接等信息进行关闭和清理. 此时, 我们就可以在自定义的初始化和销毁方法中来做这些事情!
初始化和销毁方法调用的时机
bean 对象的初始化方法调用的时机: 对象创建完成, 如果对象中存在一些属性, 并且这些属性也都赋值好之后, 会调用 bean 的初始化方法. 对于单实例 bean 来说, 在 Spring 容器创建完成后, Spring 容器会自动调用 bean 的初始化和销毁方法; 对于单实例 bean 来说, 在每次获取 bean 对象的时候, 调用 bean 的初始化和销毁方法.
bean 对象的销毁方法调用的时机: 对于单实例 bean 来说, 在容器关闭的时候, 会调用 bean 的销毁方法; 对于多实例 bean 来说, Spring 容器不会管理这个 bean, 也不会自动调用这个 bean 的销毁方法. 不过, 小伙伴们可以手动调用多实例 bean 的销毁方法.
前面, 我们已经说了单实例 bean 的初始化和销毁方法. 接下来, 我们来说下多实例 bean 的初始化和销毁方法. 我们将 Student 对象变成多实例 bean 来验证下. 接下来, 我们在 LifeCircleConfig 类的 student() 方法上通过 @Scope 注解将 Student 对象设置成多实例 bean, 如下所示.
- @Scope("prototype")
- @Bean(initMethod = "init", destroyMethod = "destroy")
- public Student student(){
- return new Student();
- }
接下来, 我们再来运行 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法, 输出的结果信息如下所示.
容器创建完成...
可以看到, 当我们将 Student 对象设置成多实例 bean, 并且没有获取 bean 实例对象时, Spring 容器并没有执行 bean 的构造方法, 初始化方法和销毁方法.
说到这, 我们就在 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法中添加一行获取 Student 对象的代码, 如下所示.
- @Test
- public void testBeanLifeCircle01(){
- // 创建 IoC 容器
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
- System.out.println("容器创建完成...");
- context.getBean(Student.class);
- context.close();
- }
此时, 我们再来运行 BeanLifeCircleTest 类中的 testBeanLifeCircle01() 方法, 输出的结果信息如下所示.
容器创建完成...
Student 类的构造方法
初始化 Student 对象
可以看到, 此时, 结果信息中输出了构造方法和初始化方法中的信息. 但是当容器关闭时, 并没有输出 bean 的销毁方法中的信息.
这是因为 将 bean 设置成多实例时, Spring 不会自动调用 bean 对象的销毁方法. 至于多实例 bean 对象何时销毁, 那就是程序员自己的事情了!!Spring 容器不再管理多实例 bean.
好了, 咱们今天就聊到这儿吧! 别忘了给个在看和转发, 让更多的人看到, 一起学习一起进步!!
项目工程源码已经提交到 GitHub: https://github.com/sunshinelyz/spring-annotation
写在最后
如果觉得文章对你有点帮助, 请微信搜索并关注「 冰河技术 」微信公众号, 跟冰河学习 Spring 注解驱动开发. 公众号回复 "spring 注解" 关键字, 领取 Spring 注解驱动开发核心知识图, 让 Spring 注解驱动开发不再迷茫.
来源: https://www.cnblogs.com/binghe001/p/13171123.html