Spring 4 метод перехватчик и использование пользовательской реализации

У меня есть следующие классы и интерфейс

interface abc {
 public A do();
}

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

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

У меня нет исходного кода Impl1 или Impl2. Но я хотел бы перехватить любой вызов метода do() и использовать мою собственную реализацию. Также на основании определенных условий может вызываться фактическая реализация do(), в других случаях она не будет делегирована исходным реализациям.

Не могли бы вы, пожалуйста, дайте мне знать, достижимо ли это. Если да, как это реализовать?

Я использую Spring 4 и JDK 7.

2 ответа

Решение

Я предоставлю автономное решение AspectJ, но в Spring AOP это будет так же, только аспект и ваши целевые классы должны быть компонентами / компонентами Spring, поэтому не забывайте такие аннотации, как @ComponentИан Мак.

Вспомогательный класс + интерфейс + реализации:

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");
  }
}

Приложение для водителя:

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());
    }
  }
}

Консольный журнал без аспекта:

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]

Так скучно.

аспект:

Теперь давайте реализуем глупый маленький аспект, который случайным образом решает вопрос о выполнении метода вместо пропуска и предоставляет вместо этого другое возвращаемое значение (потому что я не знаю реального условия, которое заставило бы вас пропустить выполнение метода):

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"); 
  }
}

Консольный журнал с активным аспектом:

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]

Ваш запрос может быть выполнен с помощью Spring AOP, а более конкретно, используя @Around совет. Совет @Around, помимо прочего, позволяет либо передать вызов непосредственно исходной реализации, либо замкнуть вызов и вместо этого вызвать вашу реализацию. Вы должны предоставить логику, чтобы выбрать один или другой.

Метод @Around передается в ProceedingJoinPoint. Чтобы вызвать оригинальную реализацию, вы используетеproceedметод. Если вы хотите короткого замыкания, то вы не звоните продолжить; скорее вызовите свой собственный метод, который создает объект "A".

Следующий код показывает класс на основе @Aspect, который демонстрирует оба метода. Вы должны внедрить свою собственную реализацию, чтобы вы могли создать свой собственный объект A по мере необходимости.

Вам следует в общем немного прочесть о Spring AOP, а точнее о Pointcuts (которые необходимы для перехвата вызова) и рекомендации @Around. Следует отметить, что вы можете комбинировать pointcut и использовать подстановочные знаки, так что, скорее всего, вы можете достичь желаемого с помощью ограниченного числа методов в своем классе @Aspect, если вы сделаете свой pointcut достаточно общим, чтобы охватить все методы do реализации.

Пример кода, показывающий как переход к исходной реализации, так и короткое замыкание, вызывающее вашу собственную.

@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;
   }

}
Другие вопросы по тегам