web-dev-qa-db-ja.com

Guice Injector.getInstance()-グッドプラクティス?

同じライブラリを共有する2つのアプリケーションがあるとします。このライブラリには、DAO、Utilsなどの一般的なクラスが含まれています。共有ライブラリ内のすべてのものがGuiceに関連付けられています。私の2つのアプリはこのライブラリに依存していますが、Guiceに直接依存していません。

 ______    ______    ______
|      |  |      |  |      |
| APP1 |->| LIB  |<-| APP2 |
'------'  '------'  '------'

私は現在このようなものを使用しています:

static <T> Utils.getInstanceOf (Class<T> type);

これは単に以下のラッパーです:

injector.getInstance (Class<T> type);

しかし、guice docsは言う:

可能であれば、この方法の使用を避け、Guiceが依存関係を事前に注入するようにします。

では、Guiceモジュールに手動でバインドすることなく、2つのアプリに依存性注入を提供するための最良の方法は何ですか?

16
albogdano

では、Guiceモジュールに手動でバインドすることなく、2つのアプリに依存性注入を提供するための最良の方法は何ですか?

そのような方法はありません。 Guiceを完全に受け入れるか、Guiceを使用せずに依存関係を明示的に渡します。まあ、そのような方法でコードを構造化して、クラスの依存関係を直接作成せず、代わりにコンストラクタを介して渡すことも、「依存性注入」と呼ばれることもありますが、これはあなたが意図したものではないと確信しています。アプリでGuiceを使用したくない場合は、特にgetInstance()より良いものを取得できません。これは特にstaticラッパー。

理想的には、ライブラリは、アプリケーションでGuice.createInjector()を介してインストールできるモジュールを提供する必要があります。逆に、ライブラリは、アプリケーションで使用できるInjectorインスタンスを提供する必要がありますcreateChildInjector()を使用し、アプリケーション固有のモジュールを提供します。このアプローチを少し変更すると、アプリケーション固有のモジュールがライブラリに渡され、Injectorの作成に使用されます。最近、GuiceベースのAPIをカスタムサーブレットのようなインターフェースで記述しました。これは、最後のアプローチを使用して、いかなる種類のDIもまったくサポートしていませんでしたが、完全に機能しています。

サーブレットまたはジャージー環境でGuiceを使用することはまったく難しくありません。たとえば後者は、Guiceとすぐに統合できます(少なくとも1.xバージョンでは)。 Guice サーブレット拡張 も非常に便利で便利です。試してみて、自分の目で確かめてください。

10

static <T> Utils.getInstanceOf (Class<T> type);

結局は Service Locator になります。

いくつかの小さなケースではこれはinjectorが他の作成オブジェクトにエスケープすることは許容できますが、これはそれらの1つではないと思います。これで、Service Locatorのすべてのデメリットが得られました。すでに使用しているツールを使用することで、すべてのメリットを得ることができます。

インジェクタを使用する「通常の」パターンは、プロジェクトのいくつかのトップレベルのエントリポイントに設定することです(サーブレットシナリオでは、Guice-Servletを使用すると、これはGuiceServletContextListenerになります)。モジュール化のために、いくつかの依存関係へのエントリポイントで個々のインジェクタを構成し、その依存関係を配線する責任を持たせることができます。両方が必要な場合は、個々のバインディングと親プロジェクトのバインディングを依存関係に含め、バインディングが見つからない場合に親に委譲する子インジェクターを作成できます。 Guiceはこれをサポートしています。

ただし、依存関係の内部にインジェクターをセットアップし、それをメインアプリで使用するのは奇妙に思えます。これは、依存関係がメインアプリに必要なすべてのバインディングについて知っていることを意味します。このアプローチで何を達成しようとしているのかよくわかりません。 2つのアプリのバインディング設定が同じ/非常に似ているため、繰り返したくないですか?この場合、すべてのバインディング構成を持つモジュールを一度定義して(おそらく依存関係にある)、各アプリのエントリポイントでインジェクターを設定するときにそれを使用する必要があります。あなたのシナリオに関してはそれだけです。

一般的にあなたの質問に。インジェクターを明示的に通過させないことは良い習慣だと思います。これを行うときはいつでも、透過的なプロジェクトであるという依存性注入の考えに反対し、具体的な注入フレームワークに自分を結び付けます。ほとんどの場合、 Providers および Factories を使用することにより、インジェクターへの明示的な参照を回避できます。

2
Sven Amann

実行時にクラスCの新しいインスタンスを作成する必要があるメソッドがある場合は、プロバイダーをクラスにバインドします。 Cは通常の方法でバインドされます。

public CModule extends AbstractModule {
    @Overide
    public void configure() {
        bind(C.class).to(CImpl.class);
    }
}

Cインスタンスを作成するクラスは次のようになります。

class UserOfC {
    private Provider<C> cProvider;
    ...

    @Inject
    UserOfC(Provider<C> cProvider, ...) {
        this.cProvider = cProvider;
        ...
    }

    public void doSomethingWithAC (...) {
        C myC = cProvider.get();  // not a singleton; new instance created!
        ...
    }
}

Guiceは無料でプロバイダーインジェクションをサポートします。 Cがバインドされている場合、Cのインスタンスを注入するのと同じくらい簡単にプロバイダーを注入できます。

その他の提案:

たとえ可能であれば、コードを数行追加する必要があるとしても、構築時にすべての依存関係を注入することを強くお勧めします。私は何年もGuiceを使用してきましたが、部分的な構築やその他の高度な機能はまだ必要ありません。

部分注入の必要性に直面したとき、私は通常自分の工場を書きます。コードを作成すると、理解とデバッグがはるかに簡単になります。

2
Eric Mintz

ええ、このようにインジェクターを通過することはOKです。

ウィケットアプリケーションでも同様の処理を行ったので、ウィケット以外のページでは、単にinjector.get.inject(this)を使用してコンストラクタに渡しました。

そしてそれは完璧に動作します。

お役に立てれば。

0
Ashish