2016-10-20 2 views
3

Я отслеживаю список поставщиков услуг в базе данных для consumerId.Инъекционная зависимость на основе параметра в Play Framework

У меня есть SchedulerActor, который создает TaskActor для каждого поставщика услуг, включенного для consumerId.

Я хочу ввести реализацию ServiceProvider в TaskActor s для каждого поставщика услуг в списке. Могут быть разные реализации ServiceProvider.

interface ServiceProvider { 
    void processRequest(Request request); 
} 

class SchedulerActor { 
    @Override 
    public void onReceive(Object consumerId) throws Exception { 
     List<String> serviceProvidersList = getFromDatabase((String) consumerId); 
     for (String serviceProviderStr : serviceProvidersList) { 

       //serviceProviderStr could be "ServiceProvider1" or "ServiceProvider2" 
       ServiceProvider serviceProvider = getServiceProviderFromStr(serviceProviderStr); 

       ActorRef r = getContext().actorOf(Props.create(TaskActor.class, serviceProvider)); 
       getContext().watch(r); 
       routees.add(new ActorRefRoutee(r)); 

     } 
     router = new Router(new BroadcastRoutingLogic(), routees); 
     router.route(message, getSelf()); 
    } 
} 

class TaskActor { 
    private final ServiceProvider serviceProvider; 
    public TaskActor(ServiceProvider serviceProvider) { 
     this.serviceProvider = serviceProvider; 
    } 

    @Override 
    public void onReceive(Object message) { 
     if (message instanceof Request) { 
      serviceProvider.processRequest((Request)message); 
     } 
    } 
} 
  1. То, что происходит внутри getServiceProviderFromStr(String) для достижения этой цели?
  2. Я не хочу добавлять switch или if else операторы, чтобы вернуть требуемую реализацию.
  3. Могу ли я установить привязки поставщиков во время выполнения, передав строку объекту поставщика?

ответ

1

другой способ использования одноименных связывания вместо предложил @Assisted

@Inject 
public Injector injector; 
... 
ServiceProvider s = injector.getInstance(Key.get(Parent.class, Names.named(serviceName))); 

где классы связаны в Configure():

bind(ServiceProvider.class).annotatedWith(Names.named("Name1")).to(Child1.class); 
bind(ServiceProvider.class).annotatedWith(Names.named("Name2")).to(Child2.class); 
+0

спасибо! это работало как шарм! –

0

Если вы не хотите использовать switch из if else, я думал, что вы должны использовать отражение, чтобы сделать это, где serviceProviderStr это имя класса.

private ServiceProvider getServiceProviderFromStr(String serviceProviderClassName){ 
     try { 
      Class<?> clazz = Class.forName(serviceProviderClassName); 
      if(!ServiceProvider.class.isAssignableFrom(clazz)){ 
       throw new IllegalArgumentException("Argument is not a ServiceProvider class."); 
      } 
      Class<? extends ServiceProvider> serviceProviderClass = (Class<? extends ServiceProvider>) clazz; 
      return serviceProviderClass.newInstance(); 
     } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 
      e.printStackTrace(); 
     } 

     return null; 
    } 

Помните, что вы можете получить имя класса, позвонив по номеру AnyClass.class.getName();.

1

Использование Guice AssistedInject, а именно FactoryModuleBuilder добавить в конструктор TaskActor параметра типа String для имени службы и использовать этот параметр для инъекций из ServiceProvider с @Named аннотации:

  1. Определением в модуле на заводе интерфейс:

    public interface TaskActorFactory{ 
        TaskActor create(String serviceName); 
    } 
    
  2. В модуле настроить метод установки фабрики и регистрации служб связывания:

    protected void configure() { 
        bind(ServiceProvider.class).annotatedWith(Names.named("serviceA")).to(ServiceA.class); 
        bind(ServiceProvider.class).annotatedWith(Names.named("serviceB")).to(ServiceB.class); 
        bind(ServiceProvider.class).annotatedWith(Names.named("serviceC")).to(ServiceC.class); 
        install(new FactoryModuleBuilder() 
         .implement(TaskActor.class, TaskActor.class) 
         .build(TaskActorFactory.class)); 
    } 
    
  3. Реализовать TaskActor:

    @Inject 
    class TaskActor extends UntypedActor { 
    
        public static Props props(String serviceName) { 
         return Props.create(TaskActor.class,()->new TaskActor(serviceName)); 
        } 
    
        private final ServiceProvider serviceProvider; 
    
        public TaskActor(Injector injector, @Assisted String serviceName) { 
         serviceProvider = injector.getInstance(Key.get(ServiceProvider.class, Names.named(serviceName))); 
        } 
        ... 
    } 
    
  4. Создать TaskActor из SchedulerActor

    @Override 
    public void onReceive(Object consumerId) throws Exception { 
        List<String> serviceProvidersList = getFromDatabase((String) consumerId); 
        for (String serviceProviderStr : serviceProvidersList) { 
         ActorRef r = getContext().actorOf(TaskActor.props(serviceProviderStr)); 
         getContext().watch(r); 
         routees.add(new ActorRefRoutee(r)); 
        } 
        ... 
    } 
    

    }

+0

Я-то здесь отсутствует. Как я могу использовать 'ServiceProviderFactory' для получения требуемой реализации без необходимости вызова' createServiceA() 'или' createServiceB() 'или' createServiceC() 'конкретно? –

+0

Решение не подходит для динамического внедрения поставщика услуг, поэтому я переписал весь ответ.Думаю, теперь вся картина более ясна (также для меня). – asch

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