2013-02-21 3 views
10

Скажет, у меня есть следующая структура с интерфейсом сервиса ServiceInterface и несколько компонентов, реализующим его: ProductAService и ProductBService У меня также есть RequestContext боба, который имеет квалификационное свойство, которое говорит, что мы сказать в настоящее время обрабатывает ProductA или ProductB. Как затем автоматически можно добавить автоуведомление или другую аннотацию правильной реализации (ProductAService или ProductBService) в некоторую услугу, которая ему нужна (ServiceThatNeedsServiceInterface ниже).Пользовательского Autowire кандидатских бобов в Spring 3

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(); 
} 

ответ

10

Источник Spring ссылался на вашу проблему, когда они создали ServiceLocatorFactoryBean еще в версии 1.1.4. Для того, чтобы использовать его необходимо добавить интерфейс, аналогичный приведенному ниже:

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. Помимо весенних аннотаций ваш код не зависит от весны. я выполнил следующий тест блока для вышеприведенного

@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(); 
    } 

} 

Консоль покажет
Здравствуйте, службу
Здравствуйте, В службе

Слова предупреждения. В документации по API указывается, что
«Такие локаторы сервисов ... обычно будут использоваться для прототипов beans, то есть для заводских методов, которые должны возвращать новый экземпляр для каждого вызова ... Для однотонных bean-компонентов, прямой установщик или инсталляция конструктора целевого компонента предпочтительнее. "

Я не могу понять, почему это может вызвать проблему. В моем коде он возвращает одну и ту же услугу на двух последовательных вызовах serviceThatNeedsServiceInterface.useService();

Можно найти исходный код моего примера в GitHub

+0

Бинго! Это правильный ответ. Я не против, что крошечный бит XML-конфигурации. – Strelok

1

Я предполагаю, что вы пропустили аннотацию, которая сообщает весне, что у вас есть таможня. Так что ваше решение, чтобы добавить эту аннотацию перед именем класса:

@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 («название вашего боб») :)

+0

Я знаю, что я могу это сделать, но я должен был бы иметь все различные сервисы в виде отдельных полей класса, который использует его. Я хочу, чтобы свойство в RequestContext было фактором принятия решения, которое «продукт» вводит при автоподготовке. – Strelok

0

Я не думаю, что вы можете сделать это с помощью аннотаций, причина вам нужен боб является динамическим во время выполнения (возможно, служба или служба B), поэтому @Autowire w перед тем, как 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(); 
    } 
} 

Вы можете поместить это еще логику где-то в классе как отдельная функция:

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); 
     } 

     } 

Это динамичное и это может быть то, что вы хотите.

3

Единственный способ, с помощью которого я могу сделать что-то вроде того, что вы ищете, это создать нечто вроде FactoryBean, которое возвращает соответствующую реализацию на основе свойства RequestContext. Вот то, что я ударил вместе что поведение вы хотите:

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"); 
     } 
    } 
} 

Я поставил рабочий пример использования этого кода on 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 видеть другие.

0

Вы можете использовать аннотацию @Qualifier в сочетании с псевдонимами. См. Пример того, как он используется для загрузки компонента на основе свойства here. Вы можете изменить этот подход и изменить свойство/псевдоним в requestcontext ...

Смежные вопросы