在 Play Framework 中根据参数注入依赖
Inject dependency based on parameter in Play Framework
我在数据库中跟踪 consumerId
的服务提供商列表。
我有一个 SchedulerActor
为 consumerId
启用的每个服务提供商创建 TaskActor
。
我想为列表中的每个服务提供商注入 ServiceProvider
到 TaskActor
的实现。 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);
}
}
}
- 为了实现这一目标,
getServiceProviderFromStr(String)
里面有什么?
- 我不想将
switch
或 if else
语句添加到 return 所需的实现。
- 我可以在运行时提供提供程序绑定,将字符串传递给提供程序对象吗?
如果你不想使用 switch
或 if else
,我认为你必须使用反射来完成,其中 serviceProviderStr
是 class 名称.
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();
获得 class 名称。
使用 Guice AssistedInject, namely the FactoryModuleBuilder 向 TaskActor 构造函数添加服务名称的字符串参数,并使用此参数注入带有 @Named 注释的 ServiceProvider:
在模块中定义一个工厂接口:
public interface TaskActorFactory{
TaskActor create(String serviceName);
}
在模块 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));
}
实施 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)));
}
...
}
从 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));
}
...
}
}
另一种使用相同命名绑定而不是提议的@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);
我在数据库中跟踪 consumerId
的服务提供商列表。
我有一个 SchedulerActor
为 consumerId
启用的每个服务提供商创建 TaskActor
。
我想为列表中的每个服务提供商注入 ServiceProvider
到 TaskActor
的实现。 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);
}
}
}
- 为了实现这一目标,
getServiceProviderFromStr(String)
里面有什么? - 我不想将
switch
或if else
语句添加到 return 所需的实现。 - 我可以在运行时提供提供程序绑定,将字符串传递给提供程序对象吗?
如果你不想使用 switch
或 if else
,我认为你必须使用反射来完成,其中 serviceProviderStr
是 class 名称.
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();
获得 class 名称。
使用 Guice AssistedInject, namely the FactoryModuleBuilder 向 TaskActor 构造函数添加服务名称的字符串参数,并使用此参数注入带有 @Named 注释的 ServiceProvider:
在模块中定义一个工厂接口:
public interface TaskActorFactory{ TaskActor create(String serviceName); }
在模块 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)); }
实施 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))); } ... }
从 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)); } ... }
}
另一种使用相同命名绑定而不是提议的@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);