IoC / DI,特别是Spring:无设置器/构造器的自动装配属性

时间:2018-06-24 20:04:41

标签: java spring dependency-injection inversion-of-control

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

想象以下代码:

@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到底如何处理它?是否为该属性创建了带有添加的构造函数的代理类?

2 个答案:

答案 0 :(得分:2)

这是否是一个不好的习惯取决于您编写此代码的时代。在EJB时代,这是一种最佳实践,该容器为您提供了生命周期的所有功能,即使在Spring中,即使在某些时候,即使在Spring中这是相当严格的模型,它也是一个很好的解决方案,java config或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是像带注释的类一样的两个bean,Spring像带注释的bean一样管理java config bean。区别在于,现在我们可以受益于显式的组合,并且可以提供更灵活的配置方案。我们可以再次受益于Spring的神奇aop,因为就像之前说过的那样,在Java config中配置的Spring bean与带注释的bean完全等效。

我建议您像示例一样使用java config。

我希望它能为您提供帮助

更新: 回复: 此外,Spring到底如何处理它?是否为该属性创建了带有添加的构造函数的代理类?

我可以说:使用组件扫描功能,例如@ComponentScan,spring可以找到所有带有构造型注释(例如@ Component,@ Service,@ Repository等)的bean。之所以有用,是因为它会触发某些功能,例如,如果我们实现JPA存储库并注册一个PersistanceExceptionTraslatorPostProcessor,则它会@@ Repository转换为DataAccessException层次结构的Exception中的SQL本机异常,但是其他注释只是使用注释@Component注册spring bean的一种方式, @Service是示例。

通过相关的Spring创建bean并注入用@Autowired注释的字段,如果直接在反射设置的字段上使用@Autowired,则直接使用该字段,否则使用setter,例如在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并提供依赖项。无论如何,最佳实践肯定是我回答中的第一段代码。在依赖关系中是明确的,在业务代码库中是clean和Spring Free

答案 1 :(得分:0)

如果您不喜欢自动接线(我也可以)。您可以使用构造函数依赖注入。

您不应该将depencendy用于接口的类byt,并且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();
    }
}