Spring , AOP , AspectJ :- 有没有办法找出哪个方法导致调用 "beforeAdvice" 和 "afterAdvice"

Spring , AOP , AspectJ :- is there a way to find out which method caused call to "beforeAdvice" and "afterAdvice"

我有一个关于 Spring 框架、面向方面编程和 AspectJ 的(新手)问题。

有没有办法找出哪个方法导致调用 "beforeAdvice" 和 "afterAdvice"?

例如,在下面的示例中,我能否找出 Dog.bark() 或 Dog.sleep() 是否导致对 "beforeAdvice" 和 "afterAdvice" 的调用?

我在源代码下面附上了控制台输出。

感谢您的宝贵时间和帮助,
詹姆斯

Dog.java

package com.tutorialspoint;

public class Dog
{
    public void bark()
    {
        System.out.println("Dog.bark()");
    }

    public void sleep()
    {
        System.out.println("Dog.sleep()");
    }
}

DogMonitor.java

package com.tutorialspoint;

public class DogMonitor
{
    public void beforeAdvice()
    {
        System.out.println("DogMonitor.beforeAdvice()  --  but was it bark or sleep ?");
    }

    public void afterAdvice()
    {
        System.out.println("DogMonitor.afterAdvice()  --  but was it bark or sleep ?");
    }

    public void afterReturningAdvice(Object retVal)
    {
        System.out.println("DogMonitor.afterReturningAdvice(): " + retVal.toString());
    }

    public void AfterThrowingAdvice(IllegalArgumentException ex)
    {
        System.out.println("DogMonitor.AfterThrowingAdvice(): " + ex.toString());
    }
}

MainApp.java

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp
{
   public static void main(String[] args)
   {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

      Dog dog = (Dog) context.getBean("dog");
      dog.bark();
      dog.sleep();
   }
}

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

   <aop:config>
      <aop:aspect id="dogMonitorAspect" ref="dogMonitorBean">
         <aop:pointcut  id="selectAll"            expression="execution(* com.tutorialspoint.Dog.*(..))"/>
         <aop:before    pointcut-ref="selectAll"  method="beforeAdvice"/>
         <aop:after     pointcut-ref="selectAll"  method="afterAdvice"/>
      </aop:aspect>
   </aop:config>

   <!-- Definition for dog bean -->
   <bean id="dog" class="com.tutorialspoint.Dog"/>

   <!-- Definition for dogMonitor bean -->
   <bean id="dogMonitorBean" class="com.tutorialspoint.DogMonitor"/>

</beans>

控制台输出

DogMonitor.beforeAdvice()  --  but was it bark or sleep ?
Dog.bark()
DogMonitor.afterAdvice()  --  but was it bark or sleep ?
DogMonitor.beforeAdvice()  --  but was it bark or sleep ?
Dog.sleep()
DogMonitor.afterAdvice()  --  but was it bark or sleep ?

您可以从一个新的 Throwable(您不抛出)中提取堆栈。像这样:

StackTraceElement[] stack = new Throwable().getStackTrace();
String methodName = stack[1].getMethodName();

尝试将 JoinPoint 作为参数添加到您的 Aspect 方法中:

 public void beforeAdvice(JoinPoint jp) {
   // This will print out "bark"
   System.out.println( p.getSignature().getName );
 }

在 Spring 中,此 JoinPoint 将始终有一个 MethodSignature 作为签名(因为 spring 只能以这种方式访问​​方法),因此您可以这样做(如果您想了解有关方法,比默认签名界面告诉你的更多)...

MethodSignature signature = (MethodSignature)jp.getSignature();
Method method = signature.getMethod();

对于 Around Aspect,您可以使用 ProceedingJoinPoint

每个方法都可以带一个JoinPoint as parameter. This object contains all the information about the interception that happened (which method, which class, etc.). For example, the getSignature()方法returns拦截方法的签名。我们可以像这样使用它来检索方法的名称:

public void beforeAdvice(JoinPoint joinPoint) {
    System.out.println("DogMonitor.beforeAdvice()");
    System.out.println("I just intercepted method " + joinPoint.getSignature().getName());
}

我遇到了同样的问题。我希望我可以从 JoinPoint 中得到一个 getSource 方法,但没有。我不得不查看堆栈。首先,我使用 Thread.dumpStack() 来了解堆栈包含的内容。我不需要遍历整个堆栈,所以我创建了一个子集。从堆栈内容来看,我对 15-20 范围感兴趣(这取决于你)

StackTraceElement[] subset = Arrays.copyOfRange(Thread.currentThread().getStackTrace(), 15, 20);

然后我只打印了我想要的

String mypackage = "abc.xwy";
for (int i = 0; i < subset.length; i++) {
  StackTraceElement element = subset[i];
  if (element.getClassName().contains(mypackage) || element.getMethodName().equals(methodName)) {
    System.out.println(element); 
    }
  }
}