目录
一、对BeanDefinition的理解
(一)理解元信息
(二)BeanDefinition理解分析
二、BeanDefinition的结构设计分析
(一)整体结构体会
(二)重要接口和类分析
三、构造 BeanDefinition:模式注解 + 组件扫描方式
四、构造 BeanDefinition:配置类 + @Bean 注解方式
五、构造 BeanDefinition:通过XML配置文件
六、构造 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 {// 配置类的其他内容@Beanpublic 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 提供的类(如 GenericBeanDefinition
、RootBeanDefinition
等)来创建 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