java - Spring AOP - 仅当从 lambda 调用 joinPoint.proceed 时才应用于方法的切入点
问题描述
(标题不是最好的,但我找不到表达以下问题的好方法)
给定
@Aspect
@Component
class MyAspect {
@Autowired private MyService service;
@Around("@target(org.springframework.ws.server.endpoint.annotation.Endpoint)")
public Object aroundEndpoint(ProceedingJoinPoint joinPoint) {
return service.around(joinPoint::proceed);
}
@Around("@target(org.springframework.stereotype.Service)") // And some other expressions to exclude `MyService`
public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
// ...
}
}
@Service
class MyService {
// My own Callable<T> with Throwable instead of Exception
public Object around(Callable<?> callable) throws Throwable {
// Do stuff
Object returnValue = callable.call();
// Do stuff
return returnValue;
}
}
当调用端点方法时,它会被aroundEndpoint
. 如果我立即调用joinPoint.proceed()
,一切都会按预期工作。但是,如果我将它作为方法引用(或 lambda)传递给MyService.around
,然后调用它,它会与我的服务切入点匹配,并且我的环绕服务建议将应用于它。
我做了一些调试,这就是我所看到的:在 中AspectJExpressionPointcut.matches
,在前一种情况下引用我的端点,但在后一种情况下引用我的服务thisObject
。targetObject
这可能是因为它使用ExposeInvocationInterceptor.currentInvocation()
, 并且做另一个方法调用会弄乱它。
这是一个错误吗?基于代理的方法的一些限制?还是我必须简单地内联MyService.aroundService
?
解决方案
我重现了您的问题,并比较了普通 Java + AspectJ 中的类似设置(即没有 Spring 或 Spring AOP,仅使用方面切入点中使用的两个 Spring 注释)。那里不会出现问题。这是 Spring AOP 特有的东西,这是肯定的。
现在 Spring 使用 AspectJ 的切入点匹配结合自己的基于代理和委托的 AOP 框架。在那里的某个地方,这种边缘情况必须弄乱 Spring 方面匹配的状态,从而导致您看到的行为。到目前为止,我还没有调试过它,但从我现在看到的情况来看,我建议创建一个问题,看看维护者对此有何看法。
这是我的 AspectJ MCVE,证明问题不会在那里发生。顺便说一句,我不得不将包重命名为aspect
,aop
因为在 AspectJaspect
中是一个保留关键字。但是我也在Spring项目中重命名了它,以确保它与手头的问题无关,并且不相关。
package aop.mcve;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
public void controllerMethod() {}
}
package aop.mcve;
import org.springframework.stereotype.Service;
@Service
public class MyService {
public Object delegateTo(MyAspect.Callable<?> callable) throws Throwable {
return callable.call();
}
public void serviceMethod() {}
}
package aop.mcve;
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 MyAspect {
private final MyService myService = new MyService();
@Pointcut("within(aop.mcve..*) && !within(MyAspect) && execution(* *(..))")
public void inDomain() {}
@Pointcut("@target(org.springframework.stereotype.Service)")
public void inService() {}
@Pointcut("execution(* aop.mcve.MyService.*(..))")
public void inMyService() {}
@Pointcut("@target(org.springframework.web.bind.annotation.RestController)")
public void inController() {}
@Around("inDomain() && inController()")
public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("aroundController -> " + joinPoint);
return myService.delegateTo(joinPoint::proceed);
}
@Around("inDomain() && inService() && !inMyService()")
public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("aroundService -> " + joinPoint);
System.out.println("You should never see this message!");
return joinPoint.proceed();
}
public interface Callable<T> {
T call() throws Throwable;
}
}
package aop.mcve;
public class AspectMcveApplication {
public static void main(String[] args) throws Throwable {
new MyService().serviceMethod();
new MyController().controllerMethod();
}
}
控制台日志:
aroundController -> execution(void aop.mcve.MyController.controllerMethod())
如您所见,通知方法aroundService(..)
不会像在 Spring AOP 中那样被触发。
更新:我修改了您的 MCVE 以使其可与 Spring AOP 和 AspectJ 一起运行,它会在活动时自动检测 AspectJ 的加载时编织器。我给你发了这个拉取请求。
推荐阅读
- javascript - JS 错误:错误:参数“可取消”需要 GCancellable 类型的对象,但得到了字符串类型
- php - PHP仅在其他索引匹配时按列过滤多维数组
- python-3.x - Chromedriver 使用 Docker 文件安装,但 chrome 驱动程序未反映在上述目录中
- r - 我应该在 RMarkdown 中正确显示谁的 vtable?
- anylogic - AnyLogic 无法正确读取 Excel 字符串值
- r - 拆分表示时间的字符串并以秒为单位提取值
- javascript - 由于看似无关的库,对象范围规则似乎发生了变化?
- google-cloud-platform - 将自定义域映射到 GCP API 网关
- android - 如何在 Kotlin Android 中迭代单个 JSON 数组结果
- php - Laravel 7不显示img