因为我一直在使用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)
有什么我想念的吗?除了在单元测试中添加代码之外,还有其他任何自动装配的构造函数吗?这是一种更优先的依赖注入方式吗?
答案 0 :(得分:117)
是的,选项B(称为构造函数注入)实际上是建议使用场注入,并且有几个优点:
有关更详细的文章,请参阅this blog post,其中一位是Spring贡献者,Olivier Gierke。
答案 1 :(得分:24)
我会用简单的话来解释你:
在选项(A)中,允许任何人(在Spring容器外部/内部的不同类中)使用默认构造函数(如new SomeService()
)创建实例,这不是好的,因为您SomeOtherService
需要SomeService
个对象(作为依赖项)。
除了添加代码之外,还有其他任何自动装配的构造函数 到单元测试?这是一种更优先的依赖方式 注射?
选项(B)是首选方法,因为它不允许在不实际解析SomeService
依赖项的情况下创建SomeOtherService
对象。
答案 2 :(得分:5)
我希望我不会因为表达自己的观点而被降级,但是对我来说,选项A更好地反映了Spring依赖注入的力量,而在选项B中,您将类与依赖关系结合在一起,实际上,您如果不从构造函数传递其依赖项,则无法实例化对象。发明了依赖注入是为了避免通过实现控制反转来实现,因此对我而言,选项B没有任何意义。
答案 3 :(得分:1)
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();
}
}
答案 4 :(得分:0)
了解
如果仅调用一个构造函数,则无需包含@Autowired批注。然后,您可以使用以下内容:
@RestController
public class NiceController {
private final DataRepository repository;
public NiceController(ChapterRepository repository) {
this.repository = repository;
}
}
... Spring Data Repository注入示例。
答案 5 :(得分:0)
实际上,以我的经验,第二种选择更好。不需要@Autowired
。实际上, 创建与框架紧密耦合的代码 (与Spring一样好)是更明智的选择。您希望代码尝试尽可能采用延迟决策方法。尽可能多地使用 pojo ,以便可以轻松替换框架。
因此,我建议您创建一个单独的Config文件并在其中定义您的bean,如下所示:
在 SomeService.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);
}
}
实际上,如果您想深入了解它,则使用 Field Injection (@Autowired
)会引起线程安全性问题(除其他外),具体取决于在项目规模上显然。 Check this out,以进一步了解advantages and disadvantages of Autowiring。实际上,关键人物实际上建议您使用构造函数注入而不是现场注入
答案 6 :(得分:0)
请注意,由于Spring 4.3甚至不需要在构造函数上使用@Autowired,因此您可以用Java样式编写代码,而不必使用Spring的注释。 您的摘要如下所示:
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => NavigationDrawerDemo(),
),
ModalRoute.withName('/LoginFieldForm'));
答案 7 :(得分:0)
很少有使用 @Autowired
的情况。
其中之一是循环依赖。想象一下以下场景:
@Service
public class EmployeeService {
private final DepartmentService departmentService;
public EmployeeService(DepartmentService departmentService) {
this.departmentService = departmentService;
}
}
和
@Service
public class DepartmentService {
private final EmployeeService employeeService;
public DepartmentService(EmployeeService employeeService) {
this.employeeService = employeeService;
}
}
然后Spring Bean Factory会抛出循环依赖异常。如果您在两个 bean 中都使用 @Autowired
注释,则不会发生这种情况。这是可以理解的:构造函数注入发生在 Spring Bean 初始化的早期阶段,在 Bean Factory 的 createBeanInstance
方法中,而基于 @Autowired
的注入发生在后期,在后期处理阶段,由AutowiredAnnotationBeanPostProcessor
。
循环依赖在复杂的 Spring Context 应用中很常见,它不需要只是两个 bean 相互引用,它可以是多个 bean 的复杂链。
另一个用例是自注入,其中 @Autowired
非常有用。
@Service
public class EmployeeService {
@Autowired
private EmployeeService self;
}
答案 8 :(得分:0)
我更喜欢构造注入,因为我可以将我的依赖标记为 final,而这在使用属性注入注入属性时是不可能的。
<块引用>您的依赖项应该是最终的,即不被程序修改。