如何检测/建议Spring Data (JPA)存储库?

我努力为春季数据jpa存储库提供建议的努力失败了。目标是检测(围绕)使用自定义注释(在本例中为 ResourceNotFound)注释的特定存储库中的所有非公共方法,并在返回值为空集合时引发异常。voidnull

@Repository 
@ResourceNotFound
@Transactional(readOnly = true)
public interface CityRepository extends JpaRepository<City, Long>, JpaSpecificationExecutor<City> { … }

以下建议是连接用 注释的接口实现的所有公共方法。@ResourceNotFound

@Pointcut("within(com.digitalmisfits.spring.aop.annotation.ResourceNotFound *)")
public void beanAnnotatedWithResourceNotFound() {}

@Pointcut("execution(public * *(..))")
public void publicMethod() {}

@Around("beanAnnotatedWithResourceNotFound() && publicMethod()")
public Object publicMethodInsideAClassMarkedWithResourceNotFound(ProceedingJoinPoint pjp) throws Throwable {

    System.out.println("publicMethodInsideAClassMarkedWithResourceNotFound " + pjp.getTarget().toString());;

    Object retVal =  pjp.proceed();

    if(((MethodSignature) pjp.getSignature()).getReturnType() != Void.TYPE && isObjectEmpty(retVal))
        throw new RuntimeException("isObjectEmpty == true");

    return retVal;
}

当切入点指定为:publicMethodInsideAClassMarkedWithResourceNotFound(…)

@Pointcut("execution(public * package.CityRepository+.*(..))")

但是,未选取注释。这可能是由于存储库接口的基础类是一个(代理的)没有该特定注释的事实。@ResourceNotFoundSimpleJpaRepository

有没有办法将@ResourceNotFound传播到实现?

- 更新 -

更改了问题以反映建议(周围)应仅适用于具有自定义注释的存储库这一事实。


答案 1

尽管OP严重依赖AspectJ解决方案,但目前的问题并不直接表明解决方案应该仅限于AspectJ。因此,我想提供一种非AspectJ的方式来为Spring Data JPA存储库提供建议。它基于在准系统Spring AOP代理拦截器链中添加自定义。Interceptor

首先,配置您的自定义,例如RepositoryFactoryBean

@Configuration
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class ConfigJpaRepositories {
}

接下来,实现将你自己的添加到CustomRepositoryFactoryBeanRepositoryProxyPostProcessorJpaRepositoryFactory

class CustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T , I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {

  protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
    RepositoryFactorySupport factory = super.createRepositoryFactory(em);
    factory.addRepositoryProxyPostProcessor(new ResourceNotFoundProxyPostProcessor());
    return factory;
  }

}

您的实现应该将您的添加到特定存储库中(检查):RepositoryProxyPostProcessorMethodInterceptorProxyFactoryRepositoryInformation

class ResourceNotFoundProxyPostProcessor implements RepositoryProxyPostProcessor {

    @Override
    public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
        if (repositoryInformation.getRepositoryInterface().equals(CityRepository.class))
            factory.addAdvice(new ResourceNotFoundMethodInterceptor());
    }

}

在你的(顺便说一句,这是 的子接口,所以仍然是一个建议:)),你有一个完整的AspectJ建议:MethodInterceptororg.aopalliance.aop.Advice@Around

class ResourceNotFoundMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        ResourceNotFound resourceNotFound = method.getAnnotation(ResourceNotFound.class);
        //...
        Object result = invocation.proceed();
        //...
        return result;
    }
}   

答案 2

如果要在存储库级别拦截存储库调用,则实际上不需要为此引入自定义注释。您应该能够使用纯类型匹配项来解决此问题:

 @Pointcut("execution(public !void org.springframework.data.repository.Repository+.*(..))")

这将拦截扩展 Spring Data 接口的所有 Spring bean 的所有非方法的执行。voidRepository

一个稍微相关的示例可以在Spring Data示例存储库中找到。


推荐