核心概念:@Bean 和 @Configuration (Basic Concepts: @Bean and @Configuration)
Spring 的 Java 配置支持中,核心组件是标注了 @Configuration 的类和标注了 @Bean 的方法。
@Bean 注解用于指示一个方法会实例化、配置并初始化一个新对象,该对象由 Spring IoC 容器管理。对于那些熟悉 Spring 的 <beans/> XML 配置的人来说,@Bean 注解的作用与 <bean/> 元素完全相同。你可以在任何 Spring @Component 类中使用 @Bean 标注的方法。然而,它们最常与 @Configuration Bean 一起使用。
将一个类标注为 @Configuration 表示其主要目的是作为 Bean 定义的来源。此外,@Configuration 类允许通过调用同一类中的其他 @Bean 方法来定义 Bean 间的依赖关系。一个最简单的 @Configuration 类如下所示:
@Configuration
public class AppConfig {
@Bean
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}@Configuration
class AppConfig {
@Bean
fun myService(): MyServiceImpl {
return MyServiceImpl()
}
}上述 AppConfig 类等价于以下的 Spring <beans/> XML 配置:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>配置类中是否应该在 @Bean 方法之间进行本地调用?
在常见场景中,@Bean 方法通常在 @Configuration 类内部声明,这样可以确保应用“完全(Full)”配置类处理,从而使跨方法引用被重定向到容器的生命周期管理。这可以防止同一 @Bean 方法被意外地通过普通的 Java 方法调用来执行,有助于减少难以追踪的微妙 Bug。
当 @Bean 方法声明在未标注 @Configuration 的类中,或者声明了 @Configuration(proxyBeanMethods=false) 时,它们被称为以“轻量(lite)”模式处理。在这种情况下,@Bean 方法实际上是一种通用的工厂方法机制,没有特殊的运行时处理(即不会为其生成 CGLIB 子类)。对这种方法的自定义 Java 调用不会被容器拦截,因此其行为就像普通的方法调用一样,每次都会创建一个新实例,而不是复用该 Bean 现有的单例(或作用域)实例。
因此,在没有运行时代理(即 Lite 模式)的情况下,类上的 @Bean 方法根本不应该声明 Bean 间的依赖关系。相反,它们应该操作其所属组件的字段,或者可选地操作工厂方法声明的参数,以便接收自动装配的协作对象。因此,此类 @Bean 方法永远不需要调用其他 @Bean 方法;每一个此类调用都可以通过工厂方法参数来表达。这样做的好处是运行时无需应用 CGLIB 子类化,从而减少了开销和内存占用。
接下来的章节将深入探讨 @Bean 和 @Configuration 注解。但在那之前,我们将先介绍使用基于 Java 的配置创建 Spring 容器的各种方式。
补充教学 —— 深入理解 Full 模式与 Lite 模式
这是面试中关于 Spring Java 配置最高频的考点之一。
1. 为什么要用 CGLIB?(Full 模式的魔力) 在普通的 Java 代码中,如果你连续调用两次 myService() 方法,你肯定会得到两个不同的对象。 但在 Spring 的 @Configuration 类(默认 Full 模式)中:
@Configuration
public class MyConfig {
@Bean
public User user() {
return new User("Tom");
}
@Bean
public Order order() {
// 直接调用上面的方法
return new Order(user());
}
@Bean
public Pay pay() {
// 再次调用上面的方法
return new Pay(user());
}
}因为 @Configuration 类被 CGLIB 代理了,Spring 会拦截对 user() 的调用。如果容器里已经有了 user 单例,它会直接返回旧的,而不是运行方法内部的 new User()。这保证了 Order 和 Pay 引用的是同一个 User 实例。
2. 什么时候使用 Lite 模式? 当你设置 @Configuration(proxyBeanMethods = false) 时,你就进入了 Lite 模式。
- 优点:启动速度更快,因为不需要生成 CGLIB 代理类。在 Spring Boot 的许多自动配置类中,为了极致的启动速度,通常会开启此选项。
- 代价:你失去了直接调用
@Bean方法来实现依赖注入的能力。
3. 如何在 Lite 模式下实现依赖注入? 既然不能直接调用方法,你应该使用方法参数注入:
@Configuration(proxyBeanMethods = false)
public class MyLiteConfig {
@Bean
public User user() { ... }
@Bean
public Order order(User user) { // 通过参数传入,由 Spring 容器自动注入
return new Order(user);
}
}这其实也是 Spring 官方更推荐的写法,因为它在 Full 和 Lite 模式下都能工作,且语义更清晰。
4. 总结建议
- 如果你需要在配置类的方法之间互相调用且必须保证单例一致性,用默认的 Full 模式。
- 如果你追求极致性能,且所有的依赖都通过方法参数注入(从未在类中内部调用过
@Bean方法),请使用 Lite 模式。 - 避坑指南:如果你在
@Component类(而不是@Configuration)里写@Bean方法,那就是 Lite 模式!千万不要在里面互相调用方法。