Search code examples
javaspringspring-aopspring-aspects

Spring 4 method interceptor and use custom implementation


I have following classes and interface

interface abc {
 public A do();
}

package x;
public Impl1 implements abc{
  public A do(){
  }
}

package y;
public Impl2 implements abc{
  public A do(){
  }
}

I don't have the source code of Impl1 or Impl2. But would like to intercept any call to do() method and use my own implementation. Also based on certain condition may call the actual do() implementation, other cases it will not be delegated to the original implementations.

Could you please let me know whether this is achievable. If yes how to implement this?

I am using Spring 4 and JDK 7.


Solution

  • I will provide a stand-alone AspectJ solution, but in spring AOP it would the same way, only the aspect and your target classes need to be Spring beans/components, so don't forget annotations like @Component, as shown by Ian Mc.

    Helper class + interface + implementations:

    package de.scrum_master.app;
    
    public class A {
      private String name;
    
      public A(String name) {
        this.name = name;
      }
    
      @Override
      public String toString() {
        return "A [name=" + name + "]";
      }
    }
    
    package de.scrum_master.app;
    
    public interface MyInterface {
      public A doSomething();
    }
    
    package de.scrum_master.app;
    
    public class FirstImpl implements MyInterface {
      @Override
      public A doSomething() {
        return new A("First");
      }
    }
    
    package de.scrum_master.app;
    
    public class SecondImpl implements MyInterface {
      @Override
      public A doSomething() {
        return new A("Second");
      }
    }
    

    Driver application:

    package de.scrum_master.app;
    
    public class Application {
      private static MyInterface myInterface;
    
      public static void main(String[] args) {
        myInterface = new FirstImpl();
        for (int i = 0; i < 5; i++) {
          System.out.println(myInterface.doSomething());
        }
    
        myInterface = new SecondImpl();
        for (int i = 0; i < 5; i++) {
          System.out.println(myInterface.doSomething());
        }
      }
    }
    

    Console log without aspect:

    A [name=First]
    A [name=First]
    A [name=First]
    A [name=First]
    A [name=First]
    A [name=Second]
    A [name=Second]
    A [name=Second]
    A [name=Second]
    A [name=Second]
    

    So far, so boring.

    Aspect:

    Now let us implement a stupid little aspect randomly deciding about method execution vs. skipping and providing another return value instead (because I do not know the real condition which would cause you to skip method execution):

    package de.scrum_master.aspect;
    
    import java.util.Random;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    
    import de.scrum_master.app.A;
    
    @Aspect
    public class MyAspect {
      private static Random random = new Random();
    
      @Around("execution(A de.scrum_master.app.MyInterface.*(..))")
      public A interceptCalls(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        if (random.nextBoolean())
          return (A) thisJoinPoint.proceed();
        else
          return new A("Aspect"); 
      }
    }
    

    Console log with active aspect:

    A [name=Aspect]
    A [name=First]
    A [name=Aspect]
    A [name=Aspect]
    A [name=First]
    A [name=Aspect]
    A [name=Second]
    A [name=Second]
    A [name=Aspect]
    A [name=Second]