一,Spring 的 Bean 对象什么?

1.1 Bean对象是什么?

        在Java传统的项目里面,代码各层之间的对象引入与调用基本上都是通过关键字 new 创建出来。对象之间的依赖关系是由程序员手动管理。这种方式代码耦合度极高,维护成本极高。

        并且每次请求都是会重新创建一次,从Controller 层,Service层,Mapper层 三层之间的对象依赖引入都会重新创建一次。最开始是程序员之间自己会写一个简单的工具类容器由于管理对象,在请求到的时候,比如请求到Controller层,而Controller 引用了 10 个service 的对象,先去容器里面查询是否拥有对应的引用对象,有就直接使用,没有则创建新的,这样大大减少内存的消耗,以及频繁创建对象性能消耗。

1.2 简单案例

import java.util.concurrent.ConcurrentHashMap;
import java.lang.reflect.Constructor;

// 简易对象容器(单例)
public class SimpleContainer {
    // 使用 ConcurrentHashMap 确保线程安全
    private static final ConcurrentHashMap<Class<?>, Object> beanCache = new ConcurrentHashMap<>();

    // 获取或创建 Bean(支持单例模式)
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<T> clazz) {
        // 先从缓存中获取
        Object bean = beanCache.get(clazz);
        if (bean == null) {
            // 双重检查锁(简化版,实际可加锁)
            synchronized (SimpleContainer.class) {
                bean = beanCache.get(clazz);
                if (bean == null) {
                    try {
                        // 假设类有无参构造函数
                        Constructor<T> constructor = clazz.getDeclaredConstructor();
                        constructor.setAccessible(true);
                        bean = constructor.newInstance();

                        // 自动注入依赖(简化:只处理字段上的 @Autowired 模拟)
                        injectDependencies(bean);

                        // 放入缓存(单例)
                        beanCache.put(clazz, bean);
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to create bean: " + clazz.getName(), e);
                    }
                }
            }
        }
        return (T) bean;
    }

    // 简单依赖注入(模拟 @Autowired)
    private static void injectDependencies(Object bean) throws Exception {
        Class<?> clazz = bean.getClass();
        for (var field : clazz.getDeclaredFields()) {
            // 假设所有字段都需要注入(实际应检查注解如 @Autowired)
            if (!field.getType().isPrimitive()) {
                field.setAccessible(true);
                Object dependency = getBean(field.getType());
                field.set(bean, dependency);
            }
        }
    }
}

// 示例:Controller 层
class UserController {
    // 依赖注入
    private UserService userService;
    private OrderService orderService;
    // ... 其他 8 个 service

    public void handleRequest() {
        userService.process();
        orderService.process();
    }
}

// 示例:Service 层
class UserService {
    private UserMapper userMapper;

    public void process() {
        userMapper.query();
    }
}

class OrderService {
    public void process() {
        System.out.println("Processing order...");
    }
}

// 示例:Mapper 层
class UserMapper {
    public void query() {
        System.out.println("Querying user from DB...");
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        // 模拟请求入口:获取 Controller(自动注入所有依赖)
        UserController controller = SimpleContainer.getBean(UserController.class);

        // 处理请求(所有依赖对象只创建一次)
        controller.handleRequest();
    }
}
  • SimpleContainer 是一个静态工具类容器,内部使用 ConcurrentHashMap 缓存已创建的对象(模拟单例)。
  • getBean(Class<T>) 方法:若对象不存在则通过反射创建,并递归注入其依赖(字段级)。
  • 依赖注入逻辑非常简化,实际 Spring 等框架会通过注解(如 @Autowired)、类型匹配、作用域等更复杂的机制处理。
  • 此伪代码假设所有类都有无参构造函数,且所有非基本类型字段都需要注入。

        当然,对于对象管理这一块业务功能,可以自己手动实现的简易容器、也可以自己手动引入这个方面的依赖包:Google Guice、Java EE CDI、PicoContainer

        不过 Java里面最强大,还得是Spring Bean

1.2 Spring Bean 与 其他对象管理工具 区别

        Spring Bean 与其他对象管理工具(如手动实现的简易容器、Google Guice、Java EE CDI、PicoContainer 等)在设计目标、功能完整性、生态集成等方面存在显著区别。

1. ** vs 手动编写的简易容器(如上文伪代码)**

功能完整性

仅支持基本单例缓存和简单反射创建

完整的 IoC/DI、生命周期、作用域、AOP 等

线程安全

需自行处理(如加锁)

容器本身线程安全,Bean 作用域可控

依赖解析

通常只支持字段注入,无类型/泛型处理

支持复杂依赖图解析、循环依赖处理(部分场景)

可维护性

代码耦合高,难以扩展

配置与逻辑分离,易于测试和维护

适用场景

教学演示、极简项目

企业级应用开发

结论:简易容器适合理解 IoC 原理,但无法替代 Spring。


2. ** vs Google Guice**

配置方式

纯 Java(Module +@Inject

注解/XML/Java Config 多种方式

学习曲线

轻量、API 简洁

功能丰富但较复杂

AOP 支持

依赖 AOP Alliance,功能较弱

内置强大 AOP(基于代理)

生态整合

无官方 Web/DB/Security 支持

完整 Spring 生态(Boot, Data, Security 等)

适用场景

轻量级应用、Android、非 Spring 项目

企业级 Java 应用主流选择

结论:Guice 更轻量,Spring 更全能。


3. ** vs Java EE CDI(Contexts and Dependency Injection)**

标准

Java EE/Jakarta EE 官方标准

Spring 自有实现(事实标准)

作用域

内置 Request/Session/Application 等 Web 作用域

需 Web 环境支持(如 Spring MVC)

事件模型

强大的事件与观察者机制

基础事件支持

可移植性

可在任何 Jakarta EE 容器运行

依赖 Spring 容器

流行度

在 Jakarta EE 生态中使用

在 Spring 生态中占绝对主导

结论:CDI 是 Jakarta EE 的标准方案,Spring 是事实上的行业标准。


4. ** vs PicoContainer / Dagger 等**

  • PicoContainer:极简 IoC,无注解,纯 API 配置,适合嵌入式或测试场景。
  • Dagger(Google):编译时依赖注入(主要用于 Android),无运行时反射,性能高但灵活性低。
  • Spring:运行时注入,功能全面,适合复杂业务系统。

关键区别总结

定位

企业级全栈框架的核心组件

轻量级、专用或标准实现

功能广度

极广(IoC + AOP + 事务 + 安全 + Web + ...)

通常只专注依赖注入

配置灵活性

极高(多种配置方式 + 条件化)

通常较固定(如 Guice 只用 Java)

社区与生态

庞大(Spring Boot 自动配置降低门槛)

相对小众或局限于特定平台

性能

运行时反射(可通过优化缓解)

部分工具(如 Dagger)编译时生成,性能更高


二,JavaBeans 组件规范

在讲解 Spring Bean 之前,首先需要了解的到的是 JavaBeans 相关的概念。

JavaBeans 是什么?和 Spring中概念的"Bean" 有什么区别?

首先,JavaBeans 是 Sun Microsystems 在 1997 年提出的一套组件规范,目标是

  • 创建可复用、可配置、可可视化(如 IDE 拖拽)的 Java 组件。
  • 支持内省(Introspection):在运行时自动发现对象的属性、方法、事件等。

📌 关键点:JavaBeans 是一套约定 + API,不是 Spring 发明的!

2.1 ✅ JavaBean 必须满足的约定:

    1. 公共无参构造函数public MyBean()
    2. 属性通过 getter/setter 访问(如 getName() / setName(String)
    3. 实现 Serializable 接口(可选,但推荐)
    4. 是具体的类(非抽象)

其中编写java的时候,体现最多的就是java的普通实体类 Entry上,其中的属性都是通过getting,setting进行获取与设置。但是常规的Service 层,Mapper 层都不符合JavaBeans 的规范。但是Spring 只要是对象就可以被管理。

2.2 ✅Spring Bean:Spring 容器管理的“对象实例”

📌 定义

A Spring Bean is any object that is instantiated, assembled, and managed by the Spring IoC container.

一个 Spring Bean 是任何由 Spring IOC 容器实例化,组装和管理的对象

2.3 ✅ Spring Bean 不要求满足 JavaBean 规范:

  • 可以没有无参构造(用构造器注入)
  • 可以没有 setter(用字段注入或构造注入)
  • 可以是抽象类的子类、内部类、甚至 Lambda(某些场景)

🎯 设计目标:

依赖注入(DI)

生命周期管理@PostConstruct, DisposableBean

AOP 代理

作用域控制(singleton, prototype 等)

🔧 核心机制:

  • BeanFactory / ApplicationContext
  • BeanDefinition
  • BeanPostProcessor

图1:用图展示关系:

Java Object
  │
  ├── (符合规范) → JavaBean → 可被 Introspector 分析
  │
  └── (被 Spring 管理) → Spring Bean(无论是否是 JavaBean)

图 2:继承与实现关系(简化版)

+---------------------+
|    java.lang.Object |
+---------------------+
          ▲
          |
+---------------------+       +----------------------+
|     JavaBean        |       |     Spring Bean      |
| (conforms to spec)  |       | (managed by Spring)  |
+---------------------+       +----------------------+
| - public no-arg     |       | - Any Java object    |
|   constructor       |       | - No spec required   |
| - getXxx()/setXxx() |       | - Managed by         |
| - Serializable      |       |   ApplicationContext |
+---------------------+       +----------------------+
          ▲                               ▲
          |                               |
+---------------------+       +----------------------+
|   MyUserBean        |       |   OrderService       |
| (valid JavaBean)    |       | (NOT a JavaBean,     |
+---------------------+       |  but a Spring Bean)  |
                                +----------------------+
                                | - private final Repo |
                                | - public OrderService(Repo) |
                                +----------------------+

💡 说明:

  • MyUserBean 同时是 JavaBean 和 Spring Bean
  • OrderServiceSpring Bean,但不是 JavaBean(无无参构造、无 setter)

图 3:容器与内省机制对比

+------------------+          +-----------------------+
|  Builder Tool    |          |   Spring Container    |
|  (e.g. NetBeans) |          |  (ApplicationContext) |
+------------------+          +-----------------------+
          |                               |
          | uses Introspector             | uses BeanFactory
          ▼                               ▼
+------------------+          +-----------------------+
|   JavaBean       |          |   Any Java Object     |
|  (must follow    |          |  (no constraints)     |
|   JavaBeans spec)|          |                       |
+------------------+          +-----------------------+
          ▲                               ▲
          |                               |
+------------------+          +-----------------------+
|  PropertyEditor  |          |  BeanPostProcessor    |
|  (type conversion)|         |  (AOP, init, etc.)    |
+------------------+          +-----------------------+

代码示例:直观对比

✅ 合法的 JavaBean(也是 Spring Bean)

public class Person implements Serializable {
    private String name;

    public Person() {} // 无参构造

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
@Service
public class PaymentService {
    private final UserRepository userRepo; // final 字段

    // 无无参构造,无 setter
    public PaymentService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    public void process() { /* ... */ }
}

🔥 这个 PaymentService不符合 JavaBean 规范(无无参构造、无 setter),但它是完全合法的 Spring Bean

2.4 JavaBeans 核心设计组件解析

JavaBeans 是所属在 java.beans 包下的;源码如下:


package java.beans;

import java.awt.Image;

/**
 * @since 1.1
 */
public interface BeanInfo {

    /** 
     * BeanDescriptor —— Bean 自身的描述
     *	描述 Bean 的基本信息:
     *		name:类名
     *		displayName:显示名称(用于 IDE)
     *		shortDescription:简短说明
     *		icon:可视化图标(Image)
     */
    BeanDescriptor getBeanDescriptor();

    /** 
     * EventSetDescriptor —— 事件集描述器
     * 描述 Bean 能触发或监听的事件(基于 Observer 模式)
     * 		例如:addXXXListener / removeXXXListener 方法对。
     */
    EventSetDescriptor[] getEventSetDescriptors();

    /**
     * 返回该 Bean 的默认事件在 EventSetDescriptor[] 数组中的索引。 
     */
    int getDefaultEventIndex();

    /** 
     * PropertyDescriptor —— 属性描述器
     * 描述一个属性(Property),对应 Java 中的 getter/setter 对。
     */
    PropertyDescriptor[] getPropertyDescriptors();

    /**
     * 返回该 Bean 的默认属性在 PropertyDescriptor[] 数组中的索引。 
     */
    int getDefaultPropertyIndex();

    /**
     * MethodDescriptor —— 方法描述器
     * 		描述 Bean 中的公开方法(除属性 getter/setter 外)。
     *		包含方法名、参数、返回类型、异常等。
     *		📌 注意:
     *        属性的 getter/setter 通常不包含在 MethodDescriptor 中,
     *        它们属于 PropertyDescriptor。 
     */
    MethodDescriptor[] getMethodDescriptors();

    /**
     * 许当前 BeanInfo 组合其他 BeanInfo 对象,实现元数据的继承或组合。 
     */
    BeanInfo[] getAdditionalBeanInfo();

    /**
     * Image —— 可视化图标
     * BeanInfo 可提供 16x16 或 32x32 的图标,用于 IDE 工具箱。
     * 通过 BeanDescriptor.getIcon(int iconKind) 获取。
     * 🖼️ 这是 JavaBeans 可视化设计理念的体现,如今已基本淘汰。 
     */
    Image getIcon(int iconKind);

    /**
     * Constant to indicate a 16 x 16 color icon.
     */
    static final int ICON_COLOR_16x16 = 1;

    /**
     * Constant to indicate a 32 x 32 color icon.
     */
    static final int ICON_COLOR_32x32 = 2;

    /**
     * Constant to indicate a 16 x 16 monochrome icon.
     */
    static final int ICON_MONO_16x16 = 3;

    /**
     * Constant to indicate a 32 x 32 monochrome icon.
     */
    static final int ICON_MONO_32x32 = 4;
}

三,Spring Bean 设计讲解

在上面介绍了 JavaBeans 基本概念,从中我们得知 JavaBeans 是一套约定,且与Spring 没有任何关系。JavaBeans 的约定是

  • 创建可复用、可配置、可可视化(如 IDE 拖拽)的 Java 组件。
  • 支持内省(Introspection):在运行时自动发现对象的属性、方法、事件等。

        Spring组件中提供的对象管理业务,其中里面管理的对象都统一称为“Bean”。项目里面的Service 层代码,Mapper层,配置层代码,过滤器,监听器等等,只要将对象交由Spring 统一管理对象以便复用。都称为“Bean”

        换句话说:“Bean”这个是Spring 对象管理业务的管理对象的专业术语。

        当然,在座的各位看官自己也可以写一个对象管理组件,其中管理的对象统一名称自己也可以随便取。比如叫ManagedObject(被管理的对象)

        而这个被管理的对象可以是符合 JavaBeans 规范的对象,也可以是其他任意对象,甚至是Lambda 表达式写的写对象也都可以被spring 所管理。

3.1 为什么 Spring 创建一个Bean 流程比我们想象中的要复杂

        在我们 1.2 简单案例 SimpleContainer 里面,仅仅只是通过反射 + 容器(ConcurrentHashMap) 进行一个对象存储以便复用。这个简单的容器的功能作用仅仅只是对象管理以便复用。

        Spring 的 Bean 不仅仅只提供对象管理以便复用。还提供完整的 IoC/DI、生命周期、作用域、AOP 等。在设计Bean对象的时候,想要同时拥有这些对象,必然需要对Bean 的创建流程进行更复杂更细节更合理的拆分与设计。

3.2 Spring Bean 基本使用

        由于Spring组件是一个企业级项目的全栈的核心组件,其中不仅仅只包含对象管理这一个业务。还是容器(IOC),AOP,事物等等。现在通过代码提供一个极简、干净、无业务语义的示例,专门用于说明“如何通过编码方式让 Spring 创建并管理一个 Bean”。

        不使用 @Component@Configuration、XML 等声明式方式

        仅通过 Java 编码动态注册一个 Bean

        展示 Spring 容器如何实例化、管理它

        体现核心接口:BeanDefinitionRegistryBeanFactoryApplicationContext

3.3 示例代码(无业务,纯机制)

package com.toast.learn.spring.bean;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author toast
 * @time 2025/10/15
 * @remark
 */
class DemoBean {}

public class ProgrammaticBeanRegistration {
	public static void main(String[] args) {
		// 1. 创建一个未刷新的 ApplicationContext(底层是 DefaultListableBeanFactory)
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

		// 2. 获取 BeanDefinition 注册表(本质是 DefaultListableBeanFactory)
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();

		// 3. 手动构造一个 BeanDefinition,描述 "要创建 DemoBean 的实例"
		RootBeanDefinition beanDefinition = new RootBeanDefinition(DemoBean.class);

		// 4. 将该定义注册到容器中,名字为 "demoBean"
		registry.registerBeanDefinition("demoBean", beanDefinition);

		// 5. 刷新容器:触发 BeanFactory 初始化、预处理、准备就绪
		context.refresh();

		// 6. 从容器中获取 Bean —— 此时 Spring 才真正创建 DemoBean 实例
		DemoBean bean = context.getBean("demoBean", DemoBean.class);

		// 7. 验证:该 Bean 确实由 Spring 创建并管理
		System.out.println("Bean created by Spring: " + (bean != null)); // true
		System.out.println("Bean class: " + bean.getClass().getName());  // DemoBean

		context.close();
	}
}

输出结果:

> Task :spring-learn:run
Bean created by Spring: true
Bean class: com.toast.learn.spring.bean.DemoBean

暂时先忽略 BeanDefinitionRegistry,RootBeanDefinition... 等这类具体的作用的什么。先熟悉一个Bean 的基本创建流程。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐