写在前面

在《【Spring注解驱动开发】如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!》一文中,我们讲述了如何使用@Bean注解来指定bean初始化和销毁的方法。具体的用法就是在@Bean注解中使用init-method属性和destroy-method属性来指定初始化方法和销毁方法。除此之外,Spring中是否还提供了其他的方式来对bean实例进行初始化和销毁呢?

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

InitializingBean接口

1.InitializingBean接口概述

Spring中提供了一个InitializingBean接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。InitializingBean接口的源码如下所示。

package org.springframework.beans.factory;
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}

根据InitializingBean接口中提供的afterPropertiesSet()方法的名字可以推断出:afterPropertiesSet()方法是在属性赋好值之后调用的。那到底是不是这样呢?我们来分析下afterPropertiesSet()方法的调用时机。

2.何时调用InitializingBean接口?

我们定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法中,来查看Spring加载bean的方法。

题外话:不要问我为什么会是这个invokeInitMethods()方法,如果你和我一样对Spring的源码非常熟悉的话,你也会知道是这个invokeInitMethods()方法,哈哈哈哈!所以,小伙伴们不要只顾着使用Spring,还是要多看看Spring的源码啊!Spring框架中使用了大量优秀的设计模型,其代码的编写规范和严谨程度也是业界开源框架中数一数二的,非常值得阅读。

我们来到AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法,如下所示。

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则调用bean的afterPropertiesSet方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
//调用afterPropertiesSet()方法
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//调用afterPropertiesSet()方法
((InitializingBean) bean).afterPropertiesSet();
}
} if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//通过反射的方式调用init-method
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

分析上述代码后,我们可以初步得出如下信息:

  • Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件和@Bean注解中通过init-method指定,两种方式可以同时使用。
  • 实现InitializingBean接口是直接调用afterPropertiesSet()方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对Spring的依赖。
  • 如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

也就是说Spring为bean提供了两种初始化的方式,第一种实现InitializingBean接口,实现afterPropertiesSet方法,第二种配置文件或@Bean注解中通过init-method指定,两种方式可以同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。

DisposableBean接口

1.DisposableBean接口概述

实现org.springframework.beans.factory.DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。我们先来看下DisposableBean接口的源码,如下所示。

package org.springframework.beans.factory;
public interface DisposableBean {
void destroy() throws Exception;
}

可以看到,在DisposableBean接口中只定义了一个destroy()方法。

在Bean生命周期结束前调用destory()方法做一些收尾工作,亦可以使用destory-method。前者与Spring耦合高,使用类型强转.方法名(),效率高。后者耦合低,使用反射,效率相对低

2.DisposableBean接口注意事项

多例bean的生命周期不归Spring容器来管理,这里的DisposableBean中的方法是由Spring容器来调用的,所以如果一个多例实现了DisposableBean是没有啥意义的,因为相应的方法根本不会被调用,当然在XML配置文件中指定了destroy方法,也是没有意义的。所以,在多实例bean情况下,Spring不会自动调用bean的销毁方法。

单实例bean案例

创建一个Animal的类实现InitializingBean和DisposableBean接口,代码如下:

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 测试InitializingBean接口和DisposableBean接口
*/
public class Animal implements InitializingBean, DisposableBean {
public Animal(){
System.out.println("执行了Animal类的无参数构造方法");
} @Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行了Animal类的初始化方法。。。。。"); }
@Override
public void destroy() throws Exception {
System.out.println("执行了Animal类的销毁方法。。。。。"); }
}

接下来,我们新建一个AnimalConfig类,并将Animal通过@Bean注解的方式注册到Spring容器中,如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.plugins.register.bean.Animal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author binghe
* @version 1.0.0
* @description AnimalConfig
*/
@Configuration
@ComponentScan("io.mykit.spring.plugins.register.bean")
public class AnimalConfig {
@Bean
public Animal animal(){
return new Animal();
}
}

接下来,我们在BeanLifeCircleTest类中新增testBeanLifeCircle02()方法来进行测试,如下所示。

@Test
public void testBeanLifeCircle02(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
System.out.println("IOC容器创建完成...");
//关闭IOC容器
context.close();
}

运行BeanLifeCircleTest类中的testBeanLifeCircle02()方法,输出的结果信息如下所示。

执行了Animal类的无参数构造方法
执行了Animal类的初始化方法。。。。。
IOC容器创建完成...
执行了Animal类的销毁方法。。。。。

从输出的结果信息可以看出:单实例bean下,IOC容器创建完成后,会自动调用bean的初始化方法;而在容器销毁前,会自动调用bean的销毁方法。

多实例bean案例

多实例bean的案例代码基本与单实例bean的案例代码相同,只不过在AnimalConfig类中,我们在animal()方法上添加了@Scope("prototype")注解,如下所示。

package io.mykit.spring.plugins.register.config;
import io.mykit.spring.plugins.register.bean.Animal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* @author binghe
* @version 1.0.0
* @description AnimalConfig
*/
@Configuration
@ComponentScan("io.mykit.spring.plugins.register.bean")
public class AnimalConfig {
@Bean
@Scope("prototype")
public Animal animal(){
return new Animal();
}
}

接下来,我们在BeanLifeCircleTest类中新增testBeanLifeCircle03()方法来进行测试,如下所示。

@Test
public void testBeanLifeCircle03(){
//创建IOC容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class);
System.out.println("IOC容器创建完成...");
System.out.println("-------");
//调用时创建对象
Object bean = ctx.getBean("animal");
System.out.println("-------");
//调用时创建对象
Object bean1 = ctx.getBean("animal");
System.out.println("-------");
//关闭IOC容器
ctx.close();
}

运行BeanLifeCircleTest类中的testBeanLifeCircle03()方法,输出的结果信息如下所示。

IOC容器创建完成...
-------
执行了Animal类的无参数构造方法
执行了Animal类的初始化方法。。。。。
-------
执行了Animal类的无参数构造方法
执行了Animal类的初始化方法。。。。。
-------

从输出的结果信息中可以看出:在多实例bean情况下,Spring不会自动调用bean的销毁方法。

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

【Spring注解驱动开发】使用InitializingBean和DisposableBean来管理bean的生命周期,你真的了解吗?的更多相关文章

  1. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  2. 【spring 注解驱动开发】spring对象的生命周期

    尚学堂spring 注解驱动开发学习笔记之 - 生命周期 生命周期 1.生命周期-@Bean指定初始化和销毁方法 2.生命周期-InitializingBean和DisposableBean 3.生命 ...

  3. spring注解驱动开发

    1.全图: 一.IOC容器部分 1.第一个初始化实例: @Configuration @ComponentScans @Bean("person") 注意: @repeatable ...

  4. 【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!

    写在前面 在<[String注解驱动开发]面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!>一文中,我们详细的介绍了BeanPostProcessor的执行流 ...

  5. 【spring 注解驱动开发】spring ioc 原理

    尚学堂spring 注解驱动开发学习笔记之 - Spring容器创建 Spring容器创建 1.Spring容器创建-BeanFactory预准备 2.Spring容器创建-执行BeanFactory ...

  6. 1、课程简介-Spring 注解驱动开发

    1.课程简介-Spring 注解驱动开发

  7. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  8. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

  9. 【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则

    写在前面 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则.如果Spring内置的过滤规则不能够满足我们的 ...

随机推荐

  1. 工作中oracle常用操作

    常用数据库操作 启动数据库监听器lsnrctl start 停止数据库监听器lsnrctl stop 登录oraclesqlplus / as sysdba启动oralcestartup;关闭orac ...

  2. 公司如何通过电脑监控来规范员工行为-iis7服务器管理功能教程

    现在员工办公基本都用电脑,而电脑不仅仅可以用来处理工作,还可以上网.娱乐等.往往很多员工的自制能力比较差,在工作时间和人聊天,浏览购物网站,看新闻,炒股等休闲娱乐上花费大量的时间,导致工作效率降低,影 ...

  3. 使用VUE开发用户后台时的动态路由问题、按钮权限问题以及其他页面处理问题

    如今前后端分离是大势所趋,笔者虽然是做后台的,但也不得不学学前端的流行框架VUE -_-||| . 为了学习VUE,笔者搭建了一个简单的用户后台,以此来了解VUE的开发思路(注:本项目不用于实际开发, ...

  4. Beta冲刺——5.26

    这个作业属于哪个课程 软件工程 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.会议内容 1.组员一起学习Git分支管 ...

  5. Java实现 蓝桥杯 算法训练 动态数组使用

    算法训练 动态数组使用 时间限制:1.0s 内存限制:512.0MB 提交此题 从键盘读入n个整数,使用动态数组存储所读入的整数,并计算它们的和与平均值分别输出.要求尽可能使用函数实现程序代码.平均值 ...

  6. Java实现 LeetCode 572 另一个树的子树(遍历树)

    572. 另一个树的子树 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 s 的一个节点和这个节点的所有子孙.s 也可以看做它自身的一棵子树 ...

  7. Java实现 LeetCode 297 二叉树的序列化与反序列化

    297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得 ...

  8. Java实现 LeetCode 21 合并两个有序链表

    21. 合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1 ...

  9. java实现 蓝桥杯 算法提高 Problem S4: Interesting Numbers 加强版

    1 问题描述 Problem Description We call a number interesting, if and only if: 1. Its digits consists of o ...

  10. java实现第四届蓝桥杯黄金连分数

    黄金连分数 题目描述 黄金分割数0.61803- 是个无理数,这个常数十分重要,在许多工程问题中会出现.有时需要把这个数字求得很精确. 对于某些精密工程,常数的精度很重要.也许你听说过哈勃太空望远镜, ...