Spring AOP方面的行为与Spring数据存储库的切入点不一致

时间:2018-08-23 23:49:55

标签: java spring spring-data-jpa spring-aop spring-data-rest

上下文

我正在使用AOP通过拦截Spring Data存储库的保存和删除方法来创建/删除托管实体的Spring ACL记录。我所有的存储库都是@RepositoryRestResource注释的接口,扩展了PagingAndSortingRepositoryCrudRepository。过去一直很好。不幸的是,我无法确切地确定它停止工作的时间点(或代码更改)。

预期行为

以下建议应在所有保存方法上触发。

@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)的各种组合,但似乎对这一特定建议没有任何影响

问题

该建议会针对某些存储库触发,而不针对其他存储库。两个存储库都在同一程序包中。调试显示这两个存储库均已代理,但左侧存储库的执行完全缺少与通知相关的拦截器。右边的一个按预期进行。

Screenshot

为了消除切入点不匹配的可能性,我创建了一个自定义批注,并将其添加到两个存储库中的.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管理)

1 个答案:

答案 0 :(得分:0)

这个问题似乎是由于Spring Booot的@Enable...注释和我的@Configuration类的不幸组合造成的。该框架似乎有几种不同的方法来确定类/接口代理(从@EnableCaching@EnableTransactionManagement@EnableAspectJAutoProxy@EnableAsync中提取)。在某些情况下,他们似乎劫持了预期的行为。我能够通过以下方式恢复预期的行为:

  • proxyTargetClass=true添加到所有@Enable..注释
  • @Enable...注释移至相关的@Configuration类中

我无法确定哪个组合特别引起了不一致的行为,即我没有最小的可复制测试用例。

我仍然对更不稳定的MethodInvocationInterceptor链不一致的根本原因感兴趣。