理解 Spring Boot 的 Bean 加载原理后,更重要的是在实际开发中正确使用。本文从用户角度出发,总结 Bean 的使用规范和最佳实践。

Bean 定义方式

Spring 提供了多种定义 Bean 的方式,各有适用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ========== 方式一:@Component 及其派生注解 ==========
// 最常用,适合业务组件
@Component // 通用组件
@Service // 业务服务层
@Repository // 数据访问层
@Controller // Web 控制器
@RestController // RESTful API 控制器(@Controller + @ResponseBody)
@Configuration // 配置类

// 示例
@Service
public class UserServiceImpl implements UserService {
// 业务逻辑
}

// ========== 方式二:@Configuration + @Bean ==========
// 适合第三方库组件、需要复杂初始化逻辑的 Bean
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(10))
.build();
}
}

// ========== 方式三:@Import 直接导入 ==========
// 适合需要强制加载的配置类
@Import({RedisConfig.class, MongoConfig.class})
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

// ========== 方式四:ImportSelector / ImportBeanDefinitionRegistrar ==========
// 适合条件化加载大量 Bean(框架开发者常用)
public class MyAutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return new String[]{"com.example.MyConfiguration"};
}
}

选择建议:

场景推荐方式
自己的业务类@Service@Repository@Controller
第三方库组件@Configuration + @Bean
需要条件化加载@Configuration + @ConditionalXxx
框架/库开发ImportSelector / ImportBeanDefinitionRegistrar

Bean 注入方式

Spring 支持三种注入方式,各有优劣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// ========== 方式一:构造器注入(推荐)==========
// 优点:不可变、强制依赖、易于测试、避免循环依赖
@Service
public class OrderService {
private final UserService userService;
private final OrderRepository orderRepository;

// Spring 4.3+ 单构造器可省略 @Autowired
public OrderService(UserService userService, OrderRepository orderRepository) {
this.userService = userService;
this.orderRepository = orderRepository;
}
}

// ========== 方式二:Setter 注入 ==========
// 优点:可选依赖、灵活性高
// 缺点:可能导致对象状态不完整
@Service
public class PaymentService {
private NotificationService notificationService;

@Autowired(required = false) // 可选依赖
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}

// ========== 方式三:字段注入(不推荐)==========
// 缺点:难以测试、隐藏依赖、无法声明不可变字段
@Service
public class BadService {
@Autowired
private UserRepository userRepository; // 不推荐!

@Autowired
private EmailService emailService; // 不推荐!
}

注入方式对比:

特性构造器注入Setter 注入字段注入
不可变性✅ 支持❌ 不支持❌ 不支持
可选依赖❌ 不支持✅ 支持✅ 支持
单元测试✅ 容易 Mock✅ 容易 Mock❌ 需要 Reflection
循环依赖❌ 编译期发现✅ 可规避✅ 可规避
明确依赖✅ 显式声明⚠️ 不够明确❌ 隐藏依赖

最佳实践:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ✅ 推荐:构造器注入 + final 字段
@Service
public class GoodService {
private final RequiredDependency required;
private OptionalDependency optional;

public GoodService(RequiredDependency required) {
this.required = required;
}

@Autowired(required = false)
public void setOptional(OptionalDependency optional) {
this.optional = optional;
}
}

接口与基类的 Bean 注入

当注入目标是接口或基类时,Spring 需要确定具体注入哪个实现。这是实际开发中常见且容易混淆的场景。

接口注入的基本原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 接口定义
public interface PaymentService {
void pay(Order order);
}

// 实现类一
@Service
public class AlipayService implements PaymentService {
@Override
public void pay(Order order) {
// 支付宝支付逻辑
}
}

// 实现类二
@Service
public class WechatPayService implements PaymentService {
@Override
public void pay(Order order) {
// 微信支付逻辑
}
}

此时容器中有两个 PaymentService 类型的 Bean:

  • alipayService(类名首字母小写)
  • wechatPayService

多实现的注入方式

方式一:按名称注入(@Qualifier)

1
2
3
4
5
6
7
8
9
@Service
public class OrderService {
private final PaymentService paymentService;

// 明确指定 Bean 名称
public OrderService(@Qualifier("alipayService") PaymentService paymentService) {
this.paymentService = paymentService;
}
}

方式二:字段名匹配

1
2
3
4
5
6
7
8
9
@Service
public class OrderService {
// 字段名与 Bean 名称一致,自动匹配
private final PaymentService alipayService;

public OrderService(PaymentService alipayService) {
this.alipayService = alipayService;
}
}

方式三:@Primary 指定默认实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
@Primary // 标记为默认实现
public class AlipayService implements PaymentService {
// ...
}

@Service
public class WechatPayService implements PaymentService {
// ...
}

// 注入时不指定名称,自动选择 @Primary 标记的 Bean
@Service
public class OrderService {
private final PaymentService paymentService; // 注入 AlipayService

public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}

方式四:注入所有实现(List/Map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Service
public class PaymentRouter {
// 注入所有 PaymentService 实现
private final List<PaymentService> paymentServices;

// 或者使用 Map,key 为 Bean 名称
private final Map<String, PaymentService> paymentServiceMap;

public PaymentRouter(List<PaymentService> paymentServices,
Map<String, PaymentService> paymentServiceMap) {
this.paymentServices = paymentServices;
this.paymentServiceMap = paymentServiceMap;
}

public void pay(String payType, Order order) {
// 方式一:遍历 List
for (PaymentService service : paymentServices) {
if (service.supports(payType)) {
service.pay(order);
return;
}
}

// 方式二:从 Map 获取
PaymentService service = paymentServiceMap.get(payType + "PayService");
if (service != null) {
service.pay(order);
}
}
}

抽象类与继承体系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 抽象基类
public abstract class BaseService<T, ID> {
protected abstract BaseRepository<T, ID> getRepository();

public Optional<T> findById(ID id) {
return getRepository().findById(id);
}

public T save(T entity) {
return getRepository().save(entity);
}
}

// 具体实现
@Service
public class UserService extends BaseService<User, Long> {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
protected BaseRepository<User, Long> getRepository() {
return userRepository;
}
}

@Service
public class OrderService extends BaseService<Order, Long> {
private final OrderRepository orderRepository;

public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}

@Override
protected BaseRepository<Order, Long> getRepository() {
return orderRepository;
}
}

注入基类类型:

1
2
3
4
5
6
7
8
9
@Service
public class CompositeService {
// 注入所有 BaseService 实现
private final List<BaseService<?, ?>> services;

public CompositeService(List<BaseService<?, ?>> services) {
this.services = services;
}
}

泛型接口注入

Spring 4.0+ 支持泛型类型作为限定符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 泛型接口
public interface Repository<T> {
Optional<T> findById(Long id);
T save(T entity);
}

// 具体实现
@Repository
public class UserRepository implements Repository<User> {
// ...
}

@Repository
public class OrderRepository implements Repository<Order> {
// ...
}

// 注入时按泛型类型区分
@Service
public class UserService {
// Spring 自动匹配 Repository<User> 的实现
private final Repository<User> userRepository;

public UserService(Repository<User> userRepository) {
this.userRepository = userRepository;
}
}

@Service
public class OrderService {
// Spring 自动匹配 Repository<Order> 的实现
private final Repository<Order> orderRepository;

public OrderService(Repository<Order> orderRepository) {
this.orderRepository = orderRepository;
}
}

条件化选择实现

结合 @Conditional 实现运行时选择不同实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 配置类
@Configuration
public class PaymentConfig {
@Bean
@Primary
@ConditionalOnProperty(name = "payment.provider", havingValue = "alipay", matchIfMissing = true)
public PaymentService alipayService() {
return new AlipayService();
}

@Bean
@ConditionalOnProperty(name = "payment.provider", havingValue = "wechat")
public PaymentService wechatPayService() {
return new WechatPayService();
}

@Bean
@ConditionalOnProperty(name = "payment.provider", havingValue = "unionpay")
public PaymentService unionPayService() {
return new UnionPayService();
}
}

接口注入决策表

场景推荐方式示例
单实现直接注入接口类型PaymentService service
多实现,有默认@Primary + 接口类型@Primary class AlipayService
多实现,需指定@Qualifier 或字段名匹配@Qualifier("alipayService")
需要所有实现List<Interface>Map<String, Interface>List<PaymentService>
泛型接口按泛型类型注入Repository<User>
运行时选择@Conditional + 配置类@ConditionalOnProperty

Bean 作用域

Spring 支持多种 Bean 作用域,默认为单例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// ========== 单例(默认)==========
// 整个应用只有一个实例
// 适合:无状态服务、工具类、配置类
@Service
@Scope("singleton") // 可省略,默认就是单例
public class UserService {
// 无状态,线程安全
}

// ========== 原型(Prototype)==========
// 每次获取都创建新实例
// 适合:有状态对象、需要隔离的场景
@Component
@Scope("prototype")
public class RequestContext {
private String requestId;
private Map<String, Object> attributes = new HashMap<>();
}

// ========== Web 作用域 ==========
// Request:每个 HTTP 请求一个实例
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
private String requestId;
}

// Session:每个 HTTP 会话一个实例
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
private User currentUser;
}

// Application:ServletContext 生命周期
@Component
@Scope(value = WebApplicationContext.SCOPE_APPLICATION)
public class ApplicationScopedBean {
// 效果类似单例,但属于 Web 容器
}

作用域选择原则:

1
2
3
4
5
6
7
8
9
10
┌─────────────────────────────────────────────────────────────┐
Bean 是否有状态? │
│ ├─ 无状态 → singleton(默认) │
│ └─ 有状态 │
│ ├─ 状态需要跨请求共享? │
│ │ ├─ 是 → session │
│ │ └─ 否 │
│ │ ├─ 单请求内有效?→ request │
│ │ └─ 每次使用都需要新实例?→ prototype
└─────────────────────────────────────────────────────────────┘

Bean 生命周期回调

正确使用生命周期回调,可以在合适的时机执行初始化和清理逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// ========== 方式一:JSR-250 注解(推荐)==========
@Component
public class DatabaseConnectionPool {
@PostConstruct
public void init() {
// 初始化连接池
System.out.println("连接池初始化完成");
}

@PreDestroy
public void destroy() {
// 关闭连接池
System.out.println("连接池已关闭");
}
}

// ========== 方式二:Spring 接口 ==========
@Component
public class CacheManager implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 初始化缓存
}

@Override
public void destroy() throws Exception {
// 清理缓存
}
}

// ========== 方式三:@Bean 属性 ==========
@Configuration
public class AppConfig {
@Bean(initMethod = "start", destroyMethod = "stop")
public CustomService customService() {
return new CustomService();
}
}

生命周期回调执行顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Bean 实例化

@PostConstruct

InitializingBean.afterPropertiesSet()

@Bean(initMethod)

[Bean 就绪,可正常使用]

@PreDestroy

DisposableBean.destroy()

@Bean(destroyMethod)

Bean 销毁

最佳实践:优先使用 @PostConstruct@PreDestroy

条件化 Bean 加载

Spring Boot 提供了丰富的条件注解,实现按需加载 Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// ========== 类路径条件 ==========
@Configuration
@ConditionalOnClass(DataSource.class) // 类路径存在 DataSource 才加载
public class DataSourceAutoConfiguration {
// ...
}

// ========== Bean 存在条件 ==========
@Configuration
@ConditionalOnBean(DataSource.class) // 容器中有 DataSource Bean
@ConditionalOnMissingBean(JdbcTemplate.class) // 容器中没有 JdbcTemplate
public class JdbcTemplateConfiguration {
// ...
}

// ========== 表达式条件 ==========
@Configuration
@ConditionalOnExpression("${app.feature.enabled} and ${app.feature.premium:false}")
public class PremiumFeatureConfiguration {
// ...
}

// ========== 资源条件 ==========
@Configuration
@ConditionalOnResource(resources = "classpath:custom-config.properties")
public class CustomConfiguration {
// ...
}

// ========== Web 应用条件 ==========
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class WebMvcConfiguration {
// ...
}

@ConditionalOnProperty 详解

@ConditionalOnProperty 是最常用的条件注解,根据配置属性决定是否加载 Bean。

基本参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {

/**
* 属性名前缀,与 name 拼接成完整属性名
* 例如:prefix = "app.cache", name = "enabled" → app.cache.enabled
*/
String prefix() default "";

/**
* 属性名,支持数组形式(多个属性时,都满足才匹配)
*/
String[] name();

/**
* 期望的属性值,属性值等于此值才匹配
* 如果不指定,则只要属性存在且不为 false 就匹配
*/
String havingValue() default "";

/**
* 属性不存在时是否匹配
* true: 属性不存在时也加载
* false: 属性不存在时不加载(默认)
*/
boolean matchIfMissing() default false;
}

常见用法

场景一:开关控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// application.yml
app:
cache:
enabled: true

// 配置类
@Configuration
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
public class CacheConfiguration {
// 只有 app.cache.enabled=true 时才加载
}

// 等价写法(不指定 havingValue,只要属性存在且不为 false)
@Configuration
@ConditionalOnProperty(prefix = "app.cache", name = "enabled")
public class CacheConfiguration {
// app.cache.enabled=true 或 app.cache.enabled=1 都会加载
// app.cache.enabled=false 不会加载
}

场景二:默认启用

1
2
3
4
5
6
7
8
9
10
11
12
// matchIfMissing = true 表示属性不存在时也加载
@Configuration
@ConditionalOnProperty(
prefix = "app.feature",
name = "enabled",
havingValue = "true",
matchIfMissing = true // 属性不存在时默认加载
)
public class FeatureConfiguration {
// 属性不存在 或 app.feature.enabled=true 时加载
// app.feature.enabled=false 时不加载
}

场景三:多值选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// application.yml
app:
database:
type: mysql // 可选:mysql, postgresql, oracle

// MySQL 配置
@Configuration
@ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "mysql")
public class MySqlConfiguration {
// app.database.type=mysql 时加载
}

// PostgreSQL 配置
@Configuration
@ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "postgresql")
public class PostgreSqlConfiguration {
// app.database.type=postgresql 时加载
}

// Oracle 配置
@Configuration
@ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "oracle")
public class OracleConfiguration {
// app.database.type=oracle 时加载
}

场景四:多属性条件

1
2
3
4
5
6
7
8
9
10
// name 支持数组,所有属性都满足才匹配
@Configuration
@ConditionalOnProperty(
prefix = "app.security",
name = {"enabled", "jwt.enabled"}, // 两个属性都要满足
havingValue = "true"
)
public class JwtSecurityConfiguration {
// app.security.enabled=true 且 app.security.jwt.enabled=true 时加载
}

场景五:方法级别条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class DataSourceConfiguration {

@Bean
@ConditionalOnProperty(prefix = "app.datasource", name = "type", havingValue = "hikari")
public DataSource hikariDataSource() {
return new HikariDataSource();
}

@Bean
@ConditionalOnProperty(prefix = "app.datasource", name = "type", havingValue = "druid", matchIfMissing = true)
public DataSource druidDataSource() {
return new DruidDataSource();
}
}

匹配规则详解

@ConditionalOnProperty 匹配规则:

属性状态havingValue=“”havingValue=“true”matchIfMissing
属性存在 = true✅ 匹配✅ 匹配-
属性存在 = false❌ 不匹配❌ 不匹配-
属性存在 = “abc”✅ 匹配❌ 不匹配-
属性存在 = “true”✅ 匹配✅ 匹配-
属性不存在❌ 不匹配❌ 不匹配✅ 匹配

关键点:

  1. 不指定 havingValue:只要属性存在且值不为 false 就匹配
  2. 指定 havingValue:属性值必须等于 havingValue 才匹配
  3. matchIfMissing=true:属性不存在时也匹配(常用于默认启用场景)
  4. 属性值忽略大小写havingValue = "true" 匹配 TRUETruetrue

实战示例

示例一:多环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// application-dev.yml
app:
env: dev

// application-prod.yml
app:
env: prod

// 开发环境配置
@Configuration
@ConditionalOnProperty(name = "app.env", havingValue = "dev")
public class DevConfiguration {
@Bean
public DataSource devDataSource() {
// 开发环境使用 H2 内存数据库
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}

// 生产环境配置
@Configuration
@ConditionalOnProperty(name = "app.env", havingValue = "prod")
public class ProdConfiguration {
@Bean
public DataSource prodDataSource() {
// 生产环境使用真实数据库
return new HikariDataSource();
}
}

示例二:功能开关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// application.yml
app:
features:
async-enabled: true
cache-enabled: false
metrics-enabled: true

// 异步功能
@Configuration
@ConditionalOnProperty(prefix = "app.features", name = "async-enabled", havingValue = "true")
@EnableAsync
public class AsyncConfiguration {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
return executor;
}
}

// 缓存功能
@Configuration
@ConditionalOnProperty(prefix = "app.features", name = "cache-enabled", havingValue = "true")
@EnableCaching
public class CacheConfiguration {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}

// 监控功能(默认启用)
@Configuration
@ConditionalOnProperty(
prefix = "app.features",
name = "metrics-enabled",
havingValue = "true",
matchIfMissing = true
)
public class MetricsConfiguration {
@Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
}

示例三:条件组合

1
2
3
4
5
6
7
8
9
10
11
12
// 多条件组合:属性条件 + Bean 条件
@Configuration
@ConditionalOnProperty(prefix = "app.kafka", name = "enabled", havingValue = "true")
@ConditionalOnClass(KafkaTemplate.class) // 类路径存在 Kafka 依赖
public class KafkaConfiguration {

@Bean
@ConditionalOnMissingBean // 没有自定义 KafkaTemplate 时才创建
public KafkaTemplate<String, String> kafkaTemplate(ProducerFactory<String, String> factory) {
return new KafkaTemplate<>(factory);
}
}

自定义条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. 实现 Condition 接口
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getProperty("os.name").toLowerCase().contains("linux");
}
}

// 2. 创建条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnLinuxCondition.class)
public @interface ConditionalOnLinux {
}

// 3. 使用自定义条件
@Configuration
@ConditionalOnLinux
public class LinuxSpecificConfiguration {
// 仅在 Linux 系统加载
}

循环依赖问题

循环依赖是 Spring 开发中常见的问题,需要正确理解和处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// ========== 问题场景 ==========
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB; // A 依赖 B
}

@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // B 依赖 A → 循环依赖!
}

// ========== 解决方案一:重构设计(推荐)==========
// 提取公共逻辑到第三个 Service
@Service
public class CommonService {
// 公共逻辑
}

@Service
public class ServiceA {
@Autowired
private CommonService commonService;
}

@Service
public class ServiceB {
@Autowired
private CommonService commonService;
}

// ========== 解决方案二:使用 @Lazy ==========
@Service
public class ServiceA {
private final ServiceB serviceB;

public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB; // 注入代理,延迟初始化
}
}

// ========== 解决方案三:Setter 注入 ==========
@Service
public class ServiceA {
private ServiceB serviceB;

@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}

// ========== 解决方案四:使用 ApplicationContext ==========
@Service
public class ServiceA implements ApplicationContextAware {
private ServiceB serviceB;

@Override
public void setApplicationContext(ApplicationContext ctx) {
this.serviceB = ctx.getBean(ServiceB.class);
}
}

循环依赖处理机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Spring 三级缓存解决循环依赖:

singletonObjects(一级缓存) → 完整的单例 Bean
earlySingletonObjects(二级缓存) → 早期暴露的 Bean(未完成属性注入)
singletonFactories(三级缓存) → Bean 工厂,用于生成早期引用

处理流程:
1. A 实例化 → 放入三级缓存
2. A 注入 BB 不存在,创建 B
3. B 实例化 → 放入三级缓存
4. B 注入 A → 从三级缓存获取 A 的早期引用
5. B 完成初始化 → 放入一级缓存
6. A 获取 B → 从一级缓存获取 B
7. A 完成初始化 → 放入一级缓存

注意:构造器注入无法利用三级缓存解决循环依赖!

Bean 最佳实践总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// ========== 推荐的 Bean 定义模板 ==========

// 1. 业务服务类
@Service
@Slf4j // Lombok 日志
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final EmailService emailService;

// 构造器注入,final 字段保证不可变
public UserServiceImpl(UserRepository userRepository,
@Lazy EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}

@PostConstruct
public void init() {
log.info("UserService 初始化完成");
}

@Transactional(readOnly = true)
@Override
public Optional<UserDTO> findById(Long id) {
return userRepository.findById(id)
.map(this::toDTO);
}

@PreDestroy
public void cleanup() {
log.info("UserService 清理资源");
}
}

// 2. 配置类
@Configuration
@EnableConfigurationProperties(AppProperties.class)
@ConditionalOnProperty(prefix = "app.feature", name = "enabled")
public class FeatureConfiguration {
@Bean
@ConditionalOnMissingBean
public FeatureService featureService(AppProperties properties) {
return new FeatureService(properties);
}
}

// 3. 配置属性类
@Data
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private boolean featureEnabled = false;
private Duration timeout = Duration.ofSeconds(30);
}

核心原则:

  1. 单一职责:每个 Bean 只做一件事
  2. 构造器注入:强制依赖用构造器,可选依赖用 Setter
  3. 不可变优先:使用 final 字段,避免状态变更
  4. 避免循环依赖:通过重构设计解决,而非技术手段绕过
  5. 合理使用作用域:无状态用单例,有状态用原型/Request/Session
  6. 生命周期回调:初始化用 @PostConstruct,清理用 @PreDestroy
  7. 条件化加载:使用 @ConditionalXxx 实现按需加载

相关阅读