1

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.

Debopam
  • 3,198
  • 6
  • 41
  • 72

2 Answers2

3

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]
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Does myInterface = new FirstImpl(); code works with AspectJ or I have to create the proxy explicitly and use it? I tried this with SPring and it didn't work. I have added aspectjweaver (1.8.13) to classpath. DO I have to add some more classes? – Debopam Feb 12 '18 at 19:52
  • Nailed it. Had to add compile time weaver in pom.xml – Debopam Feb 12 '18 at 22:31
  • 1
    If you do everything right in Spring (make your aspect and target classes `@Component`s, configure wiring/injection correctly instead of directly instantiating beans etc.), AspectJ (CTW or LTW) should not be necessary for this case. I just did it with AspectJ because for me it is easier to set up than Spring and I can concentrate on answering the AOP question. – kriegaex Feb 13 '18 at 00:35
  • I also did it using AspectJ because Spring AOP can't handle objects which were created outside Spring's context i.e. myInterface = new FirstImpl(); will not work. For my requirement, we have to intercept classes created in that way as well. – Debopam Feb 13 '18 at 01:05
  • Ah, okay, you did not mention that requirement before. Anyway, my solution works for you, as you said, because I usually implement my examples using full AspectJ anyway. :-) – kriegaex Feb 13 '18 at 01:22
0

Your request can be done using Spring AOP, and more specifically using the @Around advice. The @Around advice, among other things, lets you either pass the call directly to the original implementation, or short-circuit the call and instead call your implementation. You need to provide the logic to choose one or the other.

The @Around method is passed a ProceedingJoinPoint. To call the original implementation, you use the 'proceed' method. If you want to short-circuit, then you do not call proceed; rather call your own method which creates an 'A' object.

The following code shows an @Aspect based class which demonstrates both techniques. You must inject your own implementation, so that you can create your own A object as required.

You should do a bit of reading on Spring AOP in general, and more specifically on Pointcuts (which are needed to intercept a call) and the @Around advice. Of note is that you can combine pointcuts, and use wildcards, so it is likely you can achieve what you want with a limited number of methods in your @Aspect class if you make your pointcut general enough to capture all the implementation's do methods.

Sample code showing both a pass through to the original implementation, and a short-circuit calling your own.

@Aspect
@Component
public class DoAspects {

   @Autowired
   @Qualifier("YourQualifier")
   private abc p3;

   @Around("execution(* pkg1.Pkg1AImpl.do())")
   public A p1(ProceedingJoinPoint  jp) throws Throwable {
     // Some logic determines to call the original implementation (i.e. proceed)
     A a = (A)jp.proceed();  // Let the other implementation create A
     return a;
   }

   @Around("execution(* pkg2.Pkg2AImpl.do())")
   public A p2(ProceedingJoinPoint jp) {
     // Some logic determines to short-circuit, and call own implementation
     A a = p3.do();  // You create A
     return a;
   }

}
Ian Mc
  • 5,656
  • 4
  • 18
  • 25
  • The OP asked if he can target the interface instead of its implementations. Your example shows the opposite. Maybe you want to change it so as to demonstrate how to match the interface in a single advice with some if-else stuff inside. You have a higher chance of your answer getting accepted that way and I do not snatch it away from you just by providing a similar code sample answering the question. :-) – kriegaex Feb 08 '18 at 01:45
  • No, please answer it correctly, and I will delete. Good catch. – Ian Mc Feb 08 '18 at 01:46
  • Don't delete it, we are just showing different options to do stuff related to around-aspects. – kriegaex Feb 08 '18 at 11:15