私は4つの部分で構成されるプロジェクトに取り組んでいます:
Main
プロジェクト。これには、public static void main(String... args)
エントリポイントが含まれます。A
B
Common
とA
の両方が参照するサードパーティのB
コンポーネント。4つのパーツすべての間の配管にGuiceを使用していますが、これが私の問題です。A
sとB
sの両方のメインGuiceモジュールに、Common
で定義されているモジュールを拡張するモジュールをインストールします。実行時に、このセットアップは次のエラーで失敗します。
_
common.SomeClass
_へのバインディングは、common.AbstractCommonModule.configure()
ですでに構成されています。 [ ソース ]
これは、common.AbstractCommonModule.configure()
を2回呼び出しているためです。 1回目はコンポーネントA
のcom.a.MainModule.configure()
から_common.AbstractCommonPrivateModule
_のサブクラスインスタンスをインストールし、2回目はコンポーネントB
のcom.b.MainModule.configure()
からインストールします。
Main
に_common.AbstractCommonPrivateModule
_の one インスタンスだけをインストールすることはオプションではありません。AbstractCommonPrivateModule
は特定のバインダーメソッドbindComplicatedStuff(ComplicatedStuff)
を実装しているため、引数しかわかりません。それぞれA
とB
の内部。
A
とB
のそれぞれのメインGuiceモジュールを PrivateModule
sでラップすることにより、このすべてを回避しようとしました。ただし、これは次のエラーで失敗しました。
%sのバインディングを作成できません。 1つ以上の子インジェクターまたはプライベートモジュールで既に構成されています%s%n PrivateModule内にある場合、バインディングを公開するのを忘れましたか? [ ソース ]
私の場合、A
とB
のそれぞれのメインGuiceモジュールは実際には ServletModule
s-これは明らかにMain
から 2回インストールできます。
これらのエラーを回避してAbstractCommonPrivateModule
モジュールを2回インストールするにはどうすればよいですか?
編集:サンプルコードをアップロードしました(詳細についての説明付き) GitHubに
A
とB
にCommon
をインストールさせるのではなく、Common
から必要なクラスのrequireBinding()
をインストールします。次に、A
またはB
に依存するモジュールもCommon
をインストールする必要があります。これは少し奇妙に感じるかもしれませんが、A
とB
がCommon
とあまり緊密に結合されていないため、実際には望ましいことです。
更新
2つの
ShiroWebModule
sをインストールする理由は、ui
モジュール内のJerseyリソースを1つのShiro構成(パスワード保護リソースを理解しないもの)を使用してのみ保護し、すべてのJerseyリソースを保護するためです。api
モジュールでは、まったく異なるShiro構成(認証メカニズムとしてベアラートークンのみを理解する構成)を使用して保護する必要があります。
大まかに言えば、これは手に負えない。 Guice Injector
は、アプリケーション全体に対して何かを行う1つの方法(通常はインターフェイスの1つの実装)を提供します。パッケージごとに異なるメカニズムではありません。 2つのModule
s、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 Injector
s を使用すると、これを簡単に行うことができます。
余談ですが、「サンプルコード」は膨大で、おそらく90%以上は問題とは無関係です。将来的には、目前の問題に関連するコードのみを含む [〜#〜] sscce [〜#〜] を作成するために時間をかけてください。 100以上のJavaファイルと7,300行以上のコードをふるいにかけて問題を理解する方法はありません。これにより、あなたを助けようとしている人々が簡単になるだけでなく、多くの場合、問題を実証するSSCCEを作成しようとするだけで、自分で問題を理解して解決するのに十分です。
同じモジュールを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つの異なるShiroWebModule
sを使用したいと考えているようです。これは、Guiceがこれら2つの異なるモジュールをより高いレベルでインストールすることで得意とすることです。同様に、より高いレベルのインストールでは、テスト中にスワップアウトできます。ある時点で実際にモジュールの1つを交換したい場合、Guiceは再び壊れます。
これは、モジュールをオーバーライドした場合にも破損する可能性があります(これはテストに役立つもう1つのツールです)。
OPは、汎用モジュールを2回インストールすることも望んでいます。別のライブラリの汎用モジュールをラップすると、リスクが高まります。元の作成者には、セキュリティなど、上記のトリックを自分で実装しない非常に正当な理由がある可能性があります。