因此,由于我一直在使用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数据存储库注入的例子。

其他回答

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

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

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

很高兴知道

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

@RestController
public class NiceController {

    private final DataRepository repository;

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

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

是的,选项B(称为构造函数注入)实际上推荐在字段注入之上,它有几个优点:

依赖性被清楚地标识出来。在测试时,或者在任何其他情况下实例化对象时(比如在配置类中显式地创建bean实例),都不可能忘记一个对象。 依赖关系可以是final的,这有助于健壮性和线程安全性 您不需要反射来设置依赖项。InjectMocks仍然可用,但不是必需的。您可以自己创建mock,并通过简单地调用构造函数来注入它们

请参阅这篇由Spring贡献者之一Olivier Gierke撰写的更详细的文章。

有一种方法可以使用Lombok中的@ requeiredargsconstructor注释通过构造函数注入依赖项

@RequiredArgsConstructor
@Service
 class A {
     private final B b // needs to be declared final to be injected
}

这样就不需要指定构造函数

很少有@Autowired更可取的情况。 其中之一是循环依赖。想象一下下面的场景:

@Service
public class EmployeeService {
    private final DepartmentService departmentService;

    public EmployeeService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }
}

and

@Service
public class DepartmentService {
    private final EmployeeService employeeService;

    public DepartmentService(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }
}

Then Spring Bean Factory will throw circular dependency exception. This won't happen if you use @Autowired annotation in both beans. And this is understandable: the constructor injection happens at very early stage of Spring Bean initialization, in createBeanInstance method of Bean Factory, while @Autowired-based injection happens way later, on post processing stage and is done by AutowiredAnnotationBeanPostProcessor. Circular dependency is quite common in complex Spring Context application, and it needs not to be just two beans referring one another, it could a complex chain of several beans.

另一个@Autowired非常有用的用例是自我注入。

@Service
public class EmployeeService {
    
    @Autowired
    private EmployeeService self;

}

从同一个bean中调用被通知的方法可能需要这样做。这里和这里也讨论了自注入。