АОП: поддержка @annotation и @within в одной точке - PullRequest
1 голос
/ 30 октября 2019

У меня есть аннотация, которая может быть помещена в класс или метод:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface TestAspectAnnotation {
    String[] tags() default {};
}

Я хочу иметь один метод консультирования для обработки как на уровне класса, так и на уровне метода:

@Around(value = "@annotation(annotation) || @within(annotation)", argNames = "pjp,annotation")
public Object testAdvice(ProceedingJoinPoint pjp,
                         TestAspectAnnotation annotation) throws Throwable {

        String[] tags = annotation.tags();

        Stopwatch stopwatch = Stopwatch.createStarted();
        Object proceed = pjp.proceed();
        stopwatch.stop();
        long executionTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);

        sendMetrics(tags, executionTime);

        return proceed;

    }

Это прекрасно работает, когда я аннотирую класс с помощью TestAspectAnnotation(tags="foo").

Однако, если я аннотирую метод, аргумент annotation будет null.

Интересно, что если я поменяю порядок указателей точек ("@within(annotation) || @annotation(annotation)"), у меня возникнет обратная проблема: аннотации на уровне методов будут работать нормально, но аннотации на уровне класса приведут к nullfor для аргумента annotation.

Есть ли способ иметь единую точку и совет для поддержки аннотации как на уровне класса, так и на уровне метода?

1 Ответ

1 голос
/ 30 октября 2019

Есть ли способ иметь одиночный pointcut и совет

У меня недавно была похожая проблема, и я пробовал различные варианты, но безрезультатно. Я закончил тем, что разделил точку «или» на две отдельные точки и вызвал один и тот же метод из обоих советов.

Я создал небольшой демонстрационный проект, чтобы проиллюстрировать рабочее решение, которое я настроил. Я надеюсь, что это поможет вам:

@Component
@Aspect
public class SomeAspect {
    @Around(value = "@within(annotation)", argNames = "pjp,annotation")
    public Object methodAdvice(ProceedingJoinPoint pjp, SomeAnnotation annotation) throws Throwable {
        return logTags(pjp, annotation);
    }

    @Around(value = "@annotation(annotation)", argNames = "pjp,annotation")
    public Object classAdvice(ProceedingJoinPoint pjp, SomeAnnotation annotation) throws Throwable {
        return logTags(pjp, annotation);
    }

    private Object logTags(ProceedingJoinPoint pjp, SomeAnnotation annotation) throws Throwable {
        Stream.of(annotation.tags()).forEach(System.out::println);
        return pjp.proceed();
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface SomeAnnotation {
    String[] tags() default {};
}
@Service
@SomeAnnotation(tags = {"c", "d"})
public class SomeService {
    @SomeAnnotation(tags = {"a", "b"})
    public void test() {
    }
}
@SpringBootApplication
public class Application implements ApplicationRunner {
    @Autowired private SomeService someService;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        someService.test();
    }
}
...