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