Introduction to Advice Types in Spring – Spring中的建议类型介绍

最后修改: 2015年 12月 17日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll discuss different types of AOP advice that can be created in Spring.

在这篇文章中,我们将讨论在Spring中可以创建的不同类型的AOP建议。

Advice is an action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. The main purpose of aspects is to support cross-cutting concerns, such as logging, profiling, caching, and transaction management.

建议是一个方面在特定连接点采取的行动。不同类型的建议包括 “周围”、”之前 “和 “之后 “的建议。方面的主要目的是支持跨领域的关注,如日志、分析、缓存和事务管理。

And if you want to go deeper into pointcut expressions, check out the previous intro to these.

如果你想深入了解点状表达式,请查看之前对这些的介绍

2. Enabling Advice

2.有利的建议

With Spring, you can declare advice using AspectJ annotations, but you must first apply the @EnableAspectJAutoProxy annotation to your configuration class, which will enable support for handling components marked with AspectJ’s @Aspect annotation.

使用Spring,你可以使用AspectJ注解来声明建议,但你必须首先@EnableAspectJAutoProxy注解应用于你的配置类,这将启用对处理AspectJ的@Aspect注解的组件的支持。

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

2.1. Spring Boot

2.1.Spring启动

In Spring Boot projects, we don’t have to explicitly use the @EnableAspectJAutoProxy. There’s a dedicated AopAutoConfiguration that enables Spring’s AOP support if the Aspect or Advice is on the classpath.

在Spring Boot项目中,我们不需要明确使用@EnableAspectJAutoProxy。有一个专门的AopAutoConfiguration,如果AspectAdvice在classpath上,就可以启用Spring的AOP支持。

3. Before Advice

3.建议前

This advice, as the name implies, is executed before the join point. It doesn’t prevent the continued execution of the method it advises unless an exception is thrown.

这个建议,顾名思义,在连接点之前执行。它不会阻止它所建议的方法继续执行,除非抛出一个异常。

Consider the following aspect that simply logs the method name before it is called:

考虑下面这个方面,它只是在方法被调用之前记录了方法的名称。

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

The logMethodCall advice will be executed before any repository method defined by the repositoryMethods pointcut.

logMethodCall建议将在任何由repositoryMethods指针定义的存储库方法之前执行。

4. After Advice

4.建议之后

After advice, declared by using the @After annotation, is executed after a matched method’s execution, whether or not an exception was thrown.

After建议,通过使用@After 注解来声明,在匹配的方法执行后执行,无论是否抛出了异常。

In some ways, it is similar to a finally block. In case you need advice to be triggered only after normal execution, you should use the returning advice declared by @AfterReturning annotation. If you want your advice to be triggered only when the target method throws an exception, you should use throwing advice, declared by using the @AfterThrowing annotation.

在某些方面,它类似于finally块。如果你需要建议只在正常执行后被触发,你应该使用@AfterReturningannotation所声明的returning建议。如果你希望你的建议只在目标方法抛出异常时被触发,你应该使用throwing建议,通过使用@AfterThrowing注解来声明。

Suppose that we wish to notify some application components when a new instance of Foo is created. We could publish an event from FooDao, but this would violate the single responsibility principle.

假设我们希望在Foo的新实例被创建时通知一些应用程序组件。我们可以从FooDao发布一个事件,但这将违反单一责任原则。

Instead, we can accomplish this by defining the following aspect:

相反,我们可以通过定义以下方面来实现这一目标。

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

Notice, first, that by using the @AfterReturning annotation we can access the target method’s return value. Second, by declaring a parameter of type JoinPoint, we can access the arguments of the target method’s invocation.

注意,首先,通过使用@AfterReturning注解,我们可以访问目标方法的返回值。其次,通过声明一个JoinPoint类型的参数,我们可以访问目标方法调用的参数。

Next we create a listener which will simply log the event:

接下来我们创建一个监听器,它将简单地记录event

@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. Around Advice

5.围绕建议

Around advice surrounds a join point such as a method invocation.

环绕建议环绕一个连接点,如方法调用。

This is the most powerful kind of advice. Around advice can perform custom behavior both before and after the method invocation. It’s also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by providing its own return value or throwing an exception.

这是最强大的一种建议。Around建议可以在方法调用前后执行自定义行为。它还负责选择是继续进行连接点,还是通过提供自己的返回值或抛出异常来缩短建议方法的执行。

To demonstrate its use, suppose that we want to measure method execution time. Let’s create an Aspect for this:

为了证明其用途,假设我们想测量方法的执行时间。让我们为此创建一个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;
    }
}

This advice is triggered when any of the join points matched by the repositoryClassMethods pointcut is executed.

repositoryClassMethods点cut所匹配的任何一个连接点被执行时,这个建议被触发。

This advice takes one parameter of type ProceedingJointPoint. The parameter gives us an opportunity to take action before the target method call. In this case, we simply save the method start time.

这个建议需要一个ProceedingJointPoint类型的参数。该参数为我们提供了一个在目标方法调用前采取行动的机会。在这种情况下,我们只需保存方法的开始时间。

Second, the advice return type is Object since the target method can return a result of any type. If target method is void, null will be returned. After the target method call, we can measure the timing, log it, and return the method’s result value to the caller.

第二,建议返回类型为Object,因为目标方法可以返回任何类型的结果。如果目标方法是void,null将被返回。在目标方法调用后,我们可以测量时间,记录它,并将方法的结果值返回给调用者。

6. Overview

6.概述

In this article, we’ve learned the different types of advice in Spring and their declarations and implementations. We defined aspects using schema-based approach and using AspectJ annotations. We have also provided several possible advice applications.

在这篇文章中,我们已经了解了Spring中不同类型的建议以及它们的声明和实现。我们使用基于模式的方法和AspectJ注解来定义建议。我们还提供了几种可能的建议应用。

The implementation of all these examples and code snippets can be found in my GitHub project.

所有这些例子和代码片断的实现都可以在我的GitHub项目中找到。