spring aop @within неправильно работает для пользовательской аннотации
Я создал пользовательскую аннотацию для некоторых целей ведения журнала. Эта аннотация применяется к репозиторию Spring jpa, созданному в проекте путем расширения JpaRepository. Итак, что происходит сейчас, так это то, что для методов чтения он работает правильно, но для части сохранения совет @Around никогда не вызывается. Ниже мой совет @Around
@Around("@within(com.myproject.annotations.RepoAware)")
public void log(final ProceedingJoinPoint jp){
return log(jp,true);
}
мой метод журнала принимает один логический аргумент, на основе которого я что-то регистрирую. Ниже приведен код репо.
@Repository
@RepoAware
public interface MyRepo extends JpaRepository<Student,Long>{
}
Теперь, когда я вызываю метод репо, который не является частью моего репозитория MyRepo, например, save, saveAll или, в частности, метод, который существует в родительской иерархии, тогда совет @Around не работает. Когда я применил отладчик, я вижу, что во время сохранения вызова прокси имеет тип CrudRepository. Поэтому, когда я переопределяю метод сохранения в MyRepo.class, он начинает работать. Я запутался здесь, потому что MyRepo в конечном итоге расширил CrudRepository через JpaRepository. Пожалуйста, дайте мне знать, как это исправить или что я делаю неправильно здесь.
Также предоставьте справку о том, как использовать не выражение в pointcut. Скажем, для приведенного выше примера я хочу настроить таргетинг на все свои репозитории, кроме тех, которые имеют аннотацию @RepoAware. Я создал ниже совет, но он также не работает.
@Around("target(org.springframework.data.jpa.repository.JpaRepository) and !@within(com.myproject.annotations.RepoAware)")
public Object logDBMetrics(final ProceedingJoinPoint pjp) throws Throwable {
return log(pjp,false);
}
приведенный выше совет вызывается также для репозиториев с аннотацией @RepoAware.
Заранее спасибо !
2 ответа
AspectJ имеет очень ограниченную функциональность в случае инфраструктуры Spring, однако можно реализовать ваши требования с помощью советников (пожалуйста, также проверьте: Spring Advisor в Java с использованием @Bean)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface CustomRepositoryAnnotation {
}
@Component
public class CustomRepositoryAnnotationAdvisor extends AbstractPointcutAdvisor {
private static final Logger log = LoggerFactory.getLogger(CustomRepositoryAnnotationAdvisor.class);
private final Advice advice;
private final Pointcut pointcut;
public CustomRepositoryAnnotationAdvisor() {
this.advice = new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@NonNull MethodInvocation invocation) throws Throwable {
log.info("DemoAnnotationAdvisor: {}", invocation.getMethod().getName());
return invocation.proceed();
}
};
this.pointcut = new AnnotationMatchingPointcut(CustomRepositoryAnnotation.class, null, true);
}
@Override
public Pointcut getPointcut() {
return pointcut;
}
@Override
public Advice getAdvice() {
return advice;
}
}
УПД.
Провели некоторые исследования по этой теме и нашли блестящую статью о точечных указателях Spring и AspectJ.
Прежде всего,@within
не должен работать в вашем случае (перехватывая методы суперинтерфейса) - вы должны использовать , к сожалению, указатель pointcut, похоже, сломанSpring AOP
: наличие совета с одиночным@target
pointcut заставляет Spring создавать прокси CGLIB для каждого bean-компонента, что нежелательно и даже невозможно (классы jdk, конечные классы, классы без общедоступного конструктора и т. д.), попытка сузить объем рекомендаций также терпит неудачу: например, spring не применяется совет@target(annotation) && target(JpaRepository)
по неизвестной причине.
Во-вторых, в случаеSpring APO & AspectJ
можно достичь ваших требований, внедрив интерфейс маркера и используяtarget(marker interface)
однако, в качестве указателя точки, я бы предпочел держаться подальше от этих головоломок.
Я, наконец, смог заставить его работать, и ниже приведен подход, который я использовал. Спасибо @Andrey B. Panfilov.
Я создал интерфейс маркера, который расширяется MyRepo, а затем в своем совете я использовал тип класса, чтобы проверить, можно ли его назначать из этого интерфейса. Если да, то запишите с правдой, иначе с ложью.
@Around("target(org.springframework.data.jpa.repository.JpaRepository)")
public void log(final ProceedingJoinPoint jp){
Class<?> clazz=MyRepoInterface.class;
return clazz.isAssignableFrom(pjp.getTarget().getClass())?log(jp,true):log(pjp,false);
}
----
@Repository
public interface MyRepo extends JpaRepository<Student,Long>,MyRepoInterface{
}
-----
public interface MyRepoInterface{}
поведение spring-aop для некоторых точечных сокращений до сих пор неизвестно. Как и использование AND , NOT с выражением, похоже, это не работает. Я пробовал несколько упомянутых подходов, но ни один из них не работал.