[SPRING] 스프링 데이터 (JPA) 저장소를 계측 / 조언하는 방법은 무엇입니까?
SPRING스프링 데이터 (JPA) 저장소를 계측 / 조언하는 방법은 무엇입니까?
나는 봄 데이터 jpa 저장소에 대한 충고에 실패하고있다. 목표는 커스텀 주석 (이 예에서는 ResourceNotFound)으로 주석 된 특정의 리포지터리 (repository) 내의 비 void 공개 메소드를 모두 계측 해, 반환 값이 하늘 또는 공집 인 경우에 예외를 슬로우하는 것입니다.
@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이 다음과 같이 지정 될 때 작동합니다 :
@Pointcut("execution(public * package.CityRepository+.*(..))")
그러나 @ResourceNotFound 주석은 선택되지 않습니다. 이는 저장소 인터페이스의 기본 클래스가 해당 특수 주석이없는 (프록시 된) SimpleJpaRepository라는 사실 때문일 수 있습니다.
@ResourceNotFound를 구현에 전달할 수있는 방법이 있습니까?
- 업데이트 -
조언 (주위)이 맞춤 주석이있는 리포지토리에만 적용되어야한다는 사실을 반영하도록 질문을 변경했습니다.
해결법
-
==============================
1.OP가 AspectJ 솔루션에 크게 의존했지만, 문제는 AspectJ로 솔루션을 제한해야한다는 직접적인 제안은 아닙니다. 따라서 스프링 데이터 JPA 저장소에 조언을 제공하는 비 AspectJ 방식을 제안하고자한다. 그것은 Barebone Spring AOP 프록시 인터셉터 체인에 커스텀 인터셉터를 추가 할 때 사용됩니다.
OP가 AspectJ 솔루션에 크게 의존했지만, 문제는 AspectJ로 솔루션을 제한해야한다는 직접적인 제안은 아닙니다. 따라서 스프링 데이터 JPA 저장소에 조언을 제공하는 비 AspectJ 방식을 제안하고자한다. 그것은 Barebone Spring AOP 프록시 인터셉터 체인에 커스텀 인터셉터를 추가 할 때 사용됩니다.
먼저 사용자 정의 RepositoryFactoryBean을 구성하십시오.
@Configuration @EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class) public class ConfigJpaRepositories { }
그런 다음 CustomRepositoryFactoryBean을 구현하여 JpaRepositoryFactory에 고유 한 RepositoryProxyPostProcessor를 추가합니다.
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를 특정 Repository의 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 (BTW는 org.aopalliance.aop.Advice의 서브 인터페이스입니다. 그래서 여전히 조언 :)) 당신은 AspectJ의 전능을 가지고 있습니다. @Around advice :
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 Repository 인터페이스를 확장하는 모든 Spring bean의 void가 아닌 모든 메소드의 실행을 차단합니다.
약간 관련된 예제는 Spring Data Examples 저장소에서 찾을 수 있습니다.
-
==============================
3.다음 구문을 사용하여 문제를 해결할 수있었습니다 (기본적으로 인터페이스 체인을 검사하고 특정 Annotation을 검색).
다음 구문을 사용하여 문제를 해결할 수있었습니다 (기본적으로 인터페이스 체인을 검사하고 특정 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; }
-
==============================
4.문제는 AspectJ 나 Spring-AOP가 아니라 Java 자체에있다.
문제는 AspectJ 나 Spring-AOP가 아니라 Java 자체에있다.
일반적으로 상위 클래스의 주석은 하위 클래스에 상속되지 않지만 명시 적으로 @Inherited를 사용하여 상속되어야한다고 지정할 수 있습니다. 이 경우에도 상속은 인터페이스에서 클래스를 구현하는 것이 아니라 클래스 계층을 따라 수행됩니다 (Javadoc 참조).
-
==============================
5.
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) );
from https://stackoverflow.com/questions/26258158/how-to-instrument-advice-a-spring-data-jpa-repository by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] JSR-303 의존성 주입 및 절전 (0) | 2019.01.02 |
---|---|
[SPRING] 복합 ID가있는 엔티티에 대한 HATEOAS 링크 생성 사용자 정의 (0) | 2019.01.02 |
[SPRING] 봄에 시작할 때 @Cache를로드하는 방법? (0) | 2019.01.02 |
[SPRING] 스프링 MVC 스프링 보안과 에러 처리 (0) | 2019.01.02 |
[SPRING] convertAndSendToUser에서 "user"가 나오는 곳은 SockJS + Spring Websocket에서 작동합니까? (0) | 2019.01.02 |