您尝试做的事情是不可能的Spring AOP
(至少在没有对建议的方法进行一些重构的情况下是不可能的)。原因是 Spring AOP is proxy based
,这意味着它为每个 bean 创建一个代理类并注入它而不是你的实现。代理具有 bean 的所有方法,并添加了方面功能。所以当你调用一个bean的方法(实际上是bean的代理)时,切面代码被执行,然后你的方法被委托调用。因此,当您的方法调用其他方法时,调用是使用真正的 bean 执行的,而不是那些的代理 - 如果存在的话 - 因此您不会得到您期望的输出。
您可以想到一个代理,它看起来像这样:
class MyBeanProxy implements MyBean {
MyBeanImpl theBean;
public void foo() {
// aspect code
theBean.foo();
}
public void bar() {
// aspect code
theBean.bar();
}
}
你的豆子在哪里
interface MyBean {
foo();
bar();
}
@Component("my_bean")
class MyBeanImpl implements MyBean {
public void foo() {
System.out.println("foo");
bar();
}
public void bar() {
System.out.println("bar");
}
}
在上面的示例中,当您foo()
通过代理调用时,将执行方面代码,并且委托MyBeanImpl#foo()
发生在bar()
被调用的位置。现在很明显,方面代码bar()
不会被执行。
现在你怎样才能让它工作?
1 -以这样一种方式重构您的代码,对于您希望为它们执行方面代码的方法,调用发生在代理对象上而不是 bean 本身上。为此,您可以获得实际的代理并使用它来调用您的方法。
public void foo() {
System.out.println("foo");
MyBean myBeanProxy = (MyBean) AopContext.currentProxy();
myBeanProxy.bar();
}
请注意,这种方法更像是一种 hack,而不是一种干净的方式来完成这项工作。例如,很明显myBeanProxy
不知道当前对象的状态。
2 -重构您的代码,使其bar()
位于另一个 bean 中,您可以使用appContext
.
3-使用AspectJ:Aspect 代码被注入到目标类本身(真实的东西!)
这是使用 AspectJ 的小例子
方面
package com.aj;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspect {
@Around("execution( * com.app.services.*.* (..) )")
public Object callDurationAdvice(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
Object[] args = pjp.getArgs();
String argList = Arrays.toString(args);
System.out.println(signature.getDeclaringTypeName() +
"." + signature.getName() + "(" + argList + ") started");
long s = System.nanoTime();
Object proceed = pjp.proceed(args);
long e = System.nanoTime();
System.out.println(signature.getDeclaringTypeName() +
"." + signature.getName() + "(" + argList + ") ended after " +
((double)(e-s)/1000000) + " ms");
return proceed;
}
}
某个包中的一个类应该是方面的目标
package com.app.services;
public class ServicesVersionInfo {
public static String getVersion() {
return getVersionNumber() + " " + getVersionStage();
}
public static String getVersionNumber() {
return "1.0.0";
}
public static String getVersionStage() {
return "ALPHA";
}
}
应用程序
package com.app;
import com.app.services.ServicesVersionInfo;
public class App {
public static void main(String[] args) {
System.out.println("App services version: " +
ServicesVersionInfo.getVersion());
}
}
冉,这应该输出一些谎言
com.app.services.ServicesVersionInfo.getVersion([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) ended after 0.004862 ms
com.app.services.ServicesVersionInfo.getVersionStage([]) started
com.app.services.ServicesVersionInfo.getVersionStage([]) ended after 0.005673 ms
com.app.services.ServicesVersionInfo.getVersion([]) ended after 0.378877 ms
App services version: 1.0.0 ALPHA
最后是一些类似的问题和进一步的阅读:
Spring AOP 不适用于另一个方法中的方法调用
从对象本身获取 AOP 代理
Spring AOP 顶级问题 #1 - 未应用方面