如何在没有显式调用的情况下注入值?

时间:2016-07-09 19:21:28

标签: java dependency-injection guice

我熟悉依赖注入概念及其好处,但使用能够为我处理这项业务的框架会让人感到困惑。

这个问题适用于任何DI框架,但我会坚持使用这个问题的Guice。

问题

让我们说,我有以下课程:

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();

    }

}

现在,让我们说我有一些随机类,需要注入我的茶界:

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;
    }
}

据我所知,此解决方案需要创建新类才能工作:

//In main method
injector.injectMembers(new RandomClass());
2)更糟糕的解决方案,就是在RandomClass中注入注入器并手动询问类实例,如下所示:

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();
    }

}

即便如此,我必须在我的bootstrapping方法中从进样器获取RandomClass实例。

问题

如果他们需要以任何方式提供课程,我真的不能理解DI框架的整个概念。

1)是否有任何可能的方法将Tea实例注入RandomClass而没有明确告诉使用bootstrapping方法注入这样做?如果可能,那怎么样?

2)使用DI框架的优点是什么,如果我手动&#34;加载&#34;所有注入价值的类?我的意思是,我可以在不使用框架的情况下向依赖项提供某个类的新实例。那为什么人们会用呢?

1 个答案:

答案 0 :(得分:2)

DI框架背后的想法很简单:类不应该负责实例化其依赖项。所以,虽然我不建议这么做, 100%DI解决方案应包括对new 的零调用。它应该完全来自工厂类。

这是你的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.

现在您可以手动使用RandomClass(通过调用@Inject-annotated构造函数)或自动(仅通过Guice请求实例)。这样可以轻松切换实现,包括仅在测试中切换实现来测试您编写的双打或者您通过Mockito创建的双打。

关于提供商的最后一句话:您不必自己担心提供商:无论您如何绑定实例或提供商,都可以使用@Inject X或{{}来访问它1}}构造函数或字段中的任何位置(或@Inject Provider<X>getInstance)。如果您不确定需要实例,或者您需要多个实例,只需要手动编写提供程序,如果需要调用某个外部方法来获取实例(如果需要进行后处理),则可以注入提供程序。