深入解析Spring Bean初始化时和销毁时的扩展点

 更新时间:2023年08月04日 09:35:11   作者:刘牌  
在Bean进行初始化或者销毁的时候,如果我们需要做一些操作,比如加载和销毁一些资源或者执行一些方法时,那么就可以使用Spring提供的一些扩展,今天主要分享初始化Bean时的三种方式和销毁Bean时的三种方式,需要的朋友可以参考下

一.前言

今天来分享一下Bean在初始化时和Bean销毁时我们可以做的一些操作,如果只是单纯做CRUD开发,那么这些操作基本上不可能遇到,如果依赖于Spring来做一些框架层面的开发或者中间件开发,那么这些操作是很常用的,在Bean进行初始化或者销毁的时候,如果我们需要做一些操作,比如加载和销毁一些资源或者执行一些方法时,那么就可以使用Spring提供的一些扩展,今天主要分享初始化Bean时的三种方式和销毁Bean时的三种方式。

二.相关扩展点和方法

初始化时和销毁时都有相应的方式供我们选择,下面列出了初始化时和销毁时的各三种方式,然后再进行深度解析。

初始化时

  • @PostConstruct
  • 自定义初始化方法
  • InitializingBean

销毁时

  • @PreDestroy
  • 自定义销毁方法
  • DisposableBean

三.测试

定义Bean

下面我们定义了一个Bean,实现了InitializingBean和DisposableBean接口,分别在方法上使用了@PostConstruct注解和@PreDestroy注解,又自定义了初始化方法initMethod()和销毁方法destoryMethod()。

配置Bean

使用@Configuration注解和@Bean注解来注册Bean,我们在InitDestroyBean上使用了@Bean注解来将其标注为一个Bean,并且加上了初始化方法和销毁方法。

查看结果

从控制台输出我们可以得出这些方式的优先级

  • @PostConstruct > InitializingBean > 自定义初始化方法

  • @PreDestroy > DisposableBean > 自定义销毁方法

四.源码解析

下面进行源码解析,因为Spring的源码还是比较复杂,所以我们只从最关键的地方开始分析,下分析初始化Bean时,再分析销毁Bean时。

初始化Bean

1.解析Bean中@PostConstruct注解和@PreDestory注解

我们直接来到AbstractAutowireCapableBeanFactory类中,@PostConstruct注解和@PreDestory标注的方法会在applyMergedBeanDefinitionPostProcessors中被后置处理器InitDestroyAnnotationBeanPostProcessor解析,原理是通过反射判断Bean中是否有方法上标注了@PostConstruct注解和@PreDestory注解,如果有,则将其加入initMethodsdestroyMethods集合中,然后组装到LifecycleMetadata中,以供后续使用。

2.对Bean进行初始化-调用标注@PostConstruct的方法

下一步进入initializeBean方法中,然后进入applyBeanPostProcessorsBeforeInitialization方法,从名字我们可以看出这是Bean初始化前操作,这里会调用InitDestroyAnnotationBeanPostProcessor后置处理进行处理。

从上面可以看出会通过findLifecycleMetadata去获取元数据,就是上面我们说的LifecycleMetadata,这里会用到,然后调用invokeInitMethods方法,最终会通过反射调用到标注了@PostConstruct注解的方法。

从上面我们可以看出,标注了@PostConstruct注解的方法最先执行。

3.调用自定义初始化方法和实现了InitializingBean接口的方法。

接着调用invokeInitMethods方法,里面会判断Bean是否实现了InitializingBean接口,如果实现,那么就会调用方法afterPropertiesSet(),接着会获取Bean中自定义的初始化方法,然后通过反射调用。

从上面看出,实现了InitializingBean接口中的最先被执行,自定义的Bean初始化方法第二被执行。

4.总结

从上面看出,如果是通过@PostConstruct注解标注的方法,则需要使用后置处理器BeanPostProcessor来进行处理,实现InitializingBean接口和自定义的初始化方法则不需要使用后置处理器处理,@PostConstruct标注的方法的优先级大于实现了InitializingBean接口的方法,实现了InitializingBean接口的方法大于自定义的初始化方法。

销毁Bean

销毁Bean的动作发生在容器关闭的时候,当Spring程序中发生BeansException异常是会触发,还有我们也可以手动关闭容器,关闭容器后,Spring中的所有Bean都会被清理掉,这时候如果再去获取对应的Bean,就会发生异常。

1.手动关闭容器

为了去分析源码,我们这里直接手动去关闭Spring容器,直接调用close()方法关闭容器。

手动调用关闭容器后,会去调用doClose()方法,然后里面有一个destroyBeans()方法,这里方法就是销毁Bean,我们可以看到它有一个备注Destroy all cached singletons in the context's BeanFactory.,意思就是销毁单例Bean,至于为什么是销毁单例Bean,大家可以想一下,哈哈!

2.执行标注了@PreDestroy的方法

顺着源码一直跟进来,我们发现它它也会调用Bean的后置处理器,然后通过反射调用标注了@PreDestroy注解的方法,这里和标注了@PostConstruct的方法的执行是一样的。

3.调用实现DisposableBean接口的方法

接着判断当前的Bean是否实现了DisposableBean接口,如果实现了,则调用destroy()方法,和InitializingBean也是一样的套路。

4.执行自定义的销毁方法

往下执行,就会判断是否定义了自定义的销毁方法,如果定义了,则通过反射进行调用,和初始化方法哪里是一样的套路。

5.总结

从上面可以看出,销毁Bean和初始化Bean时这些扩展点的方式基本上都差不多,在销毁Bean时,会将其中涉及到的装Bean的一些集合都进行清空,然后再把BeanFactory关闭,不过我们这里关注的时销毁时执行的方法,就不去管那些了。

我们得出结论,标注了@PreDestroy注解的方法最先被执行,实现了DisposableBean接口的第二被执行,自定义的销毁方法最后被执行。

五.思考

我们思考一下,为什么Spring对于@PostConstruct注解和@PreDestory注解要使用专门的后置处理器来处理?

其实这也是Spring牛逼的地方,扩展性极强,因为@PostConstruct注解和@PreDestory注解其实不是属于Spring的,而是Java语言层面的注解,如果不通过扩展的方式来实现这两个注解的使用,那么就没有扩展性而言,加入那天再需要加入Java层面的注解到Spring中,那么又需要去代码里面改,显然,这样的设计时不合理的。

像@Resource注解也不是Spring的,也是Java层面的,处理这个注解也时通过后置处理器来进行处理。

所以Spring为什么发展得这么迅猛,Java程序员基本没人不用Spring,Spring的不断发展,使它成为最复杂的框架,但是它的内核设计还是十分优秀的,如果没有优秀的设计,估计代码已经不堪入目了。

六.总结

上面我们对于Spring的Bean初始化时和销毁时的一些操作进行了介绍并进行测试,然后分析了它们的原理,并对Spring的设计进行我个人的理解和评价。

其实对于像Spring这样庞大的框架,学习难度还是比较大的,需要我们一遍有一遍去debug,去分析,去理解,这样才能慢慢对它有一个了解,如果只是为了去应付,去背,那么基本上没用。

以上就是深入解析Spring Bean初始化时和销毁时的扩展点的详细内容,更多关于Spring Bean初始化和销毁扩展点的资料请关注脚本之家其它相关文章!

相关文章

  • Idea和Docker如何集成

    Idea和Docker如何集成

    今天就介绍下idea和Docker如何集成,要集成之前,需要我们本机能够访问docker服务,也就是我们的docker服务需要开启远程模式,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2023-10-10
  • java实现简单的验证码功能

    java实现简单的验证码功能

    这篇文章主要为大家详细介绍了java实现简单的验证码功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • Maven及Springboot配置JDK版本,编码,源码打包等方式

    Maven及Springboot配置JDK版本,编码,源码打包等方式

    这篇文章主要介绍了Maven及Springboot配置JDK版本,编码,源码打包等方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Eclipse中导入Maven Web项目并配置其在Tomcat中运行图文详解

    Eclipse中导入Maven Web项目并配置其在Tomcat中运行图文详解

    这篇文章主要介绍了Eclipse中导入Maven Web项目并配置其在Tomcat中运行图文详解,需要的朋友可以参考下
    2017-12-12
  • mybatis-plus实现逻辑删除的示例代码

    mybatis-plus实现逻辑删除的示例代码

    本文主要介绍了mybatis-plus实现逻辑删除的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • SpringBoot登录验证token拦截器的实现

    SpringBoot登录验证token拦截器的实现

    本文主要介绍了SpringBoot登录验证token拦截器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 一文带你搞懂Java单例模式

    一文带你搞懂Java单例模式

    单例就是单实例的意思,即在系统全局,一个类只创建一个对象,并且在系统全局都可以访问这个对象而不用重新创建。本文将通过示例为大家详细讲解Java单例模式的使用,需要的可以参考一下
    2022-11-11
  • Springboot 实现数据库备份还原的方法

    Springboot 实现数据库备份还原的方法

    这篇文章主要介绍了Springboot 实现数据库备份还原的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 关于Java实现HttpServer模拟前端接口调用

    关于Java实现HttpServer模拟前端接口调用

    这篇文章主要介绍了关于Java实现Http Server模拟前端接口调用,Http 协议是建立在 TCP 协议之上的协议,所以能用 TCP 来自己模拟一个简单的 Http Server 当然是可以的,需要的朋友可以参考下
    2023-04-04
  • Java8语法糖之Lambda表达式的深入讲解

    Java8语法糖之Lambda表达式的深入讲解

    这篇文章主要给大家介绍了关于Java8语法糖之Lambda表达式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02

最新评论