Spring @Autowire on Properties vs Constructor

时间:2016-11-15 21:29:37

标签: spring dependency-injection constructor autowired

因为我一直在使用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)

有什么我想念的吗?除了在单元测试中添加代码之外,还有其他任何自动装配的构造函数吗?这是一种更优先的依赖注入方式吗?

9 个答案:

答案 0 :(得分:117)

是的,选项B(称为构造函数注入)实际上是建议使用场注入,并且有几个优点:

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

有关更详细的文章,请参阅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;

}

这可能需要在同一个 bean 中调用 advised 方法。 herehere 还讨论了自注入。

答案 8 :(得分:0)

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

<块引用>

您的依赖项应该是最终的,即不被程序修改。