问题描述
给予
@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所提供的证明,这里没有发生问题。顺便说一句,我不得不将包 addLineModified(id) {
let index = this.detailsPieces.findIndex((x) => x.id === id);
this.detailsPiecesService
.getDetailsPieceBylineModified(this.quotation.id,id)
.subscribe((element: DetailsPieces) => {
this.detailsPieces.splice(index,element);
});
}
重命名为aspect
,因为在AspectJ中aop
是一个保留关键字。但是为了确保它与当前的问题无关,并且也无关,我还在Spring项目中对其进行了重命名。
aspect
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())
不会像Spring AOP中那样被触发。
更新:我修改了您的MCVE,使其可以在Spring AOP和AspectJ中运行,它在活动时自动检测AspectJ的加载时织入器。我已将此pull request发送给您。