我决定将我的服务从
Spring
setter-injection
更新为 constructor-injection
,以逐步摆脱循环依赖。问题是服务使用继承,父服务和子服务都有自己的字段。所以问题是 - 使用 Lombok
实现构造函数注入的最佳方法是什么。
这是原始代码的示例:
@Setter(onMethod_ = @Autowired)
public abstract class BaseService {
protected FooService fooService;
}
@Service
@Setter(onMethod_ = @Autowired)
public class ConcreteService extends BaseService {
private BarService barService;
}
FooService
用于 BaseService
和 ConcreteService
。
以下是可能的解决方案:
1. 在
constructor-injection
中实施 ConcreteService
。
@Setter(onMethod_ = @Autowired)
public abstract class BaseService {
protected FooService fooService;
}
@Service
@RequiredArgsConstructor
public class ConcreteService extends BaseService {
private final barService BarService;
}
我们很好地利用了
@RequiredArgsConstructor
注解,因为不需要调用super
(super
不支持调用Lombok
)。即使两个类中都有更多字段,代码看起来也很紧凑。但我们在 constructor-injection
中仍然没有 BaseService
。
2. 将受保护的构造函数添加到
BaseService
并在 super
构造函数中调用 ConcreteService
。
@RequiredArgsConstructor(AccessLevel.PROTECTED)
public abstract class BaseService {
protected final FooService fooService;
}
@Service
public class ConcreteService extends BaseService {
private final BarService barService;
public ConcreteService(FooService fooService, BarService barService) {
super(fooService);
this.barService = barService;
}
}
实现的主要目标 - 实现了
constructor-injection
,BaseService
看起来不错,但是ConcreteService
有一个丑陋的构造函数,其中应该传递两个服务的所有参数(并且可能有很多参数)。我们再试一次吧。
3. 将所有字段移至
ConcreteService
,在getter
中实现受保护的BaseService
以访问FooService
。
public abstract class BaseService {
protected abstract FooService getFooService();
}
@Service
@RequiredArgsConstructor
public class ConcreteService extends BaseService {
private final FooService fooService;
private final BarService barService;
}
主要目标也实现了 - 我们有一个
constructor-injection
,构造函数看起来很干净,但现在我们应该在所有 FooService
继承者中都有一个 BaseService
(当然可以有多个字段)。
所有解决方案都有其优点和缺点,但也许有更好的解决方案?
归根结底,这与其说是一个技术问题,不如说是一个意见问题。这些出色的工具(即 Lombok、Spring 等)是为了让编码变得更容易,所以当有疑问时,我们应该回到基础知识。考虑这样一种情况:您从抽象类继承了一个类,但您没有这些工具。显然,初始化它的正确(也是唯一可能)方法(如果您想让代码保持解耦)是使用继承类中的构造函数参数(也与您的第二个选项非常相似)。
如果看起来很丑而且数量很多,那只能说明设计不太理想,可能是这个领域而不是技术领域出了问题。也许班级应该分成几个?在这种情况下,使用抽象类比使用接口/具体类更好吗?显然,我不知道您的代码必须解决的挑战,但值得考虑设计。
您找到好的解决方案了吗?我对此(相当广泛)询问了 o1-preview,除了使用组件服务作为子类中的字段或返回基于字段的注入(这就是 chatpgt 推荐的)之外,似乎没有什么好的解决方案。