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

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

1. обзор

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

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

А если вы хотите углубиться в выражения pointcut, посмотритеyesterdays intro to these.

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

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

@Configuration
@ComponentScan(basePackages = {"org.example.dao", "org.example.aop"})
@EnableAspectJAutoProxy
public class TestConfig {
    ...
}

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 будет выполнен перед любым методом репозитория, определенным pointcutrepositoryMethods.

4. После совета

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

В некотором смысле он похож на блокfinally. Если вам нужно, чтобы совет запускался только после нормального выполнения, вы должны использоватьreturning advice, объявленный аннотацией@AfterReturning. Если вы хотите, чтобы ваш совет запускался только тогда, когда целевой метод генерирует исключение, вам следует использоватьthrowing advice,, объявленный с помощью аннотации@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));
    }
}

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

Затем мы создаем слушателя, который будет просто регистрировать событие. Вы можете прочитать больше о событиях вthis tutorial:

@Component
public class FooCreationEventListener implements ApplicationListener {

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

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

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

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

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

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

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

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

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

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

6. обзор

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

Реализация всех этих примеров и фрагментов кода можно найти вmy github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.