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;
    }

}

Демонстрационный проект на GH


УПД.

Провели некоторые исследования по этой теме и нашли блестящую статью о точечных указателях Spring и AspectJ.

Прежде всего,@withinне должен работать в вашем случае (перехватывая методы суперинтерфейса) - вы должны использовать , к сожалению, указатель pointcut, похоже, сломанSpring AOP: наличие совета с одиночным@targetpointcut заставляет 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 с выражением, похоже, это не работает. Я пробовал несколько упомянутых подходов, но ни один из них не работал.

Другие вопросы по тегам