一、核心注解深度解析
1.1 构造器注入(推荐方式)
核心注解:@Autowired
(可省略)、@RequiredArgsConstructor
(Lombok)
适用场景:强制依赖、不可变对象
最佳实践:
- 与
final
字段配合使用,确保依赖不可变 - Spring 4.3+ 单构造函数可省略
@Autowired
代码示例:
@Service
@RequiredArgsConstructor
public class OrderService {
private final PaymentService paymentService; // Lombok自动生成构造器
private final List<InventoryService> inventoryServices;
// 多依赖场景
public OrderService(PaymentService paymentService,
@Qualifier("mainInventory") InventoryService mainInventory,
@Qualifier("backupInventory") InventoryService backupInventory) {
this.paymentService = paymentService;
this.inventoryServices = List.of(mainInventory, backupInventory);
}
}
常见问题:
-
循环依赖问题:
- 现象:A依赖B,B又依赖A导致启动失败
- 解决方案:
或使用Setter注入替代@Service public class AService { private final @Lazy BService bService; // 延迟加载 }
-
构造函数参数过多:
- 建议:拆分服务职责或使用Builder模式
1.2 字段注入(不推荐)
核心注解:@Autowired
特点:
- 直接在字段上使用注解
- 简单但存在严重缺陷
- 强烈不推荐在生产环境使用
代码示例:
@Service
public class ProductService {
@Autowired // 反模式!
private ProductRepository productRepository;
}
主要问题:
- 隐藏依赖关系,难以通过构造函数发现
- 无法进行单元测试(必须使用反射注入)
- 违反单一职责原则
- 可能导致NPE(如果依赖未正确初始化)
1.3 Setter注入
核心注解:@Autowired
特点:
- 通过setter方法注入
- 适用于可选依赖
- 线程安全性较差
代码示例:
@Service
public class CacheService {
private CacheManager cacheManager;
@Autowired(required = false) // 可选依赖
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
}
适用场景:
- 依赖是可选的
- 需要在运行时重新配置依赖
- 遗留代码维护
1.4 @Resource(Java标准注解)
核心注解:@Resource
(JSR-250)
特点:
- 默认按名称注入,其次按类型
- 支持显式指定Bean名称
- 是Java标准注解(非Spring特有)
代码示例:
@Service
public class PaymentService {
@Resource(name = "primaryDataSource") // 明确指定Bean名称
private DataSource dataSource;
@Resource // 按类型注入(若有多个同类型Bean则报错)
private NotificationService notificationService;
}
与@Autowired对比:
特性 | @Resource | @Autowired |
---|---|---|
来源 | Java标准(JSR-250) | Spring特有 |
注入顺序 | 先按名称,后按类型 | 只按类型 |
是否必须 | 默认required=true | 默认required=true |
适用场景 | 需要明确Bean名称时 | 纯Spring环境首选 |
1.5 @Value(配置注入)
核心注解:@Value
特点:
- 注入配置文件中的值
- 支持SpEL表达式
- 可设置默认值
代码示例:
@Service
public class AppConfig {
@Value("${app.name}") // 注入简单配置
private String appName;
@Value("${app.timeout:30}") // 带默认值
private int timeout;
@Value("#{'${app.servers}'.split(',')}")
private List<String> servers; // 注入列表
@Value("#{systemProperties['user.dir']}") // 系统属性
private String userDir;
}
常见问题:
-
配置不存在时:
- 解决方案:总是设置默认值(如
${property:default}
)
- 解决方案:总是设置默认值(如
-
类型转换失败:
- 解决方案:确保配置值与字段类型匹配
1.6 @ConfigurationProperties(批量配置)
核心注解:@ConfigurationProperties
特点:
- 批量绑定配置属性
- 支持复杂类型(List、Map等)
- 类型安全
代码示例:
@ConfigurationProperties(prefix = "database")
@Component
public class DatabaseConfig {
private String url;
private String username;
private String password;
private List<String> replicas;
// 必须提供getter/setter
// getters and setters...
}
启用方式:
@SpringBootApplication
@EnableConfigurationProperties(DatabaseConfig.class)
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
二、同类型注解深度对比
2.1 依赖注入方式对比矩阵
注入方式 | 注解 | 优点 | 缺点 | 推荐指数 |
---|---|---|---|---|
构造器注入 | @Autowired | 不可变、线程安全、易测试 | 构造函数参数过多时繁琐 | ★★★★★ |
@RequiredArgsConstructor | Lombok简化代码 | 需要Lombok支持 | ★★★★☆ | |
Setter注入 | @Autowired | 灵活、支持可选依赖 | 线程不安全、破坏封装性 | ★★☆☆☆ |
字段注入 | @Autowired | 代码简洁 | 隐藏依赖、难测试、不推荐 | ★☆☆☆☆ |
2.2 配置注入方式对比
注入方式 | 注解 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|
单个属性注入 | @Value | 简单键值对 | 零配置,即时生效 | 不支持复杂类型 |
批量配置绑定 | @ConfigurationProperties | 复杂配置结构 | 类型安全、支持嵌套结构 | 需要额外配置启用 |
环境变量注入 | @Value("${VAR_NAME}") | 容器化部署 | 安全敏感信息管理 | 需要环境变量支持 |
三、高级特性与解决方案
3.1 解决常见问题的进阶方案
问题1:多实现类冲突
// 场景:PaymentService有多个实现
@Service
public class OrderService {
// 错误:存在多个PaymentService实现
// @Autowired
// private PaymentService paymentService;
// 正确方案1:使用@Qualifier
@Autowired
@Qualifier("alipayService")
private PaymentService paymentService;
// 正确方案2:使用@Primary标记首选Bean
@Autowired
private PaymentService primaryPaymentService;
}
// 标记首选Bean
@Service
@Primary
public class WechatPayService implements PaymentService {
// 实现...
}
// 指定名称的Bean
@Service("alipayService")
public class AlipayService implements PaymentService {
// 实现...
}
问题2:条件化Bean加载
@Configuration
public class FeatureConfig {
// 仅当配置属性feature.enabled=true时加载
@Bean
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public FeatureService featureService() {
return new FeatureService();
}
// 仅当类路径存在特定类时加载
@Bean
@ConditionalOnClass(name = "com.example.SpecialService")
public SpecialAdapter specialAdapter() {
return new SpecialAdapter();
}
}
问题3:配置热更新
@RestController
@RefreshScope // Spring Cloud特性
public class ConfigController {
@Value("${dynamic.config}")
private String dynamicConfig;
@GetMapping("/config")
public String getConfig() {
return dynamicConfig; // 修改配置后自动刷新
}
}
// 配置类也需要@RefreshScope
@ConfigurationProperties(prefix = "app")
@RefreshScope
@Component
public class AppProperties {
private String name;
// getters/setters...
}
四、实战案例解析
4.1 多数据源配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public DataSource routingDataSource(
@Qualifier("masterDataSource") DataSource master,
@Qualifier("slaveDataSource") DataSource slave) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbContextHolder.MASTER, master);
targetDataSources.put(DbContextHolder.SLAVE, slave);
AbstractRoutingDataSource routing = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
};
routing.setDefaultTargetDataSource(master);
routing.setTargetDataSources(targetDataSources);
return routing;
}
}
4.2 配置中心集成(Nacos)
@Configuration
@EnableConfigurationProperties(AppConfig.class)
public class NacosConfig {
@Bean
public ConfigService nacosConfigService() throws NacosException {
Properties props = new Properties();
props.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
return ConfigFactory.createConfigService(props);
}
@Bean
@RefreshScope
public DynamicConfig dynamicConfig(ConfigService configService, AppConfig appConfig) {
return new DynamicConfig() {
@Override
public String getConfig(String dataId, String group, long timeoutMs) {
return configService.getConfig(dataId, group, timeoutMs);
}
};
}
}
五、常见问题深度排查
5.1 循环依赖解决方案
现象:BeanCurrentlyInCreationException
根本原因:A依赖B,B又依赖A
解决方案:
- 重构代码:提取公共组件
- Setter注入:
@Service public class AService { private BService bService; @Autowired public void setBService(BService bService) { this.bService = bService; } }
- @Lazy延迟加载:
@Service public class AService { private final @Lazy BService bService; }
5.2 配置不生效排查流程
- 检查
@ConfigurationProperties
类是否添加@Component
- 验证配置文件前缀是否匹配(如
app.name
需对应app.name
前缀) - 确认配置类未被
@Profile
限制 - 使用
@PropertySource
指定配置文件路径
六、性能优化与最佳实践
6.1 Bean生命周期管理
@PostConstruct
public void init() {
// 初始化逻辑
}
@PreDestroy
public void cleanup() {
// 资源释放
}
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureService {
// 按条件加载Bean
}
6.2 依赖注入性能指标
注入方式 | 内存占用 | 初始化时间 | 线程安全 |
---|---|---|---|
构造器注入 | 低 | 快 | ✔️ |
Setter注入 | 中 | 中 | ❌ |
字段注入 | 低 | 快 | ❌ |
七、扩展学习资源
- Spring官方文档:依赖注入规范
通过本指南,开发者可以掌握Spring Boot依赖注入的核心要点,根据项目实际情况选择最合适的注入策略,构建高质量、易维护的应用程序。建议结合具体业务场景选择最合适的注入方式,并通过单元测试验证依赖关系的正确性。