简体   繁体   中英

Is it possible to get a class by qualifier annotation using reflection?

I have an interface with many possible implementations. The right implementation should be chosen at the runtime. And so Reflection sounds to be the solution.

I have annotated these classes by a qualifier that has as argument an enumeration.

So, is it possible to get at runtime using reflection the right implementatoin class by passing the right enumeration to the annotation?

But, reflection is not mandatory if there is another way..

First, here it is the enumeration :

public enum CATEGORY {
 A,B,C;
}

Then, here it the interface :

public interface GenericI{
   void method1(CATEGORY arg);
   // some other methods
}

And now, here there are the annotated implementations :

@MyAnnotation(CATEGORY.A)
public class impl1 implements GenericI{
    void method1(CATEGORY arg){
       // some work here
    }
}

@MyAnnotation(CATEGORY.B)
public class impl2 implements GenericI{
    void method1(CATEGORY arg){
       // some work here
    }    
}

Finally, the proxy that at a way, select dynamically the right implementation using annotation and enumeration (probably it shouldn't implement GenericI ??):

public class MyProxy implements GenericI {

  // Here we must be able to select the right implementation
}

Reflexion is an answer, but you need to get all the classes from the classpath, and examinate it to find the implementation of your interface.

You can use this reflection library and get all the implementations like this (if your interface name is MyInterface):

Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

for (Class<T extends MyInterface> c : classes) {

     check if c is the rigth implementation!.
}

If you don't want to use a external library, you can use the Java Reflection API, and scan all packages, somethis like (see this answers to use instrumentation):

Instrumentation inst = InstrumentHook.getInstrumentation();
for (Class<?> c: inst.getAllLoadedClasses()) {
    if (MyInterface.class.isAssignableFrom(c)) {
         check if c is the rigth implementation!.
    }
}

The first option allow you to save the Reflections object as a xml, so the component scan is saved and it's done only one time.

To check if the clazz have a Qualifier you can use:

if (c.isAnnotationPresent(Qualifier.class)) {
     bingo!.
}

or is a property of the annotation:

if (c.isAnnotationPresent(Qualifier.class)) {
     Qualifier q = c.getAnnotation(Qualifier.class);
     if (q.theRight()) {
         bingo!
     }
}

I recommend you to see if the FactoryProblem is applycable to your problem, choose always Factory instead of Reflection .

An example "proxy":

public class MyProxy implements GenericI {

    Map<Category, GenericI> generics;

    public MyProxy() {
        Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
        Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
        generics = new HashMap<Category, GenericI>();
        for (Class<T extends MyInterface> c : classes) {

            map.put(c.getAnnotation(MyAnnotation.class).value(), c.newInstance());
        }       
    }

    void method1(CATEGORY arg){

        map.get(arg).method1(arg);
    }   

}

This is extremely heavy and overcomplicated, if you use this, please add extensive test, and make MyProxy a Singleton.

If you use a IOC framework:

@Component
public class MyProxy implements GenericI {

    @Autoriwed // If spring
    List<GenericI> generics; 

    @Inject @Any // If CDI
    private Instance<GenericI> services;

    Map<Category, GenericI> generics;

    @PostConstruct
    void makeMap() {
        generics = new HashMap<>();
        for (GenericI component : generics) {
            generics.put(
                component.getClass().getAnnotation(MyAnnotation.class).value(),
                component);
        }
    }

    void method1(CATEGORY arg){

        map.get(arg).method1(arg);
    }   

}

I assume you don't know al possible subclasses.

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