Spring6依赖注入新特性详解:从@Fallback到最佳实践
Spring6依赖注入新特性详解:从@Fallback到最佳实践
随着Spring框架的不断演进,Spring6带来了许多令人兴奋的新特性和改进,特别是在依赖注入方面。本文将重点介绍Spring6中依赖注入的新特性、改进以及最佳实践,帮助开发者更好地理解和应用这些新功能。
新特性:@Fallback注解
在Spring6中,新增了一个非常实用的注解——@Fallback。这个注解可以看作是@Primary注解的补充,用于处理依赖注入时的备选Bean选择问题。
@Fallback vs @Primary
在Spring中,当根据类型找到多个Bean时,通常会使用@Primary注解来指定优先使用的Bean。但是,有时候我们希望在没有其他选择时才使用某个Bean,这时@Fallback就派上用场了。
- @Primary:表示主Bean,当有多个同类型Bean时优先使用。如果有多个@Primary注解的Bean,会导致错误。
- @Fallback:表示备选Bean,只有在没有其他Bean可用时才会被选择。可以有多个@Fallback注解的Bean。
使用示例
@Bean
@Primary
public OrderService primaryOrderService() {
return new OrderService();
}
@Bean
@Fallback
public OrderService fallbackOrderService() {
return new OrderService();
}
在这个例子中,如果容器中存在其他OrderService的实现,那么primaryOrderService将被优先使用。只有当没有其他选择时,fallbackOrderService才会被注入。
源码解析
@Primary和@Fallback的实现都在determinePrimaryCandidate
方法中:
protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
String primaryBeanName = null;
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
if (isPrimary(candidateBeanName, beanInstance)) {
if (primaryBeanName != null) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
"more than one 'primary' bean found");
}
primaryBeanName = candidateBeanName;
}
}
if (primaryBeanName == null) {
for (String candidateBeanName : candidates.keySet()) {
if (!isFallback(candidateBeanName, candidates.get(candidateBeanName))) {
return candidateBeanName;
}
}
}
return primaryBeanName;
}
这个方法首先查找@Primary注解的Bean,如果没有找到,则选择没有@Fallback注解的Bean。
其他改进
除了@Fallback注解,Spring6在依赖注入方面还有以下重要改进:
JDK 17和Jakarta EE 9基线变更:最低支持JDK 17,并需要使用Jakarta EE 9兼容的Web服务器。这涉及到许多API包名的变更,例如从javax.改为jakarta.。
移除过时的Servlet集成:如Commons FileUpload和FreeMarker JSP等。
ListenableFuture弃用:推荐使用Java核心提供的CompletableFuture。
控制器检测变更:Spring MVC和Spring WebFlux的控制器检测机制有所更新。
最佳实践
在Spring6中,构造函数注入仍然是推荐的依赖注入方式,特别是在处理必需的、不可缺失的依赖时。以下是几种依赖注入方式的对比:
构造函数注入
优点:
- 确保对象实例化时就有所有必需的依赖
- 提高代码健壮性,避免空指针异常
- 支持不可变对象设计,增强线程安全性
- 依赖关系清晰可见
示例:
@Service
public class UserService {
private final OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
}
Setter注入
适用场景:
- 可选依赖或易于变更的配置属性
- 对象需要先创建默认状态,再补充注入依赖
示例:
@Service
public class UserService {
private OrderService orderService;
@Autowired
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
字段注入
虽然使用简单,但不推荐:
- 隐藏依赖关系,降低代码可读性
- 不支持final修饰,不利于线程安全
示例:
@Service
public class UserService {
@Autowired
private OrderService orderService;
}
总结
Spring6在依赖注入方面带来了许多令人兴奋的新特性,特别是@Fallback注解的引入,为开发者提供了更灵活的Bean选择机制。同时,构造函数注入仍然是推荐的依赖注入方式,特别是在处理必需的、不可缺失的依赖时。这些改进和最佳实践将帮助开发者更好地管理和优化应用程序中的对象关系,提升代码质量和开发效率。