Java 解释为什么构造函数注入比其他选项更好
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21218868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Explain why constructor inject is better than other options
提问by minil
In a Pro Spring 3 Book, Chapter 4 - Introduction IOC and DI in Spring - Page 59, In "Setter Injection vs. Constructor Injection" section, a paragraph says
在 Pro Spring 3 Book 的第 4 章 - Spring 中的 IOC 和 DI 简介 - 第 59 页,在“Setter Injection vs. Constructor Injection”部分,一段说
Spring included, provide a mechanism for ensuring that all dependencies are defined when you use Setter Injection, but by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner"
包括 Spring,提供了一种机制来确保在使用 Setter 注入时定义所有依赖项,但是通过使用构造函数注入,您可以以与容器无关的方式断言对依赖项的要求”
Could you explain with examples
你能用例子解释一下吗
回答by duffymo
With examples? Here's a simple one:
有例子吗?这是一个简单的:
public class TwoInjectionStyles {
private Foo foo;
// Constructor injection
public TwoInjectionStyles(Foo f) {
this.foo = f;
}
// Setting injection
public void setFoo(Foo f) { this.foo = f; }
}
Personally, I prefer constructor injection when I can.
就个人而言,我更喜欢构造函数注入。
In both cases, the bean factory instantiates the TwoInjectionStyles
and Foo
instances and gives the former its Foo
dependency.
在这两种情况下,bean 工厂都会实例化TwoInjectionStyles
和Foo
实例,并为前者提供Foo
依赖项。
回答by MariuszS
(...) by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
(...) 通过使用构造函数注入,您可以以与容器无关的方式声明对依赖项的要求
This mean that you can enforce requirements for all injected fields without using any container specific solution.
这意味着您可以在不使用任何特定于容器的解决方案的情况下强制执行所有注入字段的要求。
Setter injection example
Setter 注入示例
With setter injection special spring annotation @Required
is required.
使用 setter 注入需要特殊的 spring 注释@Required
。
@Required
Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value.
@必需的
将方法(通常是 JavaBean setter 方法)标记为“必需”:也就是说,setter 方法必须配置为使用值进行依赖注入。
Usage
用法
import org.springframework.beans.factory.annotation.Required;
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class Foo {
private Bar bar;
@Inject
@Required
public void setBar(Bar bar) {
this.bar = bar;
}
}
Constructor injection example
构造函数注入示例
All required fields are defined in constructor, pure Java solution.
所有必需的字段都在构造函数中定义,纯 Java 解决方案。
Usage
用法
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class Foo {
private Bar bar;
@Inject
public Foo(Bar bar) {
this.bar = bar;
}
}
Unit testing
单元测试
This is especially useful in Unit Testing. Such kind of tests should be very simple and doesn't understand annotation like @Required
, they generally not need a Spring for running simple unit test. When constructor is used, setup of this class for testing is much easier, there is no need to analyze how class under test is implemented.
这在单元测试中特别有用。这种测试应该非常简单,并且不会像 那样理解注解@Required
,它们通常不需要 Spring 来运行简单的单元测试。当使用构造函数时,这个测试类的设置要容易得多,不需要分析被测类是如何实现的。
回答by Emerson Farrugia
A class that takes a required dependency as a constructor argument can only be instantiated if that argument is provided (you should have a guard clause to make sure the argument is not null.) A constructor therefore enforces the dependency requirement whether or not you're using Spring, making it container-agnostic.
将必需的依赖项作为构造函数参数的类只能在提供该参数的情况下实例化(您应该有一个保护子句以确保该参数不为空。)因此,无论您是否为,构造函数都会强制执行依赖项要求使用 Spring,使其与容器无关。
If you use setter injection, the setter may or may not be called, so the instance may never be provided with its dependency. The only way to force the setter to be called is using @Required
or @Autowired
, which is specific to Spring and is therefore not container-agnostic.
如果您使用 setter 注入,则可能会或可能不会调用 setter,因此可能永远不会为实例提供其依赖项。强制调用 setter 的唯一方法是使用@Required
or @Autowired
,它特定于 Spring,因此与容器无关。
So to keep your code independent of Spring, use constructor arguments for injection.
因此,为了让您的代码独立于 Spring,请使用构造函数参数进行注入。
Update: Spring 4.3 will perform implicit injection in single-constructor scenarios, making your code more independent of Spring by potentially not requiring an @Autowired
annotation at all.
更新:Spring 4.3 将在单构造函数场景中执行隐式注入,通过可能根本不需要@Autowired
注释,使您的代码更加独立于 Spring 。
回答by Sazzadur Rahaman
By using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
通过使用构造函数注入,您可以以与容器无关的方式声明对依赖项的要求
We need the assurance from the IoC container that, before using any bean, the injection of necessary beans must be done.
我们需要来自 IoC 容器的保证,在使用任何 bean 之前,必须完成必要 bean 的注入。
In setter injectionstrategy, we trust the IoC container that it will first create the bean first but will do the injection right before using the bean using the setter methods. And the injection is done according to your configuration. If you somehow misses to specify any beans to inject in the configuration, the injection will not be done for those beans and your dependent bean will not function accordingly when it will be in use!
在setter 注入策略中,我们相信 IoC 容器会首先创建 bean,但会在使用 setter 方法使用 bean 之前进行注入。并根据您的配置完成注入。如果您以某种方式错过在配置中指定要注入的任何 bean,则不会对这些 bean 进行注入,并且您的依赖 bean 在使用时将不会相应地运行!
But in constructor injectionstrategy, container imposes (or must impose) to provide the dependencies properly while constructing the bean. This was addressed as " container-agnostic manner", as we are required to provide dependencies while creating the bean, thus making the visibility of dependency, independent of any IoC container.
但是在构造函数注入策略中,容器在构造 bean 时强加(或必须强加)以正确提供依赖项。这被称为“容器不可知方式”,因为我们需要在创建 bean 时提供依赖项,从而使依赖项的可见性独立于任何 IoC 容器。
Edit:
编辑:
Q1:And how to prevent container from creating bean by constructor with null
values instead of missing beans?
Q1:如何防止容器通过构造函数创建beannull
而不是缺少bean?
You have no option to really miss any <constructor-arg>
(in case of Spring), because you are imposed by IoC container to provide all the constructor arguments needed to match a provided constructor for creating the bean. If you provide null
in your <constructor-arg>
intentionally. Then there is nothing IoC container can do or need to do with it!
您没有选择真正错过任何一个<constructor-arg>
(在 Spring 的情况下),因为 IoC 容器要求您提供与创建 bean 所提供的构造函数匹配所需的所有构造函数参数。如果您提供null
您的<constructor-arg>
故意。那么 IoC 容器就没有什么可以做或需要做的了!
回答by Jawa
To make it simple, let us say that we can use constructor based dependency injection for mandatory dependencies and setter based injection for optional dependencies. It is a rule of thumb!!
为了简单起见,让我们说我们可以对强制依赖使用基于构造函数的依赖注入,对可选依赖使用基于 setter 的注入。这是一个经验法则!!
Let's say for example.
比如说。
If you want to instantiate a class you always do it with its constructor. So if you are using constructor based injection, the only way to instantiate the class is through that constructor. If you pass the dependency through constructor it becomes evident that it is a mandatory dependency.
如果你想实例化一个类,你总是用它的构造函数来做。因此,如果您使用基于构造函数的注入,则实例化类的唯一方法是通过该构造函数。如果您通过构造函数传递依赖项,很明显它是一个强制依赖项。
On the other hand, if you have a setter method in a POJO class, you may or may not set value for your class variable using that setter method. It is completely based on your need. i.e. it is optional. So if you pass the dependency through setter method of a class it implicitly means that it is an optional dependency. Hope this is clear!!
另一方面,如果您在 POJO 类中有一个 setter 方法,您可能会或可能不会使用该 setter 方法为您的类变量设置值。它完全基于您的需要。即它是可选的。因此,如果您通过类的 setter 方法传递依赖项,则隐式意味着它是一个可选依赖项。希望这很清楚!!
回答by vijayst
Constructor injection is used when the class cannot function without the dependent class.
当类在没有依赖类的情况下无法运行时,使用构造函数注入。
Property injection is used when the class can function without the dependent class.
当类可以在没有依赖类的情况下运行时使用属性注入。
As a concrete example, consider a ServiceRepository which depends on IService to do its work. Since ServiceRepository cannot function usefully without IService, it makes sense to have it injected via the constructor.
作为一个具体的例子,考虑一个依赖 IService 来完成它的工作的 ServiceRepository。由于 ServiceRepository 在没有 IService 的情况下无法有效运行,因此通过构造函数注入它是有意义的。
The same ServiceRepository class may use a Logger to do tracing. The ILogger can be injected via Property injection.
同一个 ServiceRepository 类可以使用 Logger 进行跟踪。ILogger 可以通过属性注入来注入。
Other common examples of Property injection are ICache (another aspect in AOP terminology) or IBaseProperty (a property in the base class).
属性注入的其他常见示例是 ICache(AOP 术语中的另一个方面)或 IBaseProperty(基类中的属性)。
回答by quintin
This example may help:
这个例子可能有帮助:
Controller class:
控制器类:
@RestController
@RequestMapping("/abc/dev")
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class MyController {
//Setter Injection
@Resource(name="configBlack")
public void setColor(Color c) {
System.out.println("Injecting setter");
this.blackColor = c;
}
public Color getColor() {
return this.blackColor;
}
public MyController() {
super();
}
Color nred;
Color nblack;
//Constructor injection
@Autowired
public MyController(@Qualifier("constBlack")Color b, @Qualifier("constRed")Color r) {
this.nred = r;
this.nblack = b;
}
private Color blackColor;
//Field injection
@Autowired
private Color black;
//Field injection
@Resource(name="configRed")
private Color red;
@RequestMapping(value = "/customers", produces = { "application/text" }, method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.CREATED)
public String createCustomer() {
System.out.println("Field injection red: " + red.getName());
System.out.println("Field injection: " + black.getName());
System.out.println("Setter injection black: " + blackColor.getName());
System.out.println("Constructor inject nred: " + nred.getName());
System.out.println("Constructor inject nblack: " + nblack.getName());
MyController mc = new MyController();
mc.setColor(new Red("No injection red"));
System.out.println("No injection : " + mc.getColor().getName());
return "Hello";
}
}
Interface Color:
界面颜色:
public interface Color {
public String getName();
}
Class Red:
红色等级:
@Component
public class Red implements Color{
private String name;
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Red(String name) {
System.out.println("Red color: "+ name);
this.name = name;
}
public Red() {
System.out.println("Red color default constructor");
}
}
Class Black:
黑色类:
@Component
public class Black implements Color{
private String name;
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Black(String name) {
System.out.println("Black color: "+ name);
this.name = name;
}
public Black() {
System.out.println("Black color default constructor");
}
}
Config class for creating Beans:
用于创建 Bean 的配置类:
@Configuration
public class Config {
@Bean(name = "configRed")
public Red getRedInstance() {
Red red = new Red();
red.setName("Config red");
return red;
}
@Bean(name = "configBlack")
public Black getBlackInstance() {
Black black = new Black();
black.setName("config Black");
return black;
}
@Bean(name = "constRed")
public Red getConstRedInstance() {
Red red = new Red();
red.setName("Config const red");
return red;
}
@Bean(name = "constBlack")
public Black getConstBlackInstance() {
Black black = new Black();
black.setName("config const Black");
return black;
}
}
BootApplication (main class):
BootApplication(主类):
@SpringBootApplication
@ComponentScan(basePackages = {"com"})
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
Run Application and hit URL: GET 127.0.0.1:8080/abc/dev/customers/
运行应用程序并点击 URL:GET 127.0.0.1:8080/abc/dev/customers/
Output:
Injecting setter
Field injection red: Config red
Field injection: null
Setter injection black: config Black
Constructor inject nred: Config const red
Constructor inject nblack: config const Black
Red color: No injection red
Injecting setter
No injection : No injection red