如何在没有显式调用的情况下注入值?
How to inject values without explicit calls?
我熟悉依赖注入的概念及其好处,但使用框架来处理这项业务让我感到有些困惑。
这个问题对任何 DI 框架都有效,但我会坚持用这个问题回答 Guice。
问题
比方说,我有以下 classes:
public interface Tea {
void prepare();
}
public class GreenTea implements Tea {
public void prepare() {
System.out.println("Preparing Green Tea");
}
}
public class BlackTea implements Tea {
public void prepare() {
System.out.println("Preparing Black Tea");
}
}
没有框架,在我的主要方法中,我会做这样的事情:
public static void main(String[] args) {
Tea tea = new GreenTea();
tea.prepare();
}
在 Google Guice Inject 的帮助下,我可以更进一步并做这样的事情:
public class TeaModule extends AbstractModule {
protected void configure() {
bind(Tea.class).to(GreenTea.class);
}
}
public class Main {
@Inject
private Tea tea;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TeaModule());
Main main = injector.getInstance(Main.class);
main.tea.prepare();
}
}
现在,假设我有一些随机 class,需要注入我的茶接口:
public class RandomClass {
private Tea tea;
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
public Tea getTea() {
return tea;
}
@Inject
public void setTea(Tea tea) {
this.tea = tea;
}
}
不幸的是,这会抛出 NullPointerException,因为 RandomClass 不知道任何来自外部的注入。
目前我找到的解决方案
1) 我读过有关创建自定义提供程序的信息,例如:
public class TeaProvider implements Provider<Tea> {
public Tea get() {
Tea tea = new BlackTea();
return tea;
}
}
据我所知,此解决方案需要创建新的 class 才能工作:
//In main method
injector.injectMembers(new RandomClass());
2) 更糟糕的解决方案是在 RandomClass 中注入注入器并手动请求 class 实例,如下所示:
public class RandomClass {
@Inject
Injector injector;
private Tea tea;
public RandomClass() {
tea = injector.getInstance(Tea.class);
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
即便如此,我必须在我的引导方法中从注入器获取 RandomClass 实例。
问题
我真的不明白 DI 框架的全部概念,如果它们需要以任何一种方式提供 classes。
1) 是否有任何可能的方法将 Tea 实例注入 RandomClass 而无需在引导方法中明确告知注入这样做?如果可以的话,怎么做?
2) 如果我必须手动 "load" 所有 class 注入值,使用 DI 框架有什么优点?我的意思是,我可以在不使用框架的情况下向依赖者提供一些 class 的新实例。那为什么会有人用呢?
DI 框架背后的想法很简单:class 不应该负责实例化其依赖项。因此,尽管我不建议走这么远,100% DI 解决方案应该包括对 new
的零调用。它应该完全来自工厂 class.
这是你的 RandomClass
,没有 DI:
public class RandomClass {
private Tea tea;
public RandomClass() {
tea = new BlackTea();
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
此时,您应该注意到在不测试 Tea 功能的情况下测试 RandomClass 是不可能,因为您没有提供替代实现的方法。
另一种方法:
public class RandomClass {
private Tea tea;
public RandomClass(Tea tea) {
this.tea = tea;
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
public class RandomClassProvider {
public RandomClass create() {
return new RandomClass(new BlackTea());
}
}
现在,带有 DI 注释:
public class RandomClass {
private Tea tea;
@Inject public RandomClass(Tea tea) {
this.tea = tea;
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
// Guice writes Provider<RandomClass> automatically.
现在您可以手动(通过调用带@Inject 注释的构造函数)或自动(只需通过 Guice 请求实例)使用 RandomClass。这应该可以轻松切换实现,包括 仅在测试中切换实现 以测试您编写的或通过 Mockito 创建的替身。
关于提供者的最后一句话:您不必自己担心提供者:无论您如何绑定实例或提供者,您都可以使用@Inject X
或 @Inject Provider<X>
构造函数或字段中的任意位置(或 getInstance
或 getProvider
)。如果您不确定是否需要一个实例,或者如果您需要多个实例,请注入一个 Provider,如果您需要调用一些外部方法来获取一个实例,并且需要进行后处理,则仅手动编写一个 Provider。
我熟悉依赖注入的概念及其好处,但使用框架来处理这项业务让我感到有些困惑。
这个问题对任何 DI 框架都有效,但我会坚持用这个问题回答 Guice。
问题
比方说,我有以下 classes:
public interface Tea {
void prepare();
}
public class GreenTea implements Tea {
public void prepare() {
System.out.println("Preparing Green Tea");
}
}
public class BlackTea implements Tea {
public void prepare() {
System.out.println("Preparing Black Tea");
}
}
没有框架,在我的主要方法中,我会做这样的事情:
public static void main(String[] args) {
Tea tea = new GreenTea();
tea.prepare();
}
在 Google Guice Inject 的帮助下,我可以更进一步并做这样的事情:
public class TeaModule extends AbstractModule {
protected void configure() {
bind(Tea.class).to(GreenTea.class);
}
}
public class Main {
@Inject
private Tea tea;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TeaModule());
Main main = injector.getInstance(Main.class);
main.tea.prepare();
}
}
现在,假设我有一些随机 class,需要注入我的茶接口:
public class RandomClass {
private Tea tea;
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
public Tea getTea() {
return tea;
}
@Inject
public void setTea(Tea tea) {
this.tea = tea;
}
}
不幸的是,这会抛出 NullPointerException,因为 RandomClass 不知道任何来自外部的注入。
目前我找到的解决方案
1) 我读过有关创建自定义提供程序的信息,例如:
public class TeaProvider implements Provider<Tea> {
public Tea get() {
Tea tea = new BlackTea();
return tea;
}
}
据我所知,此解决方案需要创建新的 class 才能工作:
//In main method
injector.injectMembers(new RandomClass());
2) 更糟糕的解决方案是在 RandomClass 中注入注入器并手动请求 class 实例,如下所示:
public class RandomClass {
@Inject
Injector injector;
private Tea tea;
public RandomClass() {
tea = injector.getInstance(Tea.class);
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
即便如此,我必须在我的引导方法中从注入器获取 RandomClass 实例。
问题
我真的不明白 DI 框架的全部概念,如果它们需要以任何一种方式提供 classes。
1) 是否有任何可能的方法将 Tea 实例注入 RandomClass 而无需在引导方法中明确告知注入这样做?如果可以的话,怎么做?
2) 如果我必须手动 "load" 所有 class 注入值,使用 DI 框架有什么优点?我的意思是,我可以在不使用框架的情况下向依赖者提供一些 class 的新实例。那为什么会有人用呢?
DI 框架背后的想法很简单:class 不应该负责实例化其依赖项。因此,尽管我不建议走这么远,100% DI 解决方案应该包括对 new
的零调用。它应该完全来自工厂 class.
这是你的 RandomClass
,没有 DI:
public class RandomClass {
private Tea tea;
public RandomClass() {
tea = new BlackTea();
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
此时,您应该注意到在不测试 Tea 功能的情况下测试 RandomClass 是不可能,因为您没有提供替代实现的方法。
另一种方法:
public class RandomClass {
private Tea tea;
public RandomClass(Tea tea) {
this.tea = tea;
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
public class RandomClassProvider {
public RandomClass create() {
return new RandomClass(new BlackTea());
}
}
现在,带有 DI 注释:
public class RandomClass {
private Tea tea;
@Inject public RandomClass(Tea tea) {
this.tea = tea;
}
public void doStuff() {
System.out.print("Doing stuff and.. ");
tea.prepare();
}
}
// Guice writes Provider<RandomClass> automatically.
现在您可以手动(通过调用带@Inject 注释的构造函数)或自动(只需通过 Guice 请求实例)使用 RandomClass。这应该可以轻松切换实现,包括 仅在测试中切换实现 以测试您编写的或通过 Mockito 创建的替身。
关于提供者的最后一句话:您不必自己担心提供者:无论您如何绑定实例或提供者,您都可以使用@Inject X
或 @Inject Provider<X>
构造函数或字段中的任意位置(或 getInstance
或 getProvider
)。如果您不确定是否需要一个实例,或者如果您需要多个实例,请注入一个 Provider,如果您需要调用一些外部方法来获取一个实例,并且需要进行后处理,则仅手动编写一个 Provider。