因此,由于我一直在使用Spring,如果我要编写一个有依赖关系的服务,我会这样做:

@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}

我现在遇到了使用另一种约定来实现相同目标的代码

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

我知道这两种方法都有效。但是选择B有什么好处吗?对我来说,它在类和单元测试中创建了更多的代码。(必须写构造函数,不能使用@InjectMocks)

我遗漏了什么吗?除了将代码添加到单元测试之外,自动连接构造函数还有其他功能吗?这是一种更可取的依赖注入方式吗?


当前回答

很高兴知道

如果只有一个构造函数调用,则不需要包含@Autowired注释。然后你可以使用这样的语句:

@RestController
public class NiceController {

    private final DataRepository repository;

    public NiceController(ChapterRepository repository) {
        this.repository = repository;
    }
}

... Spring数据存储库注入的例子。

其他回答

我希望我不会因为表达我的观点而被降级,但对我来说,选项A更好地反映了Spring依赖注入的强大功能,而在选项B中,您将类与依赖项耦合在一起,实际上,如果不从构造函数传递依赖项,就无法实例化对象。依赖注入通过实现控制反转来避免这种情况,所以对我来说选项B没有任何意义。

我更喜欢构造注入,只是因为我可以将我的依赖标记为final,这在使用属性注入属性时是不可能的。

你的依赖关系应该是最终的,即不被程序修改。

请注意,自从Spring 4.3以来,你甚至不需要在你的构造函数上使用@Autowired,所以你可以用Java风格编写代码,而不是绑定到Spring的注释上。 你的代码片段看起来像这样:

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

事实上,根据我的经验,第二种选择更好。不需要@Autowired。事实上,更明智的做法是创建与框架耦合不太紧密的代码(就像Spring一样)。您希望代码尽可能采用延迟决策方法。这是尽可能多的pojo,以至于框架可以很容易地交换出来。 所以我建议你创建一个单独的Config文件,并在那里定义你的bean,如下所示:

在somesservice .java文件中:

public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

在ServiceConfig.java文件中:

@Config
public class ServiceConfig {
    @Bean
    public SomeService someService(SomeOtherService someOtherService){
        return new SomeService(someOtherService);
    }
}

事实上,如果你想深入了解它的技术,使用字段注入(@Autowired)会产生线程安全问题(以及其他问题),这显然取决于项目的大小。检查这来了解更多的优点和缺点的自动装配。实际上,关键的人建议你使用构造函数注入而不是字段注入

Autowired构造函数提供了一个钩子,可以在将自定义代码注册到spring容器之前添加它。假设SomeService类扩展了另一个名为SuperSomeService的类,并且它有一个以名称作为参数的构造函数。在这种情况下,Autowired构造函数工作得很好。同样,如果有其他成员要初始化,可以在将实例返回给spring容器之前在构造函数中进行初始化。

public class SuperSomeService {
     private String name;
     public SuperSomeService(String name) {
         this.name = name;
     }
}

@Component
public class SomeService extends SuperSomeService {
    private final SomeOtherService someOtherService;
    private Map<String, String> props = null;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        SuperSomeService("SomeService")
        this.someOtherService = someOtherService;
        props = loadMap();
    }
}