吉斯。如何在父class的字段中注入不同的对象?
Guice. How to inject different objects in a field of parent class?
我对 Guice 字段注入有疑问。考虑以下 classes.
的层次结构
abstract class Base{
@Inject
protected MyService myService;
}
class A extends Base{
@Inject
private AnotherService anotherService;
}
class B extends Base{
...
}
我想在运行时有两个不同的 MyService 实例 - 一个注入 class A 的所有对象,另一个注入 class B 的对象。
我知道如何使用构造函数注入实现该行为:
bind(MyService.class).annotatedWith(Names.named("forA")).to(MyServiceImpl.class).in(Singleton.class);
bind(MyService.class).annotatedWith(Names.named("forB")).to(MyServiceImpl.class).in(Singleton.class);
class A extends Base{
@Inject @Named(value = "forA")
public A(MyService service1, AnotherService service2) {
this.myService = service1;
this.anotherService = service2;
}
问题是迁移到构造函数注入会非常复杂,所以我想坚持使用字段注入。
是否可以像我想要的那样调整场注入?
我会使用 Guice
中的 Custom Injections 来实现这一点。
首先,我将创建一个注解来注解我要基于超级 class 类型注入的 MyService。
@BindingAnnotation
@interface MyServiceInject {
}
然后我会用注释来注释我的 MyService 字段。
static abstract class Base {
@MyServiceInject
protected MyService myService;
}
现在我们需要一个自定义 TypeListener
每次遇到注入点时都会调用它。
这就是我们创建它的方式。
static class ClassBasedMyServiceInjectionListener implements TypeListener {
@Override
public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> encounter) {
Class<?> clazz = typeLiteral.getRawType();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == MyService.class && field.isAnnotationPresent(MyServiceInject.class)) { //if type of field is MyService and it has MyServiceInject annotation
encounter.register(new ClassBasedMyServiceInjector<>(field,
typeLiteral.getType(),
encounter.getProvider(MyServiceA.class),
encounter.getProvider(MyServiceB.class)
)
); //Now register a MemberInjector for this encounter.
}
}
clazz = clazz.getSuperclass();
}
}
}
现在我们需要如下所示的 MemberInjector。
static class ClassBasedMyServiceInjector<T> implements MembersInjector<T> {
private final Field field;
private final Type superClassType;
private final Provider<MyServiceA> myServiceAProvider;
private final Provider<MyServiceB> myServiceBProvider;
ClassBasedMyServiceInjector(Field field, Type superClassType, Provider<MyServiceA> myServiceAProvider, Provider<MyServiceB> myServiceBProvider) {
this.field = field;
this.superClassType = superClassType;
this.myServiceAProvider = myServiceAProvider;
this.myServiceBProvider = myServiceBProvider;
field.setAccessible(true);
}
public void injectMembers(T t) { //this will be called when guice wants to inject members
try {
if (superClassType == A.class) {//if super class is of type A
field.set(t, myServiceAProvider.get()); //inject MyServiceA
} else if (superClassType == B.class) { //if super class is of type B
field.set(t, myServiceBProvider.get()); //inject MyServiceB
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
最后我会像这样在我们模块的配置方法中绑定我们的自定义 TypeListener
。
@Override
protected void configure() {
bindListener(Matchers.any(), new ClassBasedMyServiceInjectionListener());
}
希望对您有所帮助。
我对 Guice 字段注入有疑问。考虑以下 classes.
的层次结构abstract class Base{
@Inject
protected MyService myService;
}
class A extends Base{
@Inject
private AnotherService anotherService;
}
class B extends Base{
...
}
我想在运行时有两个不同的 MyService 实例 - 一个注入 class A 的所有对象,另一个注入 class B 的对象。 我知道如何使用构造函数注入实现该行为:
bind(MyService.class).annotatedWith(Names.named("forA")).to(MyServiceImpl.class).in(Singleton.class);
bind(MyService.class).annotatedWith(Names.named("forB")).to(MyServiceImpl.class).in(Singleton.class);
class A extends Base{
@Inject @Named(value = "forA")
public A(MyService service1, AnotherService service2) {
this.myService = service1;
this.anotherService = service2;
}
问题是迁移到构造函数注入会非常复杂,所以我想坚持使用字段注入。 是否可以像我想要的那样调整场注入?
我会使用 Guice
中的 Custom Injections 来实现这一点。
首先,我将创建一个注解来注解我要基于超级 class 类型注入的 MyService。
@BindingAnnotation
@interface MyServiceInject {
}
然后我会用注释来注释我的 MyService 字段。
static abstract class Base {
@MyServiceInject
protected MyService myService;
}
现在我们需要一个自定义 TypeListener
每次遇到注入点时都会调用它。
这就是我们创建它的方式。
static class ClassBasedMyServiceInjectionListener implements TypeListener {
@Override
public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> encounter) {
Class<?> clazz = typeLiteral.getRawType();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == MyService.class && field.isAnnotationPresent(MyServiceInject.class)) { //if type of field is MyService and it has MyServiceInject annotation
encounter.register(new ClassBasedMyServiceInjector<>(field,
typeLiteral.getType(),
encounter.getProvider(MyServiceA.class),
encounter.getProvider(MyServiceB.class)
)
); //Now register a MemberInjector for this encounter.
}
}
clazz = clazz.getSuperclass();
}
}
}
现在我们需要如下所示的 MemberInjector。
static class ClassBasedMyServiceInjector<T> implements MembersInjector<T> {
private final Field field;
private final Type superClassType;
private final Provider<MyServiceA> myServiceAProvider;
private final Provider<MyServiceB> myServiceBProvider;
ClassBasedMyServiceInjector(Field field, Type superClassType, Provider<MyServiceA> myServiceAProvider, Provider<MyServiceB> myServiceBProvider) {
this.field = field;
this.superClassType = superClassType;
this.myServiceAProvider = myServiceAProvider;
this.myServiceBProvider = myServiceBProvider;
field.setAccessible(true);
}
public void injectMembers(T t) { //this will be called when guice wants to inject members
try {
if (superClassType == A.class) {//if super class is of type A
field.set(t, myServiceAProvider.get()); //inject MyServiceA
} else if (superClassType == B.class) { //if super class is of type B
field.set(t, myServiceBProvider.get()); //inject MyServiceB
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
最后我会像这样在我们模块的配置方法中绑定我们的自定义 TypeListener
。
@Override
protected void configure() {
bindListener(Matchers.any(), new ClassBasedMyServiceInjectionListener());
}
希望对您有所帮助。