【发布时间】:2021-02-03 12:38:52
【问题描述】:
我在我的 Spring boot 项目中使用了在注释类中的每个公共方法上触发的方面:
@Aspect
@Component
public class DeletedAwareAspect {
@Before("@within(com.example.DeleteAware)")
public void aroundExecution(JoinPoint pjp) throws Throwable {
//... some logic before
}
@After("@within(com.example.DeleteAware)")
public void cleanUp(JoinPoint pjp) throws Throwable {
//... some logic after
}
}
该方面的用法如下:
@DeleteAware
@Service
public class MyService {
public void foo() {}
}
@DeleteAware
@Service
public class MyAnotherService {
@Autowired
private MyService service;
public void anotherFoo() {}
public void VERY_IMPORTANT_METHOD() {
service.foo();
}
}
MyService.foo() 和 MyAnotherService.anotherFoo() 按预期工作。但是这里有问题 - 如果由方面包装的方法被另一个方面方法(如 VERY_IMPORTANT_METHOD())调用,我不想触发方面两次,但只触发一次。 如何从 Aspect 检查方法是否在另一个切面方法内部被调用?
【问题讨论】:
-
只是一个想法,我目前正在使用手机,因此无法尝试:
joinpoint.getStaticPart()包含静态信息。也许可以检查连接点是否来自同一个带注释的类。我希望你明白我的意思。为了获得更好的性能,您还可以将方法签名中的 JoinPoint 替换为 StaticPart 类型。应该类似于Joinpoint.StaticPart -
如果不检查调用堆栈,就无法识别调用者方法。解决此问题的另一种方法是将
VERY_IMPORTANT_METHOD()从建议方法的范围中排除。例如@within(com.example.DeleteAware) && !@annotation(ExcludeMethod),其中ExcludeMethod将是方法级别的注释。将需要修改代码来识别此类方法。另一种方法是使用 threadlocal 来标记它已经被建议并在执行逻辑之前检查建议中的相同内容。 -
您是否考虑过切换到原生 AspectJ 并使用
cflow()或cflowbelow()? P.S.:看起来你想做类似于@Transactional所做的事情。由于您没有解释方面的作用,因此无法判断您是否可以使用该注释。我也无法具体建议如何在没有本机 AspectJ 的情况下重新实现您的方面,以便不删除资源两次,无论是使用提到的ThreadLocal还是通过其他方式。因此,如果您想进一步讨论此问题,请提供MCVE。 -
@kriegaex,是的,我想实现非常相似的功能,例如“事务”(需要传播)。更具体地说,我需要在“之前”方面打开会话上的 JPA 过滤器,并在“之后”部分将其关闭。如果我使用该注释将服务注入另一个服务,它就不起作用,因为一旦内部服务结束,它会关闭我的过滤器,然后在外部服务过滤器已经关闭并且我的功能不起作用。
-
是的,在这种情况下,您需要手动记账。问题:最外层的“之前”和对应的“之后”建议之间的所有内容是否都在一个线程中?这将大大简化事情。您对可能方法的选择很大程度上取决于您对这个问题的回答。
标签: spring-boot aop aspectj spring-aop