我没有努力建议一个spring data jpa存储库。目标是在一个特定的存储库中使用自定义注释(本例中为ResourceNotFound)注释所有非void
公共方法,并在返回值为null
或者为@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+.*(..))")
方法有效
@ResourceNotFound
但是,SimpleJpaRepository
注释未被提取。这可能是由于存储库接口的基础类是(代理的){{1}}而没有特定的注释。
有没有办法将@ResourceNotFound传播到实现?
- 更新 -
更改了问题,以反映建议(周围)仅适用于具有自定义注释的存储库。
答案 0 :(得分:9)
如果要拦截存储库级别的存储库调用,则实际上不需要为此引入自定义注释。您应该可以使用普通类型匹配来实现此功能:
@Pointcut("execution(public !void org.springframework.data.repository.Repository+.*(..))")
这将拦截扩展Spring Data void
接口的所有Spring bean的所有非Repository
方法的执行。
可以在Spring Data examples repository中找到一个稍微相关的示例。
答案 1 :(得分:4)
尽管OP严重依赖于AspectJ解决方案,但目前的问题并不直接表明解决方案应限于AspectJ。
因此,我想提供一种非AspectJ的方法来建议Spring Data JPA存储库。它基于将自定义Interceptor
添加到准系统Spring AOP代理拦截器链中。
首先,配置您的自定义RepositoryFactoryBean
,例如
@Configuration
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class ConfigJpaRepositories {
}
下一步,实现CustomRepositoryFactoryBean
,将自己的RepositoryProxyPostProcessor
添加到JpaRepositoryFactory
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;
}
}
您的RepositoryProxyPostProcessor
实现应将您的MethodInterceptor
添加到特定存储库(检查ProxyFactory
)的RepositoryInformation
:
class ResourceNotFoundProxyPostProcessor implements RepositoryProxyPostProcessor {
@Override
public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
if (repositoryInformation.getRepositoryInterface().equals(CityRepository.class))
factory.addAdvice(new ResourceNotFoundMethodInterceptor());
}
}
,并且在您的MethodInterceptor
(顺便说一句,它是org.aopalliance.aop.Advice
的子接口,因此仍然是一个建议:))中,您具有AspectJ @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 :(得分:3)
我能够使用以下构造解决我的问题(基本上检查接口链并搜索特定的Annotation):
@Pointcut("execution(public !void org.springframework.data.repository.Repository+.*(..))")
public void publicNonVoidRepositoryMethod() {}
@Around("publicNonVoidRepositoryMethod()")
public Object publicNonVoidRepositoryMethod(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = pjp.proceed();
boolean hasClassAnnotation = false;
for(Class<?> i: pjp.getTarget().getClass().getInterfaces()) {
if(i.getAnnotation(ThrowResourceNotFound.class) != null) {
hasClassAnnotation = true;
break;
}
}
if(hasClassAnnotation && isObjectEmpty(retVal))
throw new RuntimeException(messageSource.getMessage("exception.resourceNotFound", new Object[]{}, LocaleContextHolder.getLocale()));
return retVal;
}
答案 3 :(得分:1)
问题不是AspectJ或Spring-AOP所固有的,而是Java本身的问题:
通常,子类不会继承父类的注释,但您可以显式使用@Inherited
来指定它应该被继承。即使在这种情况下,继承只发生在类层次结构中,而不是从实现类的接口,请参阅Javadoc:
请注意,如果使用带注释的类型来注释除类之外的任何内容,则此元注释类型不起作用。另请注意,此元注释仅导致注释从超类继承;已实施接口上的注释无效。
更新:因为我之前已经多次回答过这个问题,所以我刚刚记录了问题以及Emulate annotation inheritance for interfaces and methods with AspectJ中的解决方法。
更新:如果您注释实现类而不是接口本身(例如,通过创建由可继承注释注释的抽象基类),您可以通过检查void来简化您的建议像这样返回类型:
@Around("execution(public !void (@com.digitalmisfits..ResourceNotFound *).*(..))")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(thisJoinPoint);
Object retVal = thisJoinPoint.proceed();
if (isObjectEmpty(retVal))
throw new RuntimeException("Illegal empty result");
return retVal;
}
答案 4 :(得分:-2)
Class[] objs = Arrays.stream(joinPoint.getArgs()).map(item -> item.getClass()).toArray(Class[]::new);
System.out.println("[AspectJ] args interfaces :"+objs);
Class clazz = Class.forName(joinPoint.getSignature().getDeclaringTypeName());
System.out.println("[AspectJ] signature class :"+clazz);
Method method = clazz.getDeclaredMethod(joinPoint.getSignature().getName(), objs) ;
System.out.println("[AspectJ] signature method :"+method);
Query m = method.getDeclaredAnnotation(Query.class) ;
System.out.println("[AspectJ] signature annotation value:"+ (m!=null?m.value():m) );