Search code examples
javareflectionannotations

Invoke method before annotated another one


I'm trying to add functionality into an interface method signature by using annotations.

the idea is to invoke some another methods fore each annotated one.

for example if I have this method signature:

public interface IMyInterface{

    @Entity(visibileName = "Name")
    public TextField getName();
}

I need to invoke a method that print the string name before, after this method. also if their is any way to define a functionality to this method on run time.

I'm also opened to structural changes.


Solution

  • If what you want is annotating interface methods, than it is possible without AOP.
    Just use Dynamic Proxies!

    The base interface to implement a proxy is InvocationHandler

    InvocationHandler is the interface implemented by the invocation handler of a proxy instance.


    Follow in-code comments.

    static class MyInterfaceProxy implements InvocationHandler {
        private static final Map<String, Method> METHODS = new HashMap<>(16);
    
        static {
            // Scan each interface method for the specific annotation
            // and save each compatible method
            for (final Method m : IMyInterface.class.getDeclaredMethods()) {
                if (m.getAnnotation(YourAnnotation.class) != null) {
                    METHODS.put(m.getName(), m);
                }
            }
        }
    
        private final IMyInterface toBeProxied;
    
        private MyInterfaceProxy(final IMyInterface toBeProxied) {
            // Accept the real implementation to be proxied
            this.toBeProxied = toBeProxied;
        }
    
        @Override
        public Object invoke(
                final Object proxy,
                final Method method,
                final Object[] args) throws InvocationTargetException, IllegalAccessException {
            // A method on MyInterface has been called!
            // Check if we need to call it directly or if we need to
            // execute something else before!
            final Method found = METHODS.get(method.getName());
    
            if (found != null) {
                // The method exist in our to-be-proxied list
                // Execute something and the call it
                // ... some other things
                System.out.println("Something else");
            }
    
            // Invoke original method
            return method.invoke(toBeProxied, args);
        }
    }
    

    To use this InvocationHandler implementation you need a real instance of the object to be proxied.

    Say you have a Factory for MyInterface implementations

    MyInterface getMyInsterface() {
       ...
       final MyInterface instance = ...
    
       // Create the proxy and inject the real implementation
       final IMyInterface proxy = (IMyInterface) Proxy.newProxyInstance(
            MyInterfaceProxy.class.getClassLoader(),
            new Class[] {IMyInterface.class},
            new MyInterfaceProxy(instance) // Inject the real instance
        );
    
       // Return the proxy!
       return proxy;
    }