IoC / DI 尤其是 Spring:没有设置器/构造函数的自动连线属性



我有一个我自己无法回答的问题 - 至少不是很好。

想象一下以下代码:

@Service
public class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceB {
@Autowired
ServiceA serviceA;
public void doService() {
serviceA.doService();
}
}

它有效,但它被认为是不好的做法吗?如果要分离这些类或测试它们,则无法手动设置依赖项。

另外,Spring究竟如何处理它?是否创建了一个代理类,并为该属性添加了构造函数?

这是否是一种不好的做法取决于你编写此代码的时代。在EJB时代,这是一种最佳实践,容器为您提供了生命周期的所有功能,即使在Spring中也很好,即使在Spring中,这是非常严格的模型java配置或xml是一个更灵活的解决方案。

然而,在20xx时代,特别是在TDD和敏捷模型中,我可以说这是一种真正的不良做法。这些 bean 无法从 Spring 容器中测试,即使在编译时,注释也会在 Spring 中耦合你。更好的解决方案可能是如下所示的代码

class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
class ServiceB {
private final ServiceA serviceA;
ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
@Configuration
class ServiceConfig{
@Bean
public ServiceA serviceA(){
return new ServiceA();
}   
@Bean
public ServiceB serviceB(ServiceA serviceA){
return new ServiceB(serviceA);
}
}

通过这种方式,ServiceA 和 ServiceB 类是两个完全没有 Spring 的 bean,特别是对于业务逻辑来说,这是一种最佳实践,bean 是可测试的,因为我们的依赖项是显式的。

想象一下,提供以这种方式编写代码的 ServiceB 类的测试,您可以存根或模拟 serviceA 依赖项,并且可以隔离测试 ServiceB Bean。

对于代理故事,请不要担心它,因为我们提供了一个配置类ServiceA和ServiceB是两个像注释类一样的豆子,Spring像注释的bean一样管理java配置bean。不同之处在于,现在我们可以从显式组合中受益,并且可以提供更灵活的配置方案。我们可以再次受益于 Spring 的神奇 AOP,因为就像之前所说,在 Java 配置中配置的 Spring Bean 与注释 Bean 完全等效。

我建议你使用像示例这样的java配置。

希望对您有所帮助

更新: 回复:另外,Spring究竟如何处理它?是否创建了一个代理类,并为该属性添加了构造函数?

我可以说:使用组件扫描功能,假设@ComponentScan,spring找到所有用sterotype注释(如@Component,@Service,@Repository等(注释的bean,其中一些注释很有用,因为触发某些功能,例如@Repository如果我们实现一个JPA存储库并注册一个PersistanceExceptionTraslatorPostProcessor,该PersistanceExceptionTraslatorPostProcessor在DataAccessException层次结构的异常中翻译SQL本机异常, 但其他注释只是使用注释@Component注册春豆的一种方式,@Service示例。

弹簧通过关系创建 bean 并注入用 @Autowired 注释的字段,如果您通过反射直接设置在字段上使用@Autowired,则字段否则使用二传手,例如在 xml 中需要 setter 或构造函数。

如果有两个构造函数,它是透明的,Spring 将使用空构造函数,然后通过反射提供 @Autowired 属性,您甚至可以执行以下操作:

@Service
class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
class ServiceB {
private ServiceA serviceA;
public ServiceB() {
}
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}

在这种情况下,spring 认识到它已将带注释的构造函数与 @Autowired 一起使用,以便创建 bean 并提供依赖项。无论如何,最佳实践绝对是我答案中的第一个代码片段。它在依赖项中是明确的,在您的业务代码库中是干净的和无Spring

如果你不喜欢自动连线(我也是(。可以使用构造函数依赖项注入。

您不应该对接口的类byt使用deencendy,并且spring将服务器正确实现(解耦(

public interface ServiceA {
public void doService();
}
@Service
public class ServiceAImpl implement ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceBImpl implements ServiceB {
private final ServiceA serviceA;
public ServiceBImpl(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}

最新更新