Перейти к основному содержимому

Введение в типы советов в Spring

· 4 мин. чтения

1. Обзор

В этой статье мы обсудим различные типы рекомендаций по АОП, которые можно создать в Spring.

Совет — это действие, предпринимаемое аспектом в определенной точке соединения. Различные типы советов включают советы «примерно», «до» и «после». Основная цель аспектов — поддержка сквозных функций, таких как ведение журнала, профилирование, кэширование и управление транзакциями.

И если вы хотите углубиться в выражения pointcut, ознакомьтесь с предыдущим введением в эти файлы .

2. Включение советов

С помощью Spring вы можете объявлять рекомендации, используя аннотации AspectJ, но вы должны сначала применить аннотацию @EnableAspectJAutoProxy к вашему классу конфигурации , что позволит поддерживать обработку компонентов, помеченных аннотацией AspectJ @Aspect .

@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
...
}

2.1. Весенний ботинок

В проектах Spring Boot нам не нужно явно использовать @EnableAspectJAutoProxy . Существует специальный AopAutoConfiguration , который включает поддержку Spring AOP, если Aspect или Advice находятся в пути к классам.

3. Перед советом

Этот совет, как следует из названия, выполняется перед точкой соединения. Он не препятствует продолжению выполнения рекомендованного метода, если только не возникнет исключение.

Рассмотрим следующий аспект, который просто регистрирует имя метода перед его вызовом:

@Component
@Aspect
public class LoggingAspect {

private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {};

@Before("repositoryMethods()")
public void logMethodCall(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Before " + methodName);
}
}

Совет logMethodCall будет выполняться перед любым методом репозитория, определенным в pointcut репозиторияMethods .

4. После консультации

Совет After, объявленный с помощью аннотации @After , выполняется после выполнения соответствующего метода, независимо от того, было ли выдано исключение.

В некотором смысле он похож на блок finally . Если вам нужно, чтобы совет запускался только после нормального выполнения, вы должны использовать совет возврата , объявленный аннотацией @AfterReturning . Если вы хотите, чтобы ваш совет срабатывал только тогда, когда целевой метод генерирует исключение, вы должны использовать совет по бросанию, объявленный с помощью аннотации @AfterThrowing .

Предположим, что мы хотим уведомить некоторые компоненты приложения, когда создается новый экземпляр Foo . Мы могли бы опубликовать событие от FooDao , но это нарушило бы принцип единой ответственности.

Вместо этого мы можем добиться этого, определив следующий аспект:

@Component
@Aspect
public class PublishingAspect {

private ApplicationEventPublisher eventPublisher;

@Autowired
public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}

@Pointcut("execution(* *..create*(Long,..))")
public void firstLongParamMethods() {}

@Pointcut("repositoryMethods() && firstLongParamMethods()")
public void entityCreationMethods() {}

@AfterReturning(value = "entityCreationMethods()", returning = "entity")
public void logMethodCall(JoinPoint jp, Object entity) throws Throwable {
eventPublisher.publishEvent(new FooCreationEvent(entity));
}
}

Обратите внимание, во-первых, что с помощью возвращающей аннотации @AfterR мы можем получить доступ к возвращаемому значению целевого метода. Во-вторых, объявив параметр типа JoinPoint, мы можем получить доступ к аргументам вызова целевого метода.

Затем мы создаем прослушиватель, который будет просто регистрировать событие :

@Component
public class FooCreationEventListener implements ApplicationListener<FooCreationEvent> {

private Logger logger = Logger.getLogger(getClass().getName());

@Override
public void onApplicationEvent(FooCreationEvent event) {
logger.info("Created foo instance: " + event.getSource().toString());
}
}

5. Вокруг Совет

Совет вокруг окружает точку соединения, такую как вызов метода.

Это самый сильный совет. Совет вокруг может выполнять пользовательское поведение как до, так и после вызова метода. Он также отвечает за выбор, следует ли перейти к точке соединения или сократить выполнение рекомендуемого метода, предоставив собственное возвращаемое значение или создав исключение.

Чтобы продемонстрировать его использование, предположим, что мы хотим измерить время выполнения метода. Давайте создадим Aspect для этого:

@Aspect
@Component
public class PerformanceAspect {

private Logger logger = Logger.getLogger(getClass().getName());

@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {};

@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
Object retval = pjp.proceed();
long end = System.nanoTime();
String methodName = pjp.getSignature().getName();
logger.info("Execution of " + methodName + " took " +
TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
return retval;
}
}

Этот совет срабатывает, когда выполняется любая из точек соединения, совпадающая с pointcut репозиторияClassMethods .

Этот совет принимает один параметр типа ProceedingJointPoint . Параметр дает нам возможность выполнить действие до вызова целевого метода. В этом случае мы просто сохраняем время начала метода.

Во-вторых, тип возврата совета — Object , поскольку целевой метод может возвращать результат любого типа. Если целевой метод недействителен, будет возвращено значение null . После вызова целевого метода мы можем измерить время, зарегистрировать его и вернуть значение результата метода вызывающей стороне.

6. Обзор

В этой статье мы узнали о различных типах советов в Spring, их объявлениях и реализациях. Мы определили аспекты, используя подход на основе схемы и аннотации AspectJ. Мы также предоставили несколько возможных приложений для консультаций.

Реализацию всех этих примеров и фрагментов кода можно найти в моем проекте на GitHub .