Java - Execute a class method with a specify annotation

℡╲_俬逩灬. 提交于 2021-02-08 11:23:20

问题


I have a android application, but it is not relevant.

I have a class called "Front controller" which will receive some message through it's constructor. The message, for brievity, could be an integer.

I want somewhere else to create a new controller which will execute a method based on the integer defined above

public class OtherController {

   @MessageId("100")
   public void doSomething(){
        //execute this code
   }


   @MessageId("101")
   public void doSomethingElse(){
        //code
   }
}

The front controller could be something like this:

public class FrontController {

    private int id;

    public FrontController(int id){
        this.id=id;
        executeProperControllerMethodBasedOnId();  
    }

    public void executeProperControllerMethodBasedOnId(){
        //code here
    }

    public int getId(){
        return id;
    }
}

So, if the Front Controller will receive the integer 100, it will execute the method annotated with @MessageId(100). The front controller don't know exactly the class where this method is.

The problem which I found is that I need to register somehow each controller class. I Spring I had @Component or @Controller for autoloading. After each controllers are register, I need to call the properly annotated method.

How to achieve this task? In Spring MVC, I had this system implemented, used to match the HTTP routes. How could I implement this in a plain java project?

Any suggestions?


回答1:


Thanks to Google Reflections (hope you can integrate this in your android project.)

    <dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections-maven</artifactId>
        <version>0.9.8</version>
    </dependency>

For optimisation I've added the requirement to also annotate the class with MessageType annotation and the classes should be in the same package (org.conffusion in my example):

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MessageType {
}

The OtherController looks like:

@MessageType
public class OtherController  {

    @MessageId(id=101)
    public void method1()
    {
        System.out.println("executing method1");
    }
    @MessageId(id=102)
    public void method2()
    {
        System.out.println("executing method2");
    }
}

The implementation will look like:

public void executeProperControllerMethodBasedOnId() {
    Set<Class<?>> classes = new org.reflections.Reflections("org.conffusion")
            .getTypesAnnotatedWith(MessageType.class);
    System.out.println("found classes " + classes.size());
    for (Class<?> c : classes) {
        for (Method m : c.getMethods()) {
            try {
                if (m.isAnnotationPresent(MessageId.class)) {
                    MessageId mid = m.getAnnotation(MessageId.class);
                        Object o = c.newInstance();
                    if (mid.id() == id)
                        m.invoke(o);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Maybe you can optimise and build a static hashmap containing already scanned message ids.




回答2:


You need to implement some of the work by yourself using reflection, I would recommend to prepare message handlers on initial phase in regards to performance. Also you possibly want to think about Singleton/Per Request controllers. Some of the ways to implement the solution:

interface MessageProcessor {
    void execute() throws Exception;
}

/* Holds single instance and method to invoke */
class SingletonProcessor implements MessageProcessor {

    private final Object instance;
    private final Method method;

    SingletonProcessor(Object instance, Method method) {
        this.instance = instance;
        this.method = method;
    }

    public void execute() throws Exception {
        method.invoke(instance);
    }
}

/* Create instance and invoke the method on execute */
class PerRequestProcessor implements MessageProcessor {

    private final Class clazz;
    private final Method method;

    PerRequestProcessor(Class clazz, Method method) {
        this.clazz = clazz;
        this.method = method;
    }

    public void execute() throws Exception {
        Object instance = clazz.newInstance();
        method.invoke(instance);
    }
}

/* Dummy controllers */
class PerRequestController {
    @MessageId(1)
    public void handleMessage1(){System.out.println(this + " - Message1");}
}

class SingletonController {
    @MessageId(2)
    public void handleMessage2(){System.out.println(this + " - Message2");}
}

class FrontController {

    private static final Map<Integer, MessageProcessor> processors = new HashMap<Integer, MessageProcessor>();

    static {
        try {
            // register your controllers
            // also you can scan for annotated controllers as suggested by Conffusion
            registerPerRequestController(PerRequestController.class);
            registerSingletonController(SingletonController.class);
        } catch (Exception e) {
            throw new ExceptionInInitializerError();
        }
    }

    private static void registerPerRequestController(Class aClass) {
        for (Method m : aClass.getMethods()) {
            if (m.isAnnotationPresent(MessageId.class)) {
                MessageId mid = m.getAnnotation(MessageId.class);
                processors.put(mid.value(), new PerRequestProcessor(aClass, m));
            }
        }
    }

    private static void registerSingletonController(Class aClass) throws Exception {
        for (Method m : aClass.getMethods()) {
            if (m.isAnnotationPresent(MessageId.class)) {
                MessageId mid = m.getAnnotation(MessageId.class);
                Object instance = aClass.newInstance();
                processors.put(mid.value(), new SingletonProcessor(instance, m));
            }
        }
    }

    /* To process the message you just need to look up processor and execute */
    public void processMessage(int id) throws Exception {
        if (processors.containsKey(id)) {
            processors.get(id).execute();
        } else {
            System.err.print("Processor not found for message " + id);
        }
    }    
}


来源:https://stackoverflow.com/questions/25054460/java-execute-a-class-method-with-a-specify-annotation

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!