Search code examples
javaannotationsaop

Non-Spring lightweight AOP without xml config for weaving annotations to methods


Need to run before and after methods on some annotations.

Not using spring, no xml. Is it possible to have some kind of AOP engine that I set from main() so that it can be invoked whenever needed? It's also OK for me to put in a method to manually call an evaluation method.

Example:

public void doThis(@RequiredSecurityRole("admin") user){
    doAOPStuff();
}

before() get from the db and check if user is admin, throws Exception if it's not admin.

after() log into db the action.

How to achieve this?


Solution

  • You can do this yourself using the java.lang.reflex.Proxy class. It does require that the code you're proxying be defined in an interface.

    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class DoItYourAop {
    
      public static void main(String[] args) {
        SaysHello saysHello = new SaysHelloImpl();
        InvocationHandler logger = new LoggingProxy(saysHello);
        SaysHello proxy = (SaysHello) Proxy.newProxyInstance(SaysHello.class.getClassLoader(),
            new Class[]{SaysHello.class}, logger);
        proxy.sayHello();
      }
    
      public interface SaysHello {
    
        void sayHello();
    
        void sayGoodbye();
      }
    
      public static class SaysHelloImpl implements SaysHello {
        @Log
        @Override
        public void sayHello() {
          System.out.println("Says Hello");
        }
    
        @Override
        public void sayGoodbye() {
          System.out.println("Says Goodbye");
        }
      }
    
      @Retention(RUNTIME)
      @interface Log {
      }
    
      public static class LoggingProxy implements InvocationHandler {
    
        private final Object proxied;
    
        public LoggingProxy(Object proxied) {
          this.proxied = proxied;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          Method proxiedMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
          boolean log = proxiedMethod.isAnnotationPresent(Log.class);
          if (log) {
            System.out.println("Before");
          }
    
          Object result = method.invoke(proxied, args);
    
          if (log) {
            System.out.println("After");
          }
    
          return result;
        }
      }
    }