【问题标题】:Inject dependency based on parameter in Play Framework根据 Play Framework 中的参数注入依赖项
【发布时间】:2016-10-20 00:43:58
【问题描述】:

我在数据库中跟踪consumerId 的服务提供商列表。

我有一个SchedulerActor,它为启用consumerId 的每个服务提供商创建TaskActor

我想为列表中的每个服务提供者注入一个ServiceProviderTaskActors 的实现。 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. 我不想添加switchif else 语句来返回所需的实现。
  3. 我能否在运行时提供提供程序绑定,将字符串传递给提供程序对象?

【问题讨论】:

    标签: java playframework dependency-injection akka guice


    【解决方案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);
    

    【讨论】:

      【解决方案2】:

      使用Guice AssistedInject,即FactoryModuleBuilder向TaskActor构造函数添加服务名称的String参数,并使用该参数注入带有@Named注解的ServiceProvider:

      1. 在模块中定义一个工厂接口:

        public interface TaskActorFactory{
            TaskActor create(String serviceName);
        }
        
      2. 在模块configure方法中安装工厂并注册服务绑定:

        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. 从 SchedulerActor 创建 TaskActor

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

        }

      【讨论】:

      • 我在这里遗漏了一些东西。如何使用ServiceProviderFactory 来获得我需要的实现,而无需专门调用createServiceA()createServiceB()createServiceC()
      • 该解决方案不适合动态服务提供者注入,所以我重写了整个答案。我认为现在整个画面更加清晰(对我来说也是如此)。
      【解决方案3】:

      如果您不想使用if elseswitch,我认为您必须使用反射来做到这一点,其中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();来获取类名。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-15
        • 1970-01-01
        • 2016-06-19
        相关资源
        最近更新 更多