Spring 源码解读:实现Bean的初始化与销毁机制

2024-08-30 06:44

本文主要是介绍Spring 源码解读:实现Bean的初始化与销毁机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引言

在Spring框架中,Bean的生命周期管理是非常重要的一个方面。Spring提供了多种方式来管理Bean的初始化和销毁,例如通过@PostConstruct@PreDestroy注解定义初始化和销毁方法。本篇文章将通过自定义实现来讲解如何手动管理Bean的初始化与销毁,同时对比Spring中的相关注解,深入理解Bean生命周期管理的实现方式及其重要性。

Bean生命周期管理的基本概念

在Spring中,Bean的生命周期涵盖了从Bean的创建、初始化、使用到销毁的全过程。Spring通过一系列回调接口、注解和配置,提供了灵活的Bean生命周期管理机制。在实际开发中,合理管理Bean的生命周期可以帮助我们在适当的时机进行资源初始化和释放,提升应用的健壮性和可维护性。

Bean的生命周期

  1. 实例化:Spring容器根据BeanDefinition创建Bean实例。
  2. 属性赋值:Spring容器将Bean的属性值注入到Bean实例中。
  3. 初始化:在属性赋值完成后,Spring容器调用Bean的初始化方法。
  4. 使用:Bean处于就绪状态,可以被Spring容器使用。
  5. 销毁:在容器关闭时,Spring容器调用Bean的销毁方法,释放资源。

手动实现Bean的初始化与销毁方法

自定义注解@Init@Destroy

我们首先定义两个自定义注解,@Init用于标识Bean的初始化方法,@Destroy用于标识Bean的销毁方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Init注解,用于标识Bean的初始化方法*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Init {
}/*** @Destroy注解,用于标识Bean的销毁方法*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Destroy {
}

扩展SimpleBeanFactory实现初始化与销毁

在前面实现的SimpleBeanFactory基础上,我们扩展其功能,使其支持对@Init@Destroy方法的调用。

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;/*** 自定义的简单Bean工厂,支持@Bean注解、@Init注解和@Destroy注解*/
public class SimpleBeanFactory {// 存储已创建的Bean实例private Map<String, Object> beanMap = new HashMap<>();// 存储Bean的定义信息,包括Bean的类、初始化方法和销毁方法private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();/*** 构造方法,扫描配置类并注册Bean定义* @param configClass 配置类*/public SimpleBeanFactory(Class<?> configClass) {// 扫描配置类中的所有方法for (Method method : configClass.getDeclaredMethods()) {// 检查方法是否标注了@Bean注解if (method.isAnnotationPresent(Bean.class)) {// 创建BeanDefinition并设置Bean的类信息BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setBeanClass(method.getReturnType());// 扫描Bean类的方法,查找@Init和@Destroy注解for (Method beanMethod : method.getReturnType().getDeclaredMethods()) {if (beanMethod.isAnnotationPresent(Init.class)) {beanDefinition.setInitMethod(beanMethod);}if (beanMethod.isAnnotationPresent(Destroy.class)) {beanDefinition.setDestroyMethod(beanMethod);}}// 将BeanDefinition注册到beanDefinitionMap中beanDefinitionMap.put(method.getName(), beanDefinition);}}}/*** 获取Bean实例* @param name Bean的名称* @return Bean实例*/public Object getBean(String name) {// 如果Bean实例已经存在于beanMap中,直接返回if (beanMap.containsKey(name)) {return beanMap.get(name);}// 从beanDefinitionMap中获取BeanDefinitionBeanDefinition beanDefinition = beanDefinitionMap.get(name);if (beanDefinition == null) {throw new RuntimeException("No bean named " + name + " is defined");}try {// 创建Bean实例Object bean = beanDefinition.getBeanClass().getDeclaredConstructor().newInstance();// 如果Bean定义了初始化方法,调用该方法if (beanDefinition.getInitMethod() != null) {beanDefinition.getInitMethod().invoke(bean);}// 将创建的Bean实例存储到beanMap中beanMap.put(name, bean);return bean;} catch (Exception e) {throw new RuntimeException("Failed to create bean " + name, e);}}/*** 销毁Bean实例* @param name Bean的名称*/public void destroyBean(String name) {// 获取Bean实例Object bean = beanMap.get(name);if (bean != null) {// 获取Bean的定义信息BeanDefinition beanDefinition = beanDefinitionMap.get(name);try {// 如果Bean定义了销毁方法,调用该方法if (beanDefinition.getDestroyMethod() != null) {beanDefinition.getDestroyMethod().invoke(bean);}// 从beanMap中移除Bean实例beanMap.remove(name);} catch (Exception e) {throw new RuntimeException("Failed to destroy bean " + name, e);}}}/*** 获取BeanDefinition* @param name Bean的名称* @return BeanDefinition实例*/public BeanDefinition getBeanDefinition(String name) {return beanDefinitionMap.get(name);}
}

BeanDefinition类的扩展

我们需要在BeanDefinition类中增加对初始化方法和销毁方法的支持。

import java.lang.reflect.Method;/*** BeanDefinition类,用于存储Bean的元数据信息*/
public class BeanDefinition {// Bean的类private Class<?> beanClass;// 初始化方法private Method initMethod;// 销毁方法private Method destroyMethod;public Class<?> getBeanClass() {return beanClass;}public void setBeanClass(Class<?> beanClass) {this.beanClass = beanClass;}public Method getInitMethod() {return initMethod;}public void setInitMethod(Method initMethod) {this.initMethod = initMethod;}public Method getDestroyMethod() {return destroyMethod;}public void setDestroyMethod(Method destroyMethod) {this.destroyMethod = destroyMethod;}
}

测试初始化与销毁

我们通过一个简单的测试类来验证自定义的@Init@Destroy注解,以及扩展后的SimpleBeanFactory

/*** Service类,包含初始化和销毁方法*/
public class Service {@Initpublic void init() {System.out.println("Service is initialized.");}@Destroypublic void destroy() {System.out.println("Service is destroyed.");}public void execute() {System.out.println("Service is executing...");}
}/*** 配置类,定义一个Service Bean*/
public class AppConfig {@Beanpublic Service service() {return new Service();}
}/*** 测试类,验证Bean的初始化与销毁*/
public class Test {public static void main(String[] args) {SimpleBeanFactory factory = new SimpleBeanFactory(AppConfig.class);// 获取并使用BeanService service = (Service) factory.getBean("service");service.execute();// 销毁Beanfactory.destroyBean("service");}
}

详细解读

  • Service类中定义了initdestroy方法,分别用@Init@Destroy注解标识。
  • AppConfig类中定义了一个名为service的Bean。
  • Test类中,我们通过SimpleBeanFactory获取service Bean并调用其方法,然后在销毁Bean时调用销毁方法。

类图和流程图

为了更好地理解整个流程,我们提供了类图和流程图。

类图
uses
SimpleBeanFactory
-Map<String, Object> beanMap
-Map<String, BeanDefinition> beanDefinitionMap
+SimpleBeanFactory(Class<?> configClass)
+Object getBean(String name)
+void destroyBean(String name)
+BeanDefinition getBeanDefinition(String name)
BeanDefinition
-Class<?> beanClass
-Method initMethod
-Method destroyMethod
+Class~?~ getBeanClass()
+void setBeanClass(Class<?> beanClass)
+Method getInitMethod()
+void setInitMethod(Method initMethod)
+Method getDestroyMethod()
+void setDestroyMethod(Method destroyMethod)
AppConfig
+Service service()
Service
+void init()
+void destroy()
+void execute()

解释

  • SimpleBeanFactory类负责管理Bean的生命周期,包括初始化和销毁。
  • BeanDefinition类存储了Bean的元数据信息,包括初始化和销毁方法。
  • AppConfig类定义了一个标注了@Bean注解的service()方法。
流程图
SimpleBeanFactory构造器
扫描AppConfig类的方法
检查方法是否标注了 Bean注解
创建BeanDefinition并存储元数据信息,包括初始化和销毁方法
调用方法并注册Bean
Bean和BeanDefinition存储在beanMap和beanDefinitionMap中
通过getBean获取Bean实例,调用初始化方法
通过destroyBean销毁Bean,调用销毁方法

解释

  • 流程图展示了SimpleBeanFactory如何通过反射扫描AppConfig类的方法,检查是否标注了@Bean注解,并注册Bean和BeanDefinition的过程。同时展示了如何在获取Bean实例时调用初始化方法,以及在销毁Bean时调用销毁方法。

Spring源码解读:@PostConstruct@PreDestroy注解的实现

在Spring中,@PostConstruct@PreDestroy注解是用来标识Bean的初始化和销毁方法的。@PostConstruct标识的方法会在Bean的依赖注入完成后自动调用,而@PreDestroy标识的方法会在Bean被销毁前调用。

@PostConstruct@PreDestroy 在Spring中的实现

Spring通过CommonAnnotationBeanPostProcessor类处理@PostConstruct@PreDestroy注解。这个类实现了BeanPostProcessor接口,可以在Bean初始化和销毁时执行特定的逻辑。

CommonAnnotationBeanPostProcessor
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor {private static final String POST_CONSTRUCT_METHOD_NAME = "postConstruct";private static final String PRE_DESTROY_METHOD_NAME = "preDestroy";public CommonAnnotationBeanPostProcessor() {super(POST_CONSTRUCT_METHOD_NAME, PRE_DESTROY_METHOD_NAME);}
}

详细解读

  • CommonAnnotationBeanPostProcessor:该类继承自InitDestroyAnnotationBeanPostProcessor,并指定了@PostConstruct@PreDestroy注解对应的方法名称。
  • InitDestroyAnnotationBeanPostProcessor:负责在Bean初始化后调用@PostConstruct标注的方法,在Bean销毁前调用@PreDestroy标注的方法。
InitDestroyAnnotationBeanPostProcessor 类的实现
public class InitDestroyAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {private final String initAnnotationType;private final String destroyAnnotationType;public InitDestroyAnnotationBeanPostProcessor(String initAnnotationType, String destroyAnnotationType) {this.initAnnotationType = initAnnotationType;this.destroyAnnotationType = destroyAnnotationType;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 扫描并调用@PostConstruct标注的方法invokeAnnotatedMethods(bean, this.initAnnotationType);return bean;}@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {// 扫描并调用@PreDestroy标注的方法invokeAnnotatedMethods(bean, this.destroyAnnotationType);}private void invokeAnnotatedMethods(Object bean, String annotationType) {// 扫描bean的方法,查找并调用指定注解标注的方法Method[] methods = bean.getClass().getMethods();for (Method method : methods) {if (method.isAnnotationPresent(annotationType)) {try {method.invoke(bean);} catch (Exception ex) {throw new BeansException("Invocation of " + annotationType + " method failed", ex);}}}}
}

详细解读

  • postProcessBeforeInitialization 方法:在Bean初始化之前调用@PostConstruct标注的方法。
  • postProcessBeforeDestruction 方法:在Bean销毁之前调用@PreDestroy标注的方法。
  • invokeAnnotatedMethods 方法:通过反射机制扫描Bean的所有方法,查找并调用指定注解标注的方法。

对比与自定义实现

  • Spring的实现

    • @PostConstruct@PreDestroy注解是Java EE规范的一部分,Spring通过CommonAnnotationBeanPostProcessor类对其进行了支持,确保在Bean初始化和销毁时执行相应的方法。
    • Spring的实现非常简洁且易于使用,只需在方法上添加注解即可。
  • 自定义实现

    • 通过自定义的@Init@Destroy注解实现了类似的功能,虽然实现简单,但展示了Spring背后的设计思路。
    • 自定义实现更适合学习和理解Spring的核心机制。

总结

通过实现自定义的@Init@Destroy注解,以及扩展SimpleBeanFactory支持初始化和销毁方法的调用,并深入解读Spring源码中的@PostConstruct@PreDestroy注解处理机制,你应该对Spring中的Bean生命周期管理有了更深入的理解。这些知识不仅有助于你更好地使用Spring框架,也能帮助你在开发中有效管理Bean的生命周期。


互动与思考

在实际项目中,你是如何管理Bean的生命周期的?你认为在什么场景下需要自定义Bean的初始化和销毁方法?欢迎在评论区分享你的看法和经验!


如果你觉得这篇文章对你有帮助,请别忘了:

  • 点赞
  • 收藏 📁
  • 关注 👀

让我们一起深入学习Spring框架,成为更优秀的开发者!


这篇关于Spring 源码解读:实现Bean的初始化与销毁机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120078

相关文章

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

Java中Integer128陷阱

《Java中Integer128陷阱》本文主要介绍了Java中Integer与int的区别及装箱拆箱机制,重点指出-128至127范围内的Integer值会复用缓存对象,导致==比较结果为true,下... 目录一、Integer和int的联系1.1 Integer和int的区别1.2 Integer和in

SpringSecurity整合redission序列化问题小结(最新整理)

《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序... 目录1. 前言2. Redission配置2.1 RedissonProperties2.2 Red