上下文
我正在使用AOP通过拦截Spring Data存储库的保存和删除方法来创建/删除托管实体的Spring ACL记录。我所有的存储库都是@RepositoryRestResource
注释的接口,扩展了PagingAndSortingRepository
或CrudRepository
。过去一直很好。不幸的是,我无法确切地确定它停止工作的时间点(或代码更改)。
预期行为
以下建议应在所有保存方法上触发。
@Pointcut("execution(* org.springframework.data.repository.*.save(*))")
public void defaultRepoSave() {}
// may be useful for overridden methods
@Pointcut("execution(* com.xxx.yyy.repository.*.save(..))")
public static void customRepoSave() {}
@Pointcut("defaultRepoSave() || customRepoSave()")
public static void repoSave() {}
@Around(value="repoSave() && args(entity)", argNames="entity")
public Object aroundSave(ProceedingJoinPoint pjp, Object entity) throws Throwable{
log.info("Saving ...");
return pjp.proceed();
}
注意:我尝试了@EnableAspectJAutoProxy(exposeProxy=true/false, proxyTargetClass=true/false)
的各种组合,但似乎对这一特定建议没有任何影响
问题
该建议会针对某些存储库触发,而不针对其他存储库。两个存储库都在同一程序包中。调试显示这两个存储库均已代理,但左侧存储库的执行完全缺少与通知相关的拦截器。右边的一个按预期进行。
为了消除切入点不匹配的可能性,我创建了一个自定义批注,并将其添加到两个存储库中的.save()方法中。
注释:
@Retention(RUNTIME)
@Target(METHOD)
public @interface AclManaged {}
用作:
@Override
@AclManaged
Entity1 save(Entity1 entity);
和建议:
@Around("@annotation(aclManaged)")
public Object aclManaged(ProceedingJoinPoint joinPoint, AclManaged aclManaged) throws Throwable {
log.info("Acl managed");
return joinPoint.proceed();
}
同一个故事-注释可在一个存储库中使用,但不会为切入点execution(..save..)
失败的存储库触发。
出于测试目的,我通过将一个空实体从Postman张贴到每个各自的存储库其余端点来调用这两种方法。但是,直接从我的代码中调用存储库时,也会发生相同的问题。
出于完整性考虑,存储库代码(即使使用最基本的repo实现,也会出现不一致的行为):
---编辑:我将存储库简化到最低限度---
@RepositoryRestResource(collectionResourceRel = "entity1s", path = "entity1s")
public interface Entity1Repository extends PagingAndSortingRepository<Entity1, Long> {
// this is the method that is supposed to fire two distinct advices
@Override
@AclManaged
Entity1 save(Entity1 entity);
}
@RepositoryRestResource(collectionResourceRel = "entity2s", path = "entity2s")
public interface Entity2Repository extends PagingAndSortingRepository<Entity2, Long> {
// both aspects match perfectly
@Override
@AclManaged
Entity2 save(Entity2 ics);
}
问题
什么可以阻止AOP方面? Spring如何填充调用拦截器链?
解决AOP方面的最佳方法是什么? (鉴于执行切入点和注释切入点均失败)
有一点不同-是否建议将JPA审核用于ACL管理而不是AOP?
版本
spring 5.0.8.RELEASE,spring数据休息3.0.9.RELEASE,spring数据jpa 2.0.9.RELEASE(全部由Spring Boot 2.0.4管理)
答案 0 :(得分:0)
这个问题似乎是由于Spring Booot的@Enable...
注释和我的@Configuration
类的不幸组合造成的。该框架似乎有几种不同的方法来确定类/接口代理(从@EnableCaching
,@EnableTransactionManagement
,@EnableAspectJAutoProxy
,@EnableAsync
中提取)。在某些情况下,他们似乎劫持了预期的行为。我能够通过以下方式恢复预期的行为:
proxyTargetClass=true
添加到所有@Enable..
注释@Enable...
注释移至相关的@Configuration
类中我无法确定哪个组合特别引起了不一致的行为,即我没有最小的可复制测试用例。
我仍然对更不稳定的MethodInvocationInterceptor链不一致的根本原因感兴趣。