Ваш подход очень проблематичен, потому что вы вручную называете один совет другим. Это не так, как следует применять АОП. Пожалуйста, пусть AspectJ решит, какие советы выполнять по их соответствующим точкам. То, как вы делегируете от одного совета другому, вы могли бы даже назвать совет, который не соответствовал бы сам по себе. Пример в простом AspectJ без весны (работает так же в Spring AOP, хотя):
Java приложение драйвера:
package de.scrum_master.app;
public class Application {
private static void doSomething() {
System.out.println("Doing something");
}
public static void main(String[] args) {
doSomething();
}
}
Формат:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyBogusAspect {
@Around("execution(* doSomething(..))")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println("matching advice called on joinpoint " + thisJoinPoint);
return nonMatchingAdvice(thisJoinPoint);
}
@Around("execution(* doSomethingElse(..))")
public Object nonMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println("non-matching advice called on joinpoint " + thisJoinPoint);
return thisJoinPoint.proceed();
}
}
журнала консоли:
matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
non-matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
Doing something
Вы видите, как нездоровится ваш прием? Совет, который иначе не соответствовал бы, называется подходящим. Это дает некоторое действительно неожиданное поведение ИМО. Пожалуйста, не делайте этого !!!
Теперь, как для первоначального вопроса о множественных советах согласующих, это то, как вы должны это сделать:
Модифицированного аспект:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyBetterAspect {
@Around("execution(* doSomething(..))")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
@Around("execution(* doSomething(..))")
public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> another matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< another matching advice on " + thisJoinPoint);
return result;
}
}
Нового журнал консоль:
>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())
Как вы можете видеть, AspectJ или Spring AOP wrap mult согласующие друг с другом рекомендации, такие как луковые скины вокруг точек соединения, и только самая внутренняя proceed()
вызывает фактическую точку соединения, в то время как внешние слои называют внутренние, следя за тем, чтобы каждая точка соединения выполнялась только один раз. Вам не нужно пытаться быть умнее, чем структура AOP, что может привести к повреждению (см. Мой первый пример).
Еще одна вещь: если несколько аспектов имеют соответствующие точки, вы можете влиять на их порядок выполнения с помощью @DeclarePrecedence
в AspectJ, но в рамках одного аспекта вы не имеете никакого влияния на порядок выполнения или, по крайней мере, вы не должны полагаться на него. В Spring AOP вы можете использовать аннотацию @Order
, чтобы определить приоритет аспект, но порядок также не определен для нескольких советов из того же аспекта, см. Также Spring manual.
Update 2016-02-28, 18:30 CET, после некоторого обсуждения в комментариях:
Хорошо, мы расширим класс драйвера немного, чтобы мы могли проверить некоторые больше:
package de.scrum_master.app;
public class Application {
private static void doSomething() {
System.out.println("Doing something");
}
private static String doSomethingElse(String text) {
System.out.println("Doing something else");
return text;
}
private static int doAnotherThing(int i, int j, int k) {
System.out.println("Doing another thing");
return (i + j) * k;
}
public static void main(String[] args) {
doSomething();
doSomethingElse("foo");
doAnotherThing(11, 22, 33);
}
}
Теперь привязка первого параметра в AspectJ так же просто, как args(request, ..)
, который работает для одного или нескольких параметров. Единственным исключением являются нулевые параметры, и в этом случае pointcut не срабатывает. Так как я в конечном итоге с чем-то подобным тому, что вы сделали:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut()")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
return anotherMatchingAdvice(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> another matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< another matching advice on " + thisJoinPoint);
return result;
}
}
Что делает тот же самый совет огонь дважды, и, таким образом, вызывает накладные расходы, даже если оригинальный метод вызывается только один раз, но вы можете увидеть накладные расходы в журнале:
>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
Doing something else
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
Doing another thing
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
Вы можете легко узнать, как двойные советы увольняются для каждой точки соединения.
В качестве альтернативы, вы можете связать параметр во время выполнения, что не очень элегантный и берет на себя немного времени исполнения пенальти, но работает отлично:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut()")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> matching advice on " + thisJoinPoint);
Object[] args = thisJoinPoint.getArgs();
Object request = args.length > 0 ? args[0] : null;
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
Это позволяет избежать двойного исполнения консультации, а также дублирования кода и выходы следующий вывод на консоль:
>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
First parameter = null
Doing something
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
First parameter = foo
Doing something else
<<< matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
First parameter = 11
Doing another thing
<<< matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
Последнее, но не менее, вы можете иметь две немного разные - один срезов в с пустой args()
и один с args(request, ..)
- оба из которых могут делегировать обработку параметров , Регистрация и обработка исключений на вспомогательный метод для того, чтобы избежать дублирования, как я уже говорил в одном из моих комментариев:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut() && args()")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
return myAdviceHelper(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
return myAdviceHelper(thisJoinPoint, request);
}
private Object myAdviceHelper(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> matching advice on " + thisJoinPoint);
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
Бревно консоль должна быть точно такой же, как и предыдущий.
Update 2:
Ну, я просто понял, что пустой args()
трюк будет также применяться к вашим оригинальным идеям и избежать двойного исполнения, а также вспомогательный метода:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut() && args()")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
return myAdviceWithParams(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> matching advice on " + thisJoinPoint);
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
Это приемлемо, так же элегантно, потому что он не генерирует байтовый код дважды на одну точку соединения. Два точечных переключателя являются взаимоисключающими, поэтому это хорошо. Я рекомендую это решение.
В общем случае, если AspectJ не может логически рассуждать о том, что одно условие включает другое, оно не может устранить необходимость в обоих. Если бы он * мог делать такие рассуждения, вы могли бы утверждать оба способа, что он должен делать. (Я не знаю). Я предполагаю, что, поскольку у вас есть эти два совета, вы действительно знаете ответ в силу его запуска. Почему бы вам не рассказать нам? –