理解 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 @Service @Repository @Controller @RestController @Configuration @Service public class UserServiceImpl implements UserService { }@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({RedisConfig.class, MongoConfig.class}) @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }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; public OrderService (UserService userService, OrderRepository orderRepository) { this .userService = userService; this .orderRepository = orderRepository; } }@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 @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; public OrderService (@Qualifier("alipayService") PaymentService paymentService) { this .paymentService = paymentService; } }
方式二:字段名匹配
1 2 3 4 5 6 7 8 9 @Service public class OrderService { 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 { }@Service public class OrderService { private final PaymentService paymentService; 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 { private final List<PaymentService> paymentServices; 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) { for (PaymentService service : paymentServices) { if (service.supports(payType)) { service.pay(order); return ; } } 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 { 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 { private final Repository<User> userRepository; public UserService (Repository<User> userRepository) { this .userRepository = userRepository; } }@Service public class OrderService { 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 { }@Component @Scope("prototype") public class RequestContext { private String requestId; private Map<String, Object> attributes = new HashMap <>(); }@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestScopedBean { private String requestId; }@Component @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public class SessionScopedBean { private User currentUser; }@Component @Scope(value = WebApplicationContext.SCOPE_APPLICATION) public class ApplicationScopedBean { }
作用域选择原则:
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 @Component public class DatabaseConnectionPool { @PostConstruct public void init () { System.out.println("连接池初始化完成" ); } @PreDestroy public void destroy () { System.out.println("连接池已关闭" ); } }@Component public class CacheManager implements InitializingBean , DisposableBean { @Override public void afterPropertiesSet () throws Exception { } @Override public void destroy () throws Exception { } }@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) public class DataSourceAutoConfiguration { }@Configuration @ConditionalOnBean(DataSource.class) @ConditionalOnMissingBean(JdbcTemplate.class) 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 { }@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 { String prefix () default "" ; String[] name(); String havingValue () default "" ; boolean matchIfMissing () default false ; }
常见用法 场景一:开关控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 app: cache: enabled: true @Configuration @ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true") public class CacheConfiguration { }@Configuration @ConditionalOnProperty(prefix = "app.cache", name = "enabled") public class CacheConfiguration { }
场景二:默认启用
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration @ConditionalOnProperty( prefix = "app.feature", name = "enabled", havingValue = "true", matchIfMissing = true // 属性不存在时默认加载 ) public class FeatureConfiguration { }
场景三:多值选择
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 app: database: type: mysql @Configuration @ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "mysql") public class MySqlConfiguration { }@Configuration @ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "postgresql") public class PostgreSqlConfiguration { }@Configuration @ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "oracle") public class OracleConfiguration { }
场景四:多属性条件
1 2 3 4 5 6 7 8 9 10 @Configuration @ConditionalOnProperty( prefix = "app.security", name = {"enabled", "jwt.enabled"}, // 两个属性都要满足 havingValue = "true" ) public class JwtSecurityConfiguration { }
场景五:方法级别条件
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” ✅ 匹配 ✅ 匹配 - 属性不存在 ❌ 不匹配 ❌ 不匹配 ✅ 匹配
关键点:
不指定 havingValue :只要属性存在且值不为 false 就匹配指定 havingValue :属性值必须等于 havingValue 才匹配matchIfMissing=true :属性不存在时也匹配(常用于默认启用场景)属性值忽略大小写 :havingValue = "true" 匹配 TRUE、True、true实战示例 示例一:多环境配置
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 app: env: dev app: env: prod@Configuration @ConditionalOnProperty(name = "app.env", havingValue = "dev") public class DevConfiguration { @Bean public DataSource devDataSource () { 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 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 @Configuration @ConditionalOnProperty(prefix = "app.kafka", name = "enabled", havingValue = "true") @ConditionalOnClass(KafkaTemplate.class) public class KafkaConfiguration { @Bean @ConditionalOnMissingBean 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 public class OnLinuxCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { return System.getProperty("os.name" ).toLowerCase().contains("linux" ); } }@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnLinuxCondition.class) public @interface ConditionalOnLinux { }@Configuration @ConditionalOnLinux public class LinuxSpecificConfiguration { }
循环依赖问题 循环依赖是 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; }@Service public class ServiceB { @Autowired private ServiceA serviceA; }@Service public class CommonService { }@Service public class ServiceA { @Autowired private CommonService commonService; }@Service public class ServiceB { @Autowired private CommonService commonService; }@Service public class ServiceA { private final ServiceB serviceB; public ServiceA (@Lazy ServiceB serviceB) { this .serviceB = serviceB; } }@Service public class ServiceA { private ServiceB serviceB; @Autowired public void setServiceB (ServiceB serviceB) { this .serviceB = serviceB; } }@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 注入 B → B 不存在,创建 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 @Service @Slf4j public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final EmailService emailService; 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 清理资源" ); } }@Configuration @EnableConfigurationProperties(AppProperties.class) @ConditionalOnProperty(prefix = "app.feature", name = "enabled") public class FeatureConfiguration { @Bean @ConditionalOnMissingBean public FeatureService featureService (AppProperties properties) { return new FeatureService (properties); } }@Data @ConfigurationProperties(prefix = "app") public class AppProperties { private String name; private boolean featureEnabled = false ; private Duration timeout = Duration.ofSeconds(30 ); }
核心原则:
单一职责 :每个 Bean 只做一件事构造器注入 :强制依赖用构造器,可选依赖用 Setter不可变优先 :使用 final 字段,避免状态变更避免循环依赖 :通过重构设计解决,而非技术手段绕过合理使用作用域 :无状态用单例,有状态用原型/Request/Session生命周期回调 :初始化用 @PostConstruct,清理用 @PreDestroy条件化加载 :使用 @ConditionalXxx 实现按需加载相关阅读