简体   繁体   中英

How to inject two instances of two different classes which implement the same interface?

When dealing with CDI in java, I want to inject two instances of two different classes, implementing the same interface.

As I understand, I can inject an instance of a class which does not implement an interface, eg:

class MyClass {
  // ...
}

class XY {
  @Inject MyClass myClass;
}

When my class implements an interface I have to declare the member by the interface name (and specify the concrete implementation):

class MyClass implements MyInterface {
  // ...
}

class XY {
  @Inject MyInterface myClass;
}

But as soon as I want to inject different implementations, I get the "Api type [...] is not found with the qualifiers" exception:

class MyClassOne implements MyInterface {
  // ...
}

class MyClassTwo implements MyInterface {
  // ...
}

class XY {
  @Inject MyClassOne myClassOne;
  @Inject MyClassTwo myClassTwo;
}

I appreciate any ideas what to try or where to continue reading (the obvious keywords for a search on this topic give very unspecific results). Thanks in advance!

In order to inject different instances, there are different ways to construct and inject beans.

Approach 1 :

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface ClassifierOne {
}

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface ClassifierTwo {
}

These qualifiers can be used in your class part of construction parameter injection or setter injection level.

@ClassifierOne
public class MyClassOne implements MyInterface {
  // ...
}

@ClassifierTwo
public class MyClassTwo implements MyInterface {
 // ...
}

public class XY {
   private final MyInterface myClassOne;
   private final MyInterface myClassTwo;

   @Inject
   public XY ( @ClassifierOne MyInterface myClassOne, @ClassifierTwo MyInterface myClassTwo ) {
         this.myClassOne = myClassOne;
         this.myClassTwo = myClassTwo;
   }
}

Approach 2 : Use of @Produces

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public @interface MyClassType {
    ClassImplName value();
}

public enum ClassImplName {
    CLASS_ONE(MyClassOne.class),
    CLASS_TWO(MyClassTwo.class);

    private Class<? extends MyInterface> classType;

    private ClassImplName(Class<? extends MyInterface> clazz) {
        this.classType = clazz;
    }

    public Class<? extends MyInterface> getClassType(){
        return classType;
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface ClassType {
    ClassImplName value();
}

Above custom qualifiers will allow you to chose type of implementation by removing abibuaty in producer method. And, you can use below mentioned MyClassFactory to produce interfaces. This mechanism would be efficient as it uses InjectionPoint where the bean is injected.

public class MyInterfaceFactory {

    @Produces
    @MyClassType
    public MyInterface createMyClasses(@Any Instance<MyInterface> instance, InjectionPoint injectionPoint) {
        Annotated annotated = injectionPoint.getAnnotated();
        ClassType classTypeAnnotation = annotated.getAnnotation(ClassType.class);
        Class<? extends MyInterface> classType = classTypeAnnotation.value().getClassType();
        return instance.select(classType).get();
    }
}

Finally, you can use these generated instances in your class.

public class XY {

    @Inject
    @ClassType(ClassImplName.CLASS_ONE)
    @MyClassType
    private MyInterface myClassOne;

    @Inject
    @ClassType(ClassImplName.CLASS_TWO)
    @MyClassType
    private MyInterface myClassTwo;

    // Other methods using injected beans ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM