重看Spring聚焦BeanDefinition分析和构造

发布于:2025-05-13 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

一、对BeanDefinition的理解

(一)理解元信息

(二)BeanDefinition理解分析

二、BeanDefinition的结构设计分析

(一)整体结构体会

(二)重要接口和类分析

三、构造 BeanDefinition:模式注解 + 组件扫描方式

四、构造 BeanDefinition:配置类 + @Bean 注解方式

五、构造 BeanDefinition:通过XML配置文件

六、构造 BeanDefinition:通过编程方式

七、对构造分析对比


干货分享,感谢您的阅读!

在这个迅速发展的技术时代,开源框架如春天般蓬勃生长,其中Spring框架则以其灵活性和强大功能赢得了开发者们的青睐。而在这个庞大的框架中,有一个小小的角色却承担着至关重要的任务,那就是BeanDefinition。它不仅是Spring容器的“元信息”守护者,更是实现高效、灵活应用的秘密武器。

想象一下,如果没有BeanDefinition,开发者们在创建和管理Bean时将面临怎样的混乱局面?是不是像失去了指南针的探险者,在茫茫大海中寻找方向?本文将带你深入探讨BeanDefinition的奥秘,从基本概念到实际应用,甚至是各种构造方式的对比分析,让你在一篇文章中全面理解这个看似不起眼但却不可或缺的组成部分。快来一起揭开BeanDefinition的面纱,看看它如何在背后默默地为你的应用程序提供支持和灵活性吧!

一、对BeanDefinition的理解

BeanDefinition 可以被认为是 Bean 的元信息。它不是 Bean 实例本身,而是描述了如何创建 Bean 实例以及 Bean 的配置信息。因此,BeanDefinition 提供了关于 Bean 的元数据,包括类名、作用域、构造函数参数、属性值等等。在 Spring 容器启动时,BeanDefinition 被解析并用于实例化和管理 Bean 实例。

(一)理解元信息

在计算机科学中,元信息是用于描述其他数据的数据。元信息提供关于数据的属性、结构、类型或其他相关信息,通常用于解释和管理数据,而不是直接操作数据本身。

在软件开发中,元信息的概念非常常见。比如,在数据库中,表的结构信息(如列名、数据类型、约束等)就是元信息;在编程语言中,注解(Annotation)提供了关于类、方法或字段的额外信息,也可以看作是元信息;在 Web 开发中,HTML 标签中的属性、HTTP 头部中的信息等都可以视为元信息。

(二)BeanDefinition理解分析

BeanDefinition 是关于 Bean 的元数据集合(包含了创建和管理 Bean 所需的所有信息),这些信息在 Spring 容器启动时被解析,并用于实例化和管理 Bean 实例。通过配置和操作 BeanDefinition可以控制和定制 Bean 的行为,实现更加灵活和高效的应用程序开发。具体的元信息可总结如下表进行查看:

元信息 描述
类信息 (Class Information) Bean的类名,用于指定如何实例化Bean。
作用域信息 (Scope Information) Bean的作用域,决定了Bean的生命周期范围,包括singleton、prototype、request、session等。
构造函数参数 (Constructor Arguments) 描述了Bean构造函数的参数信息,用于实例化Bean对象。
属性信息 (Property Information) 描述了Bean的属性值信息,包括属性名和属性值,用于在实例化后设置Bean的属性。
初始化和销毁方法 (Initialization and Destruction Methods) 指定了Bean的初始化方法和销毁方法,用于在Bean实例化后或销毁前执行额外的操作。
依赖信息 (Dependency Information) 描述了Bean之间的依赖关系,使得容器能够按正确的顺序实例化和管理Bean。
其他元信息 (Other Metadata) 包括Bean的描述、别名等其他元信息,用于进一步描述Bean的用途和特性。

二、BeanDefinition的结构设计分析

(一)整体结构体会

(二)重要接口和类分析

我们选取一些重要接口和类整理表格来快速回顾(源码暂时不进行分析了):

接口/类 描述
BeanDefinition 接口 定义了 Bean 的元信息,包括类名、作用域、构造函数参数、属性值等。
AbstractBeanDefinition 抽象类 BeanDefinition 接口的抽象实现类,提供了通用属性的默认实现,如 Bean 类型、作用域、懒加载等。
GenericBeanDefinition 类 AbstractBeanDefinition 的具体实现类,用于描述通用的 BeanDefinition 结构,适用于多种配置方式。
RootBeanDefinition 类 GenericBeanDefinition 的子类,增加了对 Bean 类型自动检测的支持,可以根据配置自动确定 Bean 的类型。
AnnotatedBeanDefinition 接口 表示使用注解配置的 BeanDefinition,继承自 BeanDefinition 接口,用于描述通过注解方式配置的 Bean。
BeanDefinitionHolder 类 BeanDefinition 的持有者,包含一个 BeanDefinition 对象以及与之关联的名称和别名。
BeanDefinitionRegistry 接口 定义了 BeanDefinition 注册的方法,允许向 Spring 容器注册新的 BeanDefinition,或者从容器中移除已有的 BeanDefinition。
DefaultListableBeanFactory 类 BeanDefinitionRegistry 接口的默认实现类,实现了 BeanDefinition 的注册和管理功能,是 Spring 容器的核心部分之一。

接下来我们重点看如何构造BeanDefinition。

三、构造 BeanDefinition:模式注解 + 组件扫描方式

基于约定优于配置的原则

使用注解(如 @Component@Service@Repository@Controller 等)标记类,然后通过组件扫描(Component Scanning)方式,Spring 容器会自动扫描指定的包,并根据这些注解自动创建相应的 BeanDefinition。

这种方式在平时开发中最为常见,比如直接定义一个简单的组件类并用 @Component 注解进行标记如下:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.stereotype.Component;

/**
 * @program: zyfboot-javabasic
 * @description: 定义一个简单的组件类,并使用 @Component 注解进行标记
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:03
 **/
@Component
public class ZyfComponent {
    public void sayHello() {
        System.out.println("Hello from ZyfComponent!");
    }
}

为符合其构造,我们在定义一个配置类并在其中启用组件扫描如下:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @program: zyfboot-javabasic
 * @description: 定义一个配置类,并在其中启用组件扫描。
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:08
 **/
@Configuration
@ComponentScan("org.zyf.javabasic.spring.beandefinition")
public class ZyfAppConfig {
    // 配置类的其他内容
}

直接验证如下:Spring 容器在启动时扫描指定包路径下的组件类,并将标记了 @Component 注解的类注册为 BeanDefinition,这样就可以方便地在应用程序中使用这些 Bean。

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @program: zyfboot-javabasic
 * @description: ceshi
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:09
 **/
public class ZyfApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class);
        ZyfComponent myComponent = context.getBean(ZyfComponent.class);
        myComponent.sayHello();
        context.close();
    }
}

四、构造 BeanDefinition:配置类 + @Bean 注解方式

显式配置每个 Bean 的创建过程

创建一个配置类,通常使用 @Configuration 注解标记,在这个类中,使用 @Bean 注解标记方法,方法返回的对象就是一个 Bean。

简单来说,可以先创建一个普通的 Java 类作为需要被 Spring 管理的 Bean:

package org.zyf.javabasic.spring.beandefinition;

/**
 * @program: zyfboot-javabasic
 * @description: 创建一个普通的 Java 类,作为需要被 Spring 管理的 Bean。
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:18
 **/
public class ZyfService {
    public void performAction() {
        System.out.println("Performing action in MyService!");
    }
}

在刚刚创建的配置类ZyfAppConfig中使用 @Bean 注解标记方法增加即可:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @program: zyfboot-javabasic
 * @description: 定义一个配置类,并在其中启用组件扫描。
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:08
 **/
@Configuration
@ComponentScan("org.zyf.javabasic.spring.beandefinition")
public class ZyfAppConfig {
    // 配置类的其他内容
    @Bean
    public ZyfService zyfService() {
        return new ZyfService();
    }
}

直接验证如下:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @program: zyfboot-javabasic
 * @description: ceshi
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:09
 **/
public class ZyfApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class);
        ZyfComponent myComponent = context.getBean(ZyfComponent.class);
        myComponent.sayHello();

        ZyfService zyfService = context.getBean(ZyfService.class);
        zyfService.performAction();
        context.close();
    }
}

返回结果符合预期。

五、构造 BeanDefinition:通过XML配置文件

通过 XML 配置文件构造 BeanDefinition 是 Spring 框架最传统的方式之一。

传统的方式是通过 XML 配置文件来定义 Bean,XML 配置文件中的 <bean> 元素就是描述 BeanDefinition 的方式之一。

现在,我们将之前的ZyfService配置到XML 配置文件中如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--    <bean id="myTestMsgBean" class="org.zyf.javabasic.springextend.bfppext.MyTestMsgBean">-->
<!--        <property name="message" value="Original Message"/>-->
<!--    </bean>-->

<!--    <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForUpdateBean"/>-->
<!--    <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForNewDefinRegi"/>-->
<!--    <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForDefineMata"/>-->

    <bean id="zyfService" class="org.zyf.javabasic.spring.beandefinition.ZyfService"/>

</beans>

使用 ClassPathXmlApplicationContext 类来加载配置文件,从而启动 Spring 容器如下:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @program: zyfboot-javabasic
 * @description: ceshi
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:09
 **/
public class ZyfApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class);
        ZyfComponent myComponent = context.getBean(ZyfComponent.class);
        myComponent.sayHello();

        ZyfService zyfService = context.getBean(ZyfService.class);
        zyfService.performAction();

        ClassPathXmlApplicationContext contextXml = new ClassPathXmlApplicationContext("zyf_application_context.xml");
        ZyfService myServiceXml = context.getBean("zyfService", ZyfService.class);
        myServiceXml.performAction();

        context.close();
    }
}

六、构造 BeanDefinition:通过编程方式

通过编程方式构造 BeanDefinition允许开发人员完全控制 BeanDefinition 的创建过程。

我们可以使用 Spring 提供的类(如 GenericBeanDefinitionRootBeanDefinition 等)来创建 BeanDefinition 对象,并指定 Bean 的各种属性和配置信息。

也就是可以直接操作代码即可,在原始代码上直接修改如下:

package org.zyf.javabasic.spring.beandefinition;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @program: zyfboot-javabasic
 * @description: ceshi
 * @author: zhangyanfeng
 * @create: 2024-05-02 11:09
 **/
public class ZyfApplication {
    public static void main(String[] args) {
        System.out.println("===================通过模式注解 + 组件扫描方式===================");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class);
        ZyfComponent myComponent = context.getBean(ZyfComponent.class);
        myComponent.sayHello();

        System.out.println("===================通过配置类 + @Bean 注解方式===================");
        ZyfService zyfService = context.getBean(ZyfService.class);
        zyfService.performAction();

        System.out.println("===================通过XML配置文件方式===================");
        ClassPathXmlApplicationContext contextXml = new ClassPathXmlApplicationContext("zyf_application_context.xml");
        ZyfService myServiceXml = context.getBean("zyfService", ZyfService.class);
        myServiceXml.performAction();

        System.out.println("===================通过编程方式===================");
        // 创建一个 BeanDefinition,并指定类名
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(ZyfService.class);

        // 将 BeanDefinition 注册到 Spring 容器中
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
        beanFactory.registerBeanDefinition("zyfService", beanDefinition);

        // 获取 Bean,并调用方法
        ZyfService myService = context.getBean("zyfService", ZyfService.class);
        myService.performAction();

        context.close();
    }
}

验证打印如下,符合预期。

七、对构造分析对比

方式 配置灵活性 可读性和维护性 适用场景
模式注解 + 组件扫描方式

配置简单,基于约定优于配置的原则

适用于大部分情况

配置简单,但可能导致扫描过多的类,降低可读性

简单的项目或小规模团队

快速开发原型或中小型项目

配置类 + @Bean 注解方式 灵活性更高,可以通过代码进行复杂的配置和逻辑处理 相对易读,也更易于维护,因为 Bean 的创建过程明确可见

需要更灵活配置的项目

对可维护性要求较高的大型项目

通过 XML 配置文件 传统方式,配置直观,但可读性较差 相对直观,但随项目规模增大,配置文件可能变得臃肿

传统项目

需要与其他框架整合的场景

通过编程方式 灵活性最高,可以完全控制 BeanDefinition 的创建过程 -代码量较多,可读性较差,维护成本相对较高

需要动态配置 BeanDefinition 的场景

需要在运行时动态注册 Bean 的场景

参考链接和文章

Java-based Container Configuration :: Spring Framework

Bean Scopes :: Spring Framework

Container Overview :: Spring Framework


网站公告

今日签到

点亮在社区的每一天
去签到