Java Spring AOP可重入方面

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被调用了多少次(计入重复调用) 有没有办法做到这一点?您

是否可以使用SpringAOP(或AspectJ)创建可重入方面

以下是一个例子:

@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
    注释现在有一个参数:“每次执行斐波那契方法时创建一个实例,但排除递归实例。”
输出,版本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);
    }
}
执行(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。内部的