10

サービス インターフェイスServiceInterfaceとそれを実装するいくつかのコンポーネントを含む次の構造があるProductAServiceとします。また、現在 ProductA または ProductB を処理していることを示す修飾プロパティを持つ BeanProductBServiceもあります。RequestContext次に、オートワイヤリングまたはその他の注釈を使用して、正しい実装 (ProductAService または ProductBService) を必要とするサービスに自動的に挿入する方法 (ServiceThatNeedsServiceInterface以下)。

public interface ServiceInterface {
  void someMethod();
}

@Component(name="ProductAService")
public class ProductAService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
  }
}

@Component(name="ProductBService")
public class ProductBService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
  }
}

@Component
public class ServiceThatNeedsServiceInterface {

  // What to do here???
  @Autowired
  ServiceInterface service;

  public void useService() {
    service.someMethod();
  }
}

@Component
@Scope( value = WebApplicationContext.SCOPE_REQUEST )
public class RequestContext {
  String getSomeQualifierProperty();
}
4

6 に答える 6

11

Spring Sourceは、バージョン1.1.4でServiceLocatorFactoryBeanを作成したときに、問題を参照していました。これを使用するには、以下のようなインターフェースを追加する必要があります。

public interface ServiceLocator {
    //ServiceInterface service name is the one 
      //set by @Component
    public ServiceInterface lookup(String serviceName);
}

次のスニペットをapplicationContext.xmlに追加する必要があります

<bean id="serviceLocatorFactoryBean"
    class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
    <property name="serviceLocatorInterface"
              value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>

これで、ServiceThatNeedsServiceInterfaceは次のようになります。

@Component
public class ServiceThatNeedsServiceInterface {
    // What to do here???
    //  @Autowired
    //  ServiceInterface service;

    /*
     * ServiceLocator lookup returns the desired implementation
     * (ProductAService or ProductBService) 
     */ 
 @Autowired
     private ServiceLocator serviceLocatorFactoryBean;

     //Let’s assume we got this from the web request 
     public RequestContext context;

     public void useService() {
        ServiceInterface service =  
        serviceLocatorFactoryBean.lookup(context.getQualifier());
        service.someMethod();         
      }
}

ServiceLocatorFactoryBeanは、RequestContext修飾子に基づいて目的のサービスを返します。Springアノテーションを除けば、コードはSpringに依存していません。上記に対して以下の単体テストを実行しました

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" })
public class ServiceThatNeedsServiceInterfaceTest {

    @Autowired
    ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface;

    @Test
    public void testUseService() {
    //As we are not running from a web container
    //so we set the context directly to the service 
        RequestContext context = new RequestContext();
        context.setQualifier("ProductAService");
        serviceThatNeedsServiceInterface.context = context;
        serviceThatNeedsServiceInterface.useService();

        context.setQualifier("ProductBService");
        serviceThatNeedsServiceInterface.context = context;
        serviceThatNeedsServiceInterface.useService();
    }

}

コンソールに
Hello、A Service
Hello、 BServiceが表示されます

警告の言葉。APIドキュメントには、
「このようなサービスロケーターは、通常、プロトタイプBeanに使用されます。つまり、呼び出しごとに新しいインスタンスを返すことになっているファクトリメソッドに使用されます。シングルトンBeanの場合、ターゲットBeanの直接セッターまたはコンストラクターインジェクションが推奨されます。 」</p>

なぜこれが問題になるのか理解できません。私のコードでは、serviceThatNeedsServiceInterface.useService();への2つのシーケンス呼び出しで同じサービスを返します。

私の例のソースコードはGitHubにあります

于 2013-03-04T14:17:48.050 に答える
3

あなたが探しているようなことをするために私が考えることができる唯一の方法は、RequestContextプロパティに基づいて適切な実装を返すFactoryBeanのようなものを作成することです。これが私が一緒に叩いたもので、あなたが望む振る舞いをしています:

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.http.HttpServletRequest;

public class InjectionQualifiedByProperty {

    @Controller
    @Scope(WebApplicationContext.SCOPE_REQUEST)
    public static class DynamicallyInjectedController {
        @Autowired
        @Qualifier("picker")
        Dependency dependency;

        @RequestMapping(value = "/sayHi", method = RequestMethod.GET)
        @ResponseBody
        public String sayHi() {
            return dependency.sayHi();
        }
    }

    public interface Dependency {
        String sayHi();
    }

    @Configuration
    public static class Beans {
        @Bean
        @Scope(WebApplicationContext.SCOPE_REQUEST)
        @Qualifier("picker")
        FactoryBean<Dependency> dependencyPicker(final RequestContext requestContext,
                                                 final BobDependency bob, final FredDependency fred) {
            return new FactoryBean<Dependency>() {
                @Override
                public Dependency getObject() throws Exception {
                    if ("bob".equals(requestContext.getQualifierProperty())) {
                        return bob;
                    } else {
                        return fred;
                    }
                }

                @Override
                public Class<?> getObjectType() {
                    return Dependency.class;
                }

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

    @Component
    public static class BobDependency implements Dependency {
        @Override
        public String sayHi() {
            return "Hi, I'm Bob";
        }
    }

    @Component
    public static class FredDependency implements Dependency {
        @Override
        public String sayHi() {
            return "I'm not Bob";
        }
    }

    @Component
    @Scope(WebApplicationContext.SCOPE_REQUEST)
    public static class RequestContext {
        @Autowired HttpServletRequest request;

        String getQualifierProperty() {
            return request.getParameter("which");
        }
    }
}

このコードを使用した実用的な例をGithubに配置しました。次のコマンドでクローンを作成して実行できます。

git clone git://github.com/zzantozz/testbed tmp
cd tmp/spring-mvc
mvn jetty:run

次に、にアクセスhttp://localhost:8080/dynamicallyInjectedして、一方の依存関係の結果をhttp://localhost:8080/dynamicallyInjected?which=bob確認し、もう一方の依存関係を確認します。

于 2013-03-02T05:32:36.830 に答える
1

私は、Spring にカスタム サービスがあることを伝えるアノテーションを見逃していると思います。したがって、解決策は、クラス名の前にこの注釈を追加することです。

@Service("ProductAService")
public class ProductAService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
  }
}

@Service("ProductBService")
public class ProductBService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
  }
}

そして、それを自動配線できますが、特定のサービスを使用するには、次のように注釈 Qualifier() を追加する必要があります。

  @Autowired
  @Qualifier("ProductBService") // or ProductAService
  ServiceInterface service;

または、注釈 Qualifier("name of your bean") だけを追加する必要があるかもしれません:)

于 2013-02-21T01:11:37.127 に答える
1

これはあなたを助けるかもしれません:

使用する

AutowireCapeableBeanFactory.autowireBean(Object existingBean)  

また

AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)

AutowireCapeableBeanFactory.autowireBean(Object existingBean) AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)

于 2013-03-02T10:07:10.983 に答える
0

@Qualifier アノテーションをエイリアスと組み合わせて使用​​できます。プロパティに基づいて Bean をロードするために使用する方法の例を参照してください。このアプローチを変更して、リクエストコンテキストのプロパティ/エイリアスを変更できます...

于 2013-03-02T11:59:31.430 に答える
0

注釈を使用してこれを行うことはできないと思います。その理由は、実行時に動的な Bean (おそらく A サービスまたは B サービス) が必要なため、Bean が任意の場所で使用される前に @Autowire が配線されます。1 つの解決策は、必要なときにコンテキストから Bean を取得することです。

    @Component
public class ServiceThatNeedsServiceInterface {


  ServiceInterface service;

  public void useService() {
     if(something is something){
        service = applicationContext.getBean("Abean", ServiceInterface.class);
     }else{
        service = applicationContext.getBean("Bbean", ServiceInterface.class);
     }
    service.someMethod();
  }
}

別の関数としてクラスのどこかにelseロジックを置くことができます:

public void useService() {
        service = findService();
        service.someMethod();
      }

public ServiceInterface findService() {
         if(something is something){
            return applicationContext.getBean("Abean", ServiceInterface.class);
         }else{
            return applicationContext.getBean("Bbean", ServiceInterface.class);
         }

      }

これは動的であり、これはあなたが望むものかもしれません。

于 2013-03-01T00:28:30.757 に答える