> 技术文档 > 【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean


文章目录

  • 前言
  • 一、创建两个组件
  • 二、使用传统方式
    • 源代码
    • 解释
  • 三、使用SpringBoot方法
    • 源代码
    • 解释
  • 四、查看是否添加到组件中
    • 查看
    • 自定义组件名
    • 配置类在容器中注册的是单实例组件
    • 配置类本身也是容器中的一个组件
    • Configuration的proxyBeanMethods属性:代理bean的方法
      • `proxyBeanMethods` 参数详解
      • 详细说明
      • 1. `proxyBeanMethods = true`(默认)
      • 2. `proxyBeanMethods = false`
      • 选择建议
  • 总结

前言

@Configuration // 告诉 SpringBoot 这是一个配置类 == 配置文件
@Bean // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例
@Bean(“tom123”) // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例

IOC 容器(Inversion of Control Container)是实现 控制反转 思想的一个运行期框架组件,用来集中创建、装配、管理对象(Bean)及其生命周期。
一句话:原来由程序代码 new 的对象,现在统一交给容器“注入”进来,代码只声明“我需要什么”,而不关心“怎么得到”。

  • 控制反转(IoC):对象创建与依赖绑定的“控制权”从业务代码反转到容器。
  • 依赖注入(DI):容器把依赖对象“注入”到需要它的地方,常见方式:
    • 构造函数注入
    • Setter 注入
    • 字段/注解注入
// 声明组件@Componentpublic class OrderService { }@RestControllerpublic class OrderController { @Autowired // 容器注入 private OrderService service;}

启动 Spring 后,容器扫描注解 → 创建 OrderService → 注入 OrderController,全程无 new。

一、创建两个组件

我们先创建两个组件User和Pet,也就是两个类,放在bean包里面。

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

package com.hello.bean;/** * 用户 */public class User { // 要把这两个组件添加到容器中 private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } // 添加带参数的构造方法 public User(String name, Integer age){ this.name = name; this.age = age; } @Override public String toString() { return \"User [name=\" + name + \", age=\" + age + \"]\"; }}
package com.hello.bean;/** * 宠物 */public class Pet { private String name; public String getName(){ return name; }; public void setName(String name){ this.name = name; } // 添加构造器 public Pet() { // 无参构造器 } public Pet(String name) { // 带参构造器 this.name = name; } @Override public String toString() { return \"Pet{\" + \"name=\'\" + name + \'\\\'\' + \'}\'; }}

二、使用传统方式

要添加到容器中,首先在资源文件夹中新建beans.xml文件,然后进行组件的添加。

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

源代码

<?xml version=\"1.0\" encoding=\"UTF-8\"?><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=\"user\" class=\"com.hello.bean.User\"> <property name=\"name\" value=\"zhangsan\"></property> <property name=\"age\" value=\"18\"></property> </bean> <bean id=\"cat\" class=\"com.hello.bean.Pet\"> <property name=\"name\" value=\"tomcat\"></property> </bean></beans>

解释

这个XML文件是一个典型的Spring框架配置文件,用于定义和配置Spring容器中的Bean。下面是对这个配置文件的详细解释:

  1. 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\">
    • 第一行是标准的XML声明
    • 定义了beans根元素和Spring的命名空间
    • 指定了XML Schema定义文件的位置
  2. User Bean定义

    <bean id=\"user\" class=\"com.hello.bean.User\"> <property name=\"name\" value=\"zhangsan\"></property> <property name=\"age\" value=\"18\"></property></bean>
    • 定义了一个ID为\"user\"的Bean
    • 类路径为com.hello.bean.User
    • 设置了两个属性:
      • name属性值为\"zhangsan\"
      • age属性值为18
  3. Pet Bean定义

    <bean id=\"cat\" class=\"com.hello.bean.Pet\"> <property name=\"name\" value=\"tomcat\"></property></bean>
    • 定义了一个ID为\"cat\"的Bean
    • 类路径为com.hello.bean.Pet
    • 设置了name属性值为\"tomcat\"
  4. 配置特点

    • 使用了setter注入方式(通过元素)
    • 注入的都是简单值(String和int)
    • 没有定义Bean之间的依赖关系
  5. 对应的Java类结构
    假设的User类:

    package com.hello.bean;public class User { private String name; private int age; // 必须有setter方法 public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; }}

    假设的Pet类:

    package com.hello.bean;public class Pet { private String name; public void setName(String name) { this.name = name; }}
  6. 使用场景

    • 这种配置方式在传统的Spring XML配置中很常见
    • 适用于简单的Bean定义和属性注入
    • 在现代Spring Boot应用中,这种配置方式通常被注解配置(如@Component@Configuration)取代

注意:要使这个配置正常工作,对应的Java类必须有无参构造函数和相应的setter方法,因为这里使用的是setter注入方式。

三、使用SpringBoot方法

新建config.MyConfig类,在其中写入代码:

源代码

package com.hello.config;import com.hello.bean.User;import com.hello.bean.Pet;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration // 告诉 SpringBoot 这是一个配置类 == 配置文件public class MyConfig { @Bean // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例 public User user01() { return new User(\"zhangsan\", 18); } @Bean // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例 public Pet cat(){ return new Pet(\"tomcat\"); }}

解释

使用 Java 配置类替代 XML 配置的 Spring Boot 配置示例,下面是对这段代码的详细解释:

  1. 类级别注解
@Configuration // 告诉 SpringBoot 这是一个配置类 == 配置文件public class MyConfig {
  • @Configuration 注解标识这是一个 Spring 配置类,相当于 XML 配置文件
  • 配置类中可以定义多个 @Bean 方法,用于向 Spring 容器注册组件
  1. User Bean 定义
@Bean // 给容器中添加组件public User user01() { return new User(\"zhangsan\", 18);}
  • @Bean 注解表示该方法会返回一个对象,该对象要注册到 Spring 容器中
  • 默认情况下,Bean 的名称(ID)就是方法名 user01
  • 这里创建了一个 User 对象,使用构造器注入方式设置 name=“zhangsan” 和 age=18
  1. Pet Bean 定义
@Bean // 给容器中添加组件public Pet cat(){ return new Pet(\"tomcat\");}
  • 同样使用 @Bean 注解,Bean 的名称为 cat
  • 创建了一个 Pet 对象,设置 name=“tomcat”
  1. 与 XML 配置的对比

    • 等效于之前的 XML 配置,但使用了更现代的 Java 配置方式
    • 不需要 setter 方法,直接使用构造器注入
    • 类型安全,编译器可以检查类型错误
    • 可以方便地添加逻辑(如条件判断等)
  2. 对应的 Java 类结构
    假设的 User 类(需要对应的构造器):

package com.hello.bean;public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; }}

假设的 Pet 类:

package com.hello.bean;public class Pet { private String name; public Pet(String name) { this.name = name; }}
  1. 使用特点

    • 这是 Spring Boot 推荐的首选配置方式
    • 配置类本身也是一个 Spring 组件(@Configuration@Component 的派生注解)
    • 可以方便地与其他 Spring 特性(如 @Profile, @Conditional 等)结合使用
    • 支持方法间调用(Spring 会拦截确保单例)
  2. 获取这些 Bean
    在应用中可以通过以下方式获取:

// 通过类型获取User user = applicationContext.getBean(User.class);// 通过名称获取Pet pet = (Pet) applicationContext.getBean(\"cat\");

这种基于 Java 的配置方式比 XML 更类型安全、更灵活,是现代 Spring Boot 应用的主要配置方式。

四、查看是否添加到组件中

查看

在主程序中进行容器IOC中所有组件的输出。
【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

可以搜到对应的组件,说明组件已经添加成功。
【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

自定义组件名

可以通过@Bean(“tom123”) 这样的方式自定义组件名,这样添加组件名称就是不是默认的方法名。

 @Bean(\"tom123\") // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例 public Pet cat(){ return new Pet(\"tomcat\"); }

我们来进行组件名的输出,点启动项目。在控制台观察、搜索添加的组件。

 public static void main(String[] args) { // 1、返回我们IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // 2、查看容器里面的所有组件 String[] names = run.getBeanDefinitionNames(); for (String name : names){ System.out.println(name); } // 3、从容器中获取组件 // 拼接最终启动成功信息 String successMessage = \"🎉 **启动成功!** (ノ◕ヮ◕)ノ*:・゚✧\\n\" + \"✨ *服务已就绪,端口 8083* ✨\\n\" + \"💻 访问地址:`http://localhost:8083`\\n\" + \"💪 **Go! Go! Go!** (ง •_•)ง\"; System.out.println(successMessage); }

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean
这样我们就给容器中注入了两个组件。

配置类在容器中注册的是单实例组件

package com.hello.config;import com.hello.bean.User;import com.hello.bean.Pet;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 配置类里面使用@Bean标注在方法上给容器中添加组件,默认是单实例的 */@Configuration // 告诉 SpringBoot 这是一个配置类 == 配置文件public class MyConfig { @Bean // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例 public User user01() { return new User(\"zhangsan\", 18); } @Bean(\"tom123\") // 给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例 public Pet cat(){ return new Pet(\"tomcat\"); }}

我们可以来读取组件对象进行验证一下。

// 3、从容器中获取组件 Pet tom1 = run.getBean(\"tom123\", Pet.class); Pet tom2 = run.getBean(\"tom123\", Pet.class); System.out.println(\"两个组件是否相同\" + (tom1 == tom2));

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

配置类本身也是容器中的一个组件

当时一个类名上加了@Configuratio以后,这个类也会被加入到容器中,作为一个组件。

验证:

MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean);

【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean

Configuration的proxyBeanMethods属性:代理bean的方法

应用场景:解决组件依赖问题。

 * 3、proxyBeanMethods:代理bean的方法 * full: 全量模式,IOC容器启动时,会创建所有的单实例Bean(true) * light: 轻量模式,IOC容器启动时,不会创建单实例Bean,而是当调用getBean方法时,才会创建Bean(false)

proxyBeanMethods 参数详解

proxyBeanMethods@Configuration 注解中的一个重要属性,它决定了配置类中 @Bean 方法是否被代理。下面是它的取值含义表格:

值 含义 适用场景 性能影响 容器行为 true (默认值) 启用代理,确保 @Bean 方法调用总是返回相同的实例(单例) 需要保证单例的场景,方法间有依赖关系时 有轻微性能开销(CGLIB代理) Spring会拦截方法调用,检查容器中是否已存在该Bean false 禁用代理,直接调用方法 不需要方法间依赖,或追求启动速度的场景 无代理开销,启动更快 每次调用方法都会真正执行,不保证单例

详细说明

1. proxyBeanMethods = true(默认)

@Configuration(proxyBeanMethods = true)public class MyConfig { @Bean public User user() { return new User(pet()); // 无论调用多少次,返回的都是同一个Pet实例 } @Bean public Pet pet() { return new Pet(\"tomcat\"); }}
  • 特点:方法间调用会被Spring拦截,确保单例
  • 优点:保证Bean的单例性,方法间依赖安全
  • 缺点:有CGLIB代理开销

2. proxyBeanMethods = false

@Configuration(proxyBeanMethods = false)public class MyConfig { @Bean public User user() { return new User(pet()); // 每次调用都会new新的Pet实例 } @Bean public Pet pet() { return new Pet(\"tomcat\"); }}
  • 特点:相当于普通Java方法调用
  • 优点:无代理开销,启动更快
  • 缺点:不能保证方法间调用的单例性

选择建议

考虑因素 推荐设置 配置类中的@Bean方法需要相互调用 true 需要严格的单例保证 true 追求应用启动速度 false 配置类中没有方法间调用 false Spring Boot 2.2+ 的@Configuration类 通常设为false

在Spring Boot 2.2+中,很多场景下推荐使用proxyBeanMethods = false以提高性能,特别是大型应用。

总结

本文全面对比了Spring Boot中两种主要的组件配置方式:传统XML配置和现代Java配置类方式,并深入分析了@Configuration@Bean注解的核心特性。

  1. 配置方式演进

    • XML配置是传统Spring的经典方式,通过标签定义组件,依赖setter注入
    • Java配置类是现代Spring Boot推荐方式,使用@Configuration@Bean注解,更类型安全灵活
  2. 注解核心功能

    • @Configuration标记配置类,替代XML配置文件
    • @Bean注解方法向容器注册组件,默认单例,方法名作为组件ID
    • 支持自定义组件名(@Bean(\"name\"))和方法间依赖调用
  3. 高级特性

    • proxyBeanMethods参数控制代理行为,平衡单例保证与性能
    • 配置类本身也是容器组件,可通过上下文获取
    • 与各种条件注解(@Conditional等)无缝集成
  4. 实践建议

    • 新项目优先使用Java配置类方式
    • 简单场景可设置proxyBeanMethods=false提升性能
    • 需要严格单例和方法间依赖时保持默认true
    • 合理命名组件ID提高可读性

现代Spring Boot应用开发中,基于注解的配置方式凭借其类型安全、代码导航方便和与Java语言的天然契合等优势,已成为事实标准。理解这些核心配置机制,是掌握Spring Boot自动配置原理的基础,也是进行高效应用开发的关键。