Spring Boot @Configuration
与 @Component
笔记
1️⃣ 基本概念
注解 | 作用 | 是否有代理 | 适用场景 |
---|---|---|---|
@Component |
标记普通组件,将类交给 Spring 容器管理 | ❌ 没有 CGLIB 代理 | 普通 Bean,工具类、过滤器、监听器等 |
@Configuration |
标记配置类,用来声明 @Bean |
✅ 有 CGLIB 代理 | 声明 Bean,保证同一个 @Bean 方法多次调用返回同一个对象 |
2️⃣ 单例与代理机制
Spring 中的 Bean 默认是单例的:
-
直接注入 Bean(
@Autowired
)java@Autowired private MyService myServiceA; @Autowired private MyService myServiceB;
- 容器中只有一份
MyService
实例 - 无论注入多少次,拿到的都是同一个对象
- 和配置类是否用代理无关
- 容器中只有一份
-
通过配置类方法调用 Bean
javaMyService s1 = configA.myServiceA(); MyService s2 = configA.myServiceA(); MyService s3 = configB.myServiceB(); MyService s4 = configB.myServiceB();
-
@Configuration
的类被 Spring 生成了 CGLIB 代理- 每次调用
myServiceA()
→ 代理先检查容器 - 容器已有实例就直接返回 → s1 == s2
- 每次调用
-
@Component
的类没有代理- 每次调用
myServiceB()
→ 都是 new 一个新对象 → s3 != s4
- 每次调用
-
3️⃣ Demo 代码
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
// 业务类
@Service
public class MyService {
}
// 配置类 A
@Configuration
public class ConfigA {
@Bean
public MyService myServiceA() {
return new MyService();
}
}
// 配置类 B,用 Component 替代 Configuration
@Component
public class ConfigB {
@Bean
public MyService myServiceB() {
return new MyService();
}
}
// 测试 Runner
@Component
public class TestRunner implements CommandLineRunner {
@Autowired
private ConfigA configA;
@Autowired
private ConfigB configB;
@Autowired
private MyService myServiceA;
@Autowired
private MyService myServiceB;
@Override
public void run(String... args) throws Exception {
System.out.println("从容器拿到 myServiceA: " + myServiceA);
System.out.println("从容器拿到 myServiceB: " + myServiceB);
// 调用配置类方法
MyService s1 = configA.myServiceA();
MyService s2 = configA.myServiceA();
MyService s3 = configB.myServiceB();
MyService s4 = configB.myServiceB();
System.out.println("ConfigA.myServiceA() 两次调用是否同一对象: " + (s1 == s2)); // true
System.out.println("ConfigB.myServiceB() 两次调用是否同一对象: " + (s3 == s4)); // false
}
}
4️⃣ 结论
-
注入 Bean → 拿到的都是容器里的单例
-
调用配置类方法
@Configuration
→ 通过代理保证单例@Component
→ 没有代理,每次调用返回新对象
-
如果想保证通过方法调用也返回单例,必须使用
@Configuration
5️⃣ 小贴士
-
不要把
@Configuration
写成@Component
,否则通过配置类方法调用 Bean 可能产生多个对象 -
Bean 名称冲突可以通过
@Bean("myBeanName")
或开启覆盖:yamlspring.main.allow-bean-definition-overriding=true