2011-01-30 16 views
10

私は複雑な初期化スキームを持つクラスのセットを持っています。基本的に、私は、私はホールドを取得するために必要なインターフェイスを開始し、コールの束を作る、と私はそのインターフェイスを実装するオブジェクトで終わる。Spring @工場で作られた一般的な豆で@Autowiring

これを処理するために、私はインターフェースを与えられて最終的なオブジェクトを生成できるファクトリクラスを作成しました。私はこのファクトリをBeanにしました。そして、XMLで、さまざまなサービスBeanを、実装するインターフェイスのパラメータでこのファクトリオブジェクトを介してインスタンス化するものとして指定しました。

これは素晴らしいことです。私は完全に正確に必要な豆を手に入れます。残念ながら、私はコントローラのクラスからアクセスしたいと思います。コントローラのクラスは、コンポーネントのスキャンによって発見されます。私はここでは@Autowiredを使用していますが、Springにはどのようなオブジェクトの型がないのかわかりませんが、@Autowiredは型によって動作するため、私はSOLです。

ここで@Resource(name = "beanName")を使用すると完全に動作しますが、一部のBeanでは@Resource、他のBeanでは@Autowiredを使用するのが奇妙に見えます。

各タイプのファクトリメソッドを別にしなくても、これらのBeanごとにどのようなインターフェイスが作成されるかをSpringに知らせる方法はありますか?

私はSpring 2.5.6を使用しています。それ以外の場合は、JavaConfig全体を忘れてしまいます。

ファクトリクラス:

<T extends Client> T buildService(Class<T> clientClass) { 
    //Do lots of stuff with client class and return an object of clientClass. 
} 

アプリコンテキスト:

<bean id="serviceFactoryBean" class="com.captainAwesomePants.FancyFactory" /> 
<bean id="userService" factory-bean="serviceFactoryBean" factory-method="buildService"> 
    <constructor-arg value="com.captain.services.UserServiceInterface" /> 
</bean> 
<bean id="scoreService" factory-bean="serviceFactoryBean" factory-method="buildService"> 
    <constructor-arg value="com.captain.services.ScoreServiceInterface" /> 
</bean> 

私のコントローラ:

public class HomepageController { 

    //This doesn't work 
    @Autowired @Qualifier("userService") UserServiceInterface userService; 

    //This does 
    @Resource(name="scoreService") ScoreServiceInterface scoreService; 
} 

答えて

8

私はあなたがさらに一歩とimplement your factories as Spring FactoryBean classes工場のパターンを取ることをお勧めします。 FactoryBeanインターフェイスには、getObjectType()メソッドがあります。これは、containsが呼び出して、どのタイプのファクトリが返されるかを検出します。あなたのファクトリが合理的な価値を返す限り、これはあなたのautowiringに歯を取るものを与えます。

+0

私はそれが好きです。私はFactoryBeansから最初は傾いていましたが、正直言って、彼らは私が彼らがしたいものを正確にやることになります。 –

+0

@CaptainAwesomePants:私は自分の 'FactoryBean'実装をできるだけ薄くして、Spring APIを使用しない別のクラスに委譲させます。同じことをすることもできます - 既存の 'serviceFactoryBean'に委譲するだけです。 – skaffman

+0

@skaffman:この場合、リターンタイプのバリエーションの数だけ、多くの「FactoryBean」実装が存在するはずです。 2つの追加メソッドが工場の 'UserServiceInterface buildUserService(){return buildService(UserServiceInterface.class);}に追加された場合、Springは正しい型発見を実行します。 } 'と' ScoreServiceInterface buildScoreService(){buildService(ScoreServiceInterface.class)を返します。 } '(そしてコンテキストは再利用されています)? –

3

あなたは、この使用して達成することができるはずです。そして、あなたが直接あなたのオブジェクトに注入する@Resourceまたは@Autowired + @Qualifierのいずれかを使用することができます

<bean id="myCreatedObjectBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> 
    <property name="targetClass"> 
     <value>com.mycompany.MyFactoryClass</value> 
    </property> 
    <property name="targetMethod"> 
     <value>myFactoryMethod</value> 
    </property> 
</bean> 

を。

5

私も同様の問題がありましたが、JMockit(私が使用する必要があるテストフレームワーク)を使用して自動配線依存関係のモックアウトされた実装を作成するための単一のファクトリを使用したかったのです。

インターウェブ上で満足のいく解決策が見つからなかったので、私は本当にうまくいっている簡単な解決策をまとめました。

私のソリューションでは、Spring FactoryBeanも使用されていますが、私のすべてのBeanを作成するために単一のファクトリBeanを使用しています(元のAskerがやりたがっているようです)。

私の解決策は、実際の単一工場の周りにFactoryBeanラッパーを提供するファクトリ工場のメタファクトリを実装することでした。

public class MockBeanFactory<C> implements FactoryBean<C> { 

    private Class<C> mockBeanType; 
    protected MockBeanFactory(){} 

    protected <C> C create(Class<C> mockClass) { 
     return Mockit.newEmptyProxy(mockClass); 
    } 

    @Override 
    public C getObject() throws Exception { 
     return create(mockBeanType); 
    } 

    @Override 
    public Class<C> getObjectType() { 
     return mockBeanType; 
    } 

    @Override 
    public boolean isSingleton() { 
     return true; 
    } 

    public static class MetaFactory { 
     public <C> MockBeanFactory<C> createFactory(Class<C> mockBeanType) { 
      MockBeanFactory<C> factory = new MockBeanFactory<C>(); 
      factory.mockBeanType = mockBeanType; 
      return factory; 
     } 
    } 
} 

そしてSpringコンテキストXMLファイルで、あなたは単に特定のBean型ファクトリを生成するメタファクトリを作成することができます:ここで

は私のJMockitモックビーンファクトリのJavaです

<bean id="metaFactory" class="com.stackoverflow.MockBeanFactory$MetaFactory"/> 

<bean factory-bean="metaFactory" factory-method="createFactory"> 
    <constructor-arg name="mockBeanType" value="com.stackoverflow.YourService"/> 
</bean> 

元アスカーの状況のた​​め、この作業を行うには、serviceFactoryBeanのためのラッパー/アダプタにFactoryBeansを作るために微調整することができます

public class FancyFactoryAdapter<C> implements FactoryBean<C> { 

    private Class<C> clientClass; 
    private FancyFactory serviceFactoryBean; 

    protected FancyFactoryAdapter(){} 

    @Override 
    public C getObject() throws Exception { 
     return serviceFactoryBean.buildService(clientClass); 
    } 

    @Override 
    public Class<C> getObjectType() { 
     return clientClass; 
    } 

    @Override 
    public boolean isSingleton() { 
     return true; 
    } 

    public static class MetaFactory { 

     @Autowired FancyFactory serviceFactoryBean; 

     public <C> FancyFactoryAdapter<C> createFactory(Class<C> clientClass) { 
      FancyFactoryAdapter<C> factory = new FancyFactoryAdapter<C>(); 
      factory.clientClass = clientClass; 
      factory.serviceFactoryBean = serviceFactoryBean; 
      return factory; 
     } 
    } 
} 

その後XMLに(userServiceFactory idとuserService豆IDが@Qualifier注釈で動作するようにのみ必要です注意してください):

<bean id="metaFactory" class="com.stackoverflow.FancyFactoryAdapter$MetaFactory"/> 

<bean id="userServiceFactory" factory-bean="metaFactory" factory-method="createFactory"> 
    <constructor-arg name="clientClass" value="com.captain.services.UserServiceInterface"/> 
</bean> 

<bean id="userService" factory-bean="userServiceFactory"/> 

<bean id="scoreServiceFactory" factory-bean="metaFactory" factory-method="createFactory"> 
    <constructor-arg name="clientClass" value="com.captain.services.ScoreServiceInterface"/> 
</bean> 

<bean id="scoreService" factory-bean="scoreServiceFactory"/> 

そして、それはそれだ、ただ一つの小さなJavaクラスとのsmidgeボイラープレートの構成とカスタムBeanファクトリは、すべてのBeanを作成し、Springがそれらを正常に解決できるようにします。

+2

これは主にこれがJavaであり、したがってFactoryFactoryオブジェクトが常に正しいので、これをupvotingしています! –

関連する問題