关于依赖注入:Spring @Autowire on Properties vs Constructor

Spring @Autowire on Properties vs Constructor

因此,由于我一直在使用Spring,如果要编写一个具有依赖性的服务,我将执行以下操作:

1
2
3
4
@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}

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

1
2
3
4
5
6
7
8
9
@Component
public class SomeService {
    private final SomeOtherService someOtherService;

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

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

我有什么东西不见了吗?除了向单元测试中添加代码之外,自动布线的构造函数还有其他功能吗?这是进行依赖项注入的一种更可取的方法吗?


是的,方案B(称为构造器注入)实际上是相对于现场注入而推荐的,它有几个优点:

  • 这些依赖关系是明确的。在任何其他情况下(比如在配置类中显式创建bean实例)测试或实例化对象时,都无法忘记一个实例。
  • 依赖关系可以是最终的,这有助于健壮性和线程安全性。
  • 您不需要反射来设置依赖项。InjectMock仍然可用,但不是必需的。您只需自己创建模拟并通过简单地调用构造函数来注入它们。

请参阅这个博客文章,以获取一篇更详细的文章,作者是Spring的一位撰稿人OlivierGierke。


我将用简单的语言向您解释:

在选项(a)中,您允许任何人(在Spring容器外部/内部的不同类中)使用默认的构造函数(如new SomeService())创建实例,这并不像您需要SomeOtherService对象(作为SomeService的依赖项)那样好。

Is there anything else the autowired constructor does besides add code
to the unit tests? Is this a more preferred way to do dependency
injection?

选项(b)是首选方法,因为它不允许在没有实际解决SomeOtherService依赖性的情况下创建SomeService对象。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
    }
}

好知

如果只有一个构造函数调用,则不需要包含@autowired注释。然后您可以使用类似的方法:

1
2
3
4
5
6
7
8
9
@RestController
public class NiceController {

    private final DataRepository repository;

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

…Spring数据存储库注入示例。