Java Spring AOP可重入方面
是否可以使用SpringAOP(或AspectJ)创建可重入方面 以下是一个例子:Java Spring AOP可重入方面,java,spring,aspectj,spring-aop,aspect,Java,Spring,Aspectj,Spring Aop,Aspect,是否可以使用SpringAOP(或AspectJ)创建可重入方面 以下是一个例子: @Log public int calcFibonacci(int n) { if(n <= 1) { return n; } else { return calcFibonacci(n - 1) + calcFibonacci(n - 2); } } } 现在我想知道calcFibonacci被调用了多少次(计入重复调用) 有没有办法做到这一点?您
@Log
public int calcFibonacci(int n) {
if(n <= 1) {
return n;
} else {
return calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
}
现在我想知道calcFibonacci被调用了多少次(计入重复调用)
有没有办法做到这一点?您需要以下几点:
<aop:aspectj-autoproxy expose-proxy="true"/>
好吧,你不能在春季AOP中优雅地解决这个问题-请看我对的回答的第一句话。如果在AOP中,应用程序代码需要知道方面的存在,甚至需要调用与方面相关的代码,那么这就是糟糕的设计和反AOP。因此,这里我为您提供了一个AspectJ解决方案
首先,在AspectJ中不仅仅有execution()
切入点,例如call()
。因此,仅计算由@Log
注释的连接点,结果将是对calcFibonacci(int)
的递归调用的实际数量的两倍。因此,切入点不应该只是
@注释(日志)
但是
执行(**(..)&&&@注释(日志)
事实上,这仍然不够,因为如果多个方法包含@Log
注释怎么办?这些电话都要算吗?不,只有那些到calcFibonacci(int)
的!因此,我们应该将“斐波那契呼叫计数器”更严格地限制为:
执行(**…Application.calcFibonacci(int))&&@注释(log)
以下是一些完全可编译的示例代码:
注释:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
}
@Log
public int calcFibonacci(int n) {
return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Log;
@Aspect
public class LoggingAspect {
int count = 0;
@Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
public void measure(JoinPoint thisJoinPoint, Log log) {
System.out.println(thisJoinPoint + " - " + ++count);
}
}
递归斐波那契方法的应用:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
}
@Log
public int calcFibonacci(int n) {
return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Log;
@Aspect
public class LoggingAspect {
int count = 0;
@Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
public void measure(JoinPoint thisJoinPoint, Log log) {
System.out.println(thisJoinPoint + " - " + ++count);
}
}
输出,第1版:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
}
@Log
public int calcFibonacci(int n) {
return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Log;
@Aspect
public class LoggingAspect {
int count = 0;
@Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
public void measure(JoinPoint thisJoinPoint, Log log) {
System.out.println(thisJoinPoint + " - " + ++count);
}
}
执行(int de.scrum_master.app.Application.calcFibonacci(int))-1
执行(int de.scrum_master.app.Application.calcFibonacci(int))-2
(...)
执行(int de.scrum_master.app.Application.calcFibonacci(int))-24
执行(int de.scrum_master.app.Application.calcFibonacci(int))-25
斐波那契#6=8
现在,如果我们两次调用斐波那契方法呢
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
fibonacciNumber = 4;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
执行(int de.scrum_master.app.Application.calcFibonacci(int))-1
执行(int de.scrum_master.app.Application.calcFibonacci(int))-2
(...)
执行(int de.scrum_master.app.Application.calcFibonacci(int))-24
执行(int de.scrum_master.app.Application.calcFibonacci(int))-25
斐波那契#6=8
执行(int de.scrum_master.app.Application.calcFibonacci(int))-26
执行(int de.scrum_master.app.Application.calcFibonacci(int))-27
(...)
执行(int de.scrum_master.app.Application.calcFibonacci(int))-33
执行(int de.scrum_master.app.Application.calcFibonacci(int))-34
斐波那契#4=3
啊哦
我们需要在调用之间重置计数器(并通过使用ThreadLocal
等确保整个过程是线程安全的),或者对每个控制流使用方面实例化,而不是单例方面,这正是我在这里使用它的目的:
方面,第2版:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
}
@Log
public int calcFibonacci(int n) {
return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Log;
@Aspect
public class LoggingAspect {
int count = 0;
@Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
public void measure(JoinPoint thisJoinPoint, Log log) {
System.out.println(thisJoinPoint + " - " + ++count);
}
}
package de.scrum\u master.aspect;
导入org.aspectj.lang.JoinPoint;
导入org.aspectj.lang.annotation.Aspect;
导入org.aspectj.lang.annotation.Before;
导入de.scrum_master.app.Log;
@方面(“percflow(执行(**.calcFibonacci(int))和&!cflowdown(执行(**.calcFibonacci(int))))
公共类日志方面{
整数计数=0;
@在(“执行(**.calcFibonacci(int))&&&@注释(log)”之前
公共无效度量(连接点-此连接点,日志){
System.out.println(thisJoinPoint+“-”+++计数);
}
}
注:
- 为了使源代码更具可读性,我将包和类规范缩短为
。您也可以使用*
或其任何缩写,以避免与类似的类/方法名称发生冲突de.scrum\u master.app.Application
注释现在有一个参数:“每次执行斐波那契方法时创建一个实例,但排除递归实例。”@Aspect
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
}
@Log
public int calcFibonacci(int n) {
return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Log;
@Aspect
public class LoggingAspect {
int count = 0;
@Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
public void measure(JoinPoint thisJoinPoint, Log log) {
System.out.println(thisJoinPoint + " - " + ++count);
}
}
执行(int de.scrum_master.app.Application.calcFibonacci(int))-1
执行(int de.scrum_master.app.Application.calcFibonacci(int))-2
(..)
执行(int de.scrum_master.app.Application.calcFibonacci(int))-24
执行(int de.scrum_master.app.Application.calcFibonacci(int))-25
斐波那契#6=8
执行(int de.scrum_master.app.Application.calcFibonacci(int))-1
执行(int de.scrum_master.app.Application.calcFibonacci(int))-2
(..)
执行(int de.scrum_master.app.Application.calcFibonacci(int))-8
执行(int de.scrum_master.app.Application.calcFibonacci(int))-9
斐波那契#4=3
现在看起来好多了<代码>:)
享受吧 在
calcFibonacci()
中,内部调用需要如下:((CalcFibonaciiInterface)AopContext.currentProxy()).calcFibonacci()
。您还没有发布完整的类,但我在这里假设您包含calcFibonacci
的类实现了一个接口(我称之为CalcFibonacciInterface)。我发现非方面代码应该知道甚至显式调用方面相关的代码,这相当奇怪。它与AOP的所有内容相矛盾。@kriegaex它并不奇怪,它很难看,我上面提供的Spring文档链接明确建议不要这样做。这个问题唯一真正的解决方案是更改代码,这样就不会有内部调用(或者在本例中是递归方法调用)。有,但这篇文章中的问题是一个一般性的问题,斐波那契只是一个例子。问题是:“有可能用Spring AOP(或AspectJ)创建可重入方面吗?”过一会儿,我将用AspectJ提出一个解决方案,在此之前我只需要做一点工作。回头见,没错。期待您的解决方案。@kriegaex问题不是太多AOP,而是Spring应用AOP的方法。它使用代理,这意味着只有对对象的方法调用才会应用AOP。内部的