web-dev-qa-db-ja.com

共通の依存関係モジュールをインストールする2つのGuiceモジュールの使用方法

私は4つの部分で構成されるプロジェクトに取り組んでいます:

  • すべてをまとめるMainプロジェクト。これには、public static void main(String... args)エントリポイントが含まれます。
  • コンポーネントA
  • コンポーネントB
  • CommonAの両方が参照するサードパーティのBコンポーネント。

4つのパーツすべての間の配管にGuiceを使用していますが、これが私の問題です。
AsとBsの両方のメインGuiceモジュールに、Commonで定義されているモジュールを拡張するモジュールをインストールします。実行時に、このセットアップは次のエラーで失敗します。

_common.SomeClass_へのバインディングは、common.AbstractCommonModule.configure()ですでに構成されています。 [ ソース ]

これは、common.AbstractCommonModule.configure()を2回呼び出しているためです。 1回目はコンポーネントAcom.a.MainModule.configure()から_common.AbstractCommonPrivateModule_のサブクラスインスタンスをインストールし、2回目はコンポーネントBcom.b.MainModule.configure()からインストールします。

Mainに_common.AbstractCommonPrivateModule_の one インスタンスだけをインストールすることはオプションではありません。AbstractCommonPrivateModuleは特定のバインダーメソッドbindComplicatedStuff(ComplicatedStuff)を実装しているため、引数しかわかりません。それぞれABの内部。

ABのそれぞれのメインGuiceモジュールを PrivateModule sでラップすることにより、このすべてを回避しようとしました。ただし、これは次のエラーで失敗しました。

%sのバインディングを作成できません。 1つ以上の子インジェクターまたはプライベートモジュールで既に構成されています%s%n PrivateModule内にある場合、バインディングを公開するのを忘れましたか? [ ソース ]

私の場合、ABのそれぞれのメインGuiceモジュールは実際には ServletModule s-これは明らかにMainから 2回インストールできます

これらのエラーを回避してAbstractCommonPrivateModuleモジュールを2回インストールするにはどうすればよいですか?

編集:サンプルコードをアップロードしました(詳細についての説明付き) GitHubに

22
derabbink

ABCommonをインストールさせるのではなく、Commonから必要なクラスのrequireBinding()をインストールします。次に、AまたはBに依存するモジュールもCommonをインストールする必要があります。これは少し奇妙に感じるかもしれませんが、ABCommonとあまり緊密に結合されていないため、実際には望ましいことです。


更新

2つのShiroWebModulesをインストールする理由は、uiモジュール内のJerseyリソースを1つのShiro構成(パスワード保護リソースを理解しないもの)を使用してのみ保護し、すべてのJerseyリソースを保護するためです。 apiモジュールでは、まったく異なるShiro構成(認証メカニズムとしてベアラートークンのみを理解する構成)を使用して保護する必要があります。

大まかに言えば、これは手に負えない。 Guice Injectorは、アプリケーション全体に対して何かを行う1つの方法(通常はインターフェイスの1つの実装)を提供します。パッケージごとに異なるメカニズムではありません。 2つのModules、SwsApiServletModuleおよびSwsUiServletModuleは多数の同一のバインディングを提供し、SwsModuleはそれらを一緒にインストールします。本質的には、「Guice、ベアラートークンベースの認証メカニズムを提供してください」と言っており、「Guice、パスワードベースの認証メカニズムを提供してください」と言った直後です。どちらか一方しか実行できないため、いずれかを任意に選択するのではなく、失敗します。

もちろん、ニーズが正確に何であるかに応じて、いくつかの解決策があります。最も一般的なのは、 バインディングアノテーション を使用し、UIコードとAPIコードに異なるアノテーションを要求させることです。このようにして、同じインターフェースまたはクラスの2つの異なる実装(異なるアノテーションを使用)をインストールできます。

次に例を示します。

package api;

public class ApiResources {
  @Inject
  public ApiResources(@ApiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package api;

public class ApiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(ApiAuthMechanism.class)
        .to(BearerTokenAuthMechanism.class);
  }
}

---

package ui;

public class UiResources {
  @Inject
  public UiResources(@UiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package ui;

public class UiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(UiAuthMechanism.class)
        .to(PasswordAuthMechanism.class);
  }
}

---

package webap;

public class WebappModule implements Module {
  public void configure() {
    // These modules can be installed together,
    // because they don't install overlapping bindings 
    install(new ApiModule());
    install(new UiModule());
  }
}

コメントで、重複するバインディングはサードパーティのモジュールからのものであるため、インストールすることを制御できないと述べています。その場合(コードのどこでそれが起こっているのかわかりませんでした)、サードパーティがあなたがやろうとしていることを望んでいない可能性があります。セキュリティ上の理由から。たとえば、パスワードベースのメカニズムをバインドするだけで、アプリ全体に脆弱性が発生する可能性があります。サードパーティがモジュールの使用をどのように意図しているかをよりよく理解することは価値があるかもしれません。

別のオプションは、理想的ではありませんが、一部のユースケースでは機能しますが、2つの完全に別個のInjectorインスタンスを使用し、1つは各バインディングを使用します。次に、必要なインスタンスを手動でUIおよびAPIコードに直接渡します。これはGuiceの目的をいくらか無効にしますが、必ずしも間違った決定であるとは限りません。 child Injectors を使用すると、これを簡単に行うことができます。


余談ですが、「サンプルコード」は膨大で、おそらく90%以上は問題とは無関係です。将来的には、目前の問題に関連するコードのみを含む [〜#〜] sscce [〜#〜] を作成するために時間をかけてください。 100以上のJavaファイルと7,300行以上のコードをふるいにかけて問題を理解する方法はありません。これにより、あなたを助けようとしている人々が簡単になるだけでなく、多くの場合、問題を実証するSSCCEを作成しようとするだけで、自分で問題を理解して解決するのに十分です。

15
dimo414

同じモジュールを2回インストールするには、モジュールの.equalsメソッドをオーバーライドして、オブジェクトの同等性ではなくクラスを参照します。 Guiceは、すでにインストールされているものと同じモジュールをインストールしません。これは、入力するときにほとんどの場合役に立ちません。

install new AbstractCommonPrivateModule();

したがって、各オブジェクトは、最後のオブジェクトと等しくない異なるインスタンスです。 equalsメソッドをオーバーライドすると、次のことが回避されます。

@Override
public boolean equals(Object obj) {
    return obj != null && this.getClass().equals(obj.getClass());
}

// Override hashCode as well.
@Override
public int hashCode() {
    return this.getClass().hashCode();
}

ただし、この方法はしばしば正しくないことに注意してください。

上記をしない理由

この時点では、Guiceや依存性注入を実際に利用していません。代わりに、AbstractCommonPrivateModuleの実装とそれをインストールするBおよびCの実装を緊密に結合しました。 @ dimo414で述べたように、ここではOPが2つの異なるShiroWebModulesを使用したいと考えているようです。これは、Guiceがこれら2つの異なるモジュールをより高いレベルでインストールすることで得意とすることです。同様に、より高いレベルのインストールでは、テスト中にスワップアウトできます。ある時点で実際にモジュールの1つを交換したい場合、Guiceは再び壊れます。

これは、モジュールをオーバーライドした場合にも破損する可能性があります(これはテストに役立つもう1つのツールです)。

OPは、汎用モジュールを2回インストールすることも望んでいます。別のライブラリの汎用モジュールをラップすると、リスクが高まります。元の作成者には、セキュリティなど、上記のトリックを自分で実装しない非常に正当な理由がある可能性があります。

0
hubatish