SpringBoot使用AOP简单示例

声明:参考链接https://blog.csdn.net/qq_21033663/article/details/73137207

环境说明:Windows10、IntelliJ IDEA、SpringBoot

准备工作:在pom.xml中引入依赖

<!-- aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

注:下面的实例代码中还涉及到阿里的fastjson依赖,也需要引入;这里不再给出。

 

现有Controller:

 

编写AOP:

通过表达式(模糊)匹配来指定切点:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;

import java.util.Map;


/**
 * AOP切面
 *
 * 以下几个增强的执行顺序是:
 *     1.aroundAdvice(.proceed();语句之前的代码)
 *     2.beforeAdvice
 *     3.被增强方法
 *     4.aroundAdvice(.proceed();语句之后的代码)
 *     5.afterAdvice
 *     6.afterReturningAdvice
 *     7.afterThrowingAdvice(有异常时才会走,无异常不会走此方法)
 *
 *  注: 当被增强方法 或 afterAdvice 或 afterReturningAdvice抛出异常时,会被afterThrowingAdvice
 *      捕获到异常,进而短路走 afterThrowingAdvice方法
 *
 * @author JustryDeng
 * @date 2018/12/17 20:25
 */
@Aspect
@Configuration
public class AopConfig {

    /**
     * 将表达式【* com.aspire.controller.AopController.*(..))】所匹配的所有方法标记为切点,
     * 切点名为 excudeAdvice()
     *
     * 注:execution里的表达式所涉及到的类名(除了基本类以外),其它的要用全类名;干脆不管是不
     *    是基础类,都推荐使用全类名
     *
     * @author JustryDeng
     * @date 2018/12/18 13:43
     */
    @Pointcut("execution(* com.aspire.controller.AopController.*(..))")
    public void excudeAdvice() {
    }

    /**
     * 切点excudeAdvice()的前置增强方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @Before(value = "excudeAdvice()")
    public void beforeAdvice(JoinPoint joinPoint) {
        Object[] paramArray = joinPoint.getArgs();
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> beforeAdvice获取到了被增强方法的参数了,为:" + paramArray);
    }

    /**
     * 切点excudeAdvice()的后增强方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @After("excudeAdvice()")
    public void afterAdvice() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> 后置增强afterAdvice执行了");
    }

    /**
     * 切点excudeAdvice()的后增强方法
     *
     * 注:当被增强方法 或  afterAdvice正常执行时,才会走此方法
     * 注: returning指明获取到的(环绕增强返回的)返回值
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @AfterReturning(value = "excudeAdvice()", returning = "map")
    public void afterReturningAdvice(Map<String, Object> map) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> afterReturningAdvice获得了返回结果 map -> " + map);
    }


    /**
     *  当被增强方法 或 afterAdvice 或 afterReturningAdvice抛出异常时,会被afterThrowingAdvice
     *  捕获到异常,进而短路走 afterThrowingAdvice方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:57
     */
    @AfterThrowing(value = "excudeAdvice()", throwing ="ex")
    public void afterThrowingAdvice(Exception ex) {
        System.out.println("AfterThrowing捕获到了 --->" + ex);
    }


    /**
     *  环绕增强 会在 被增强方法执行完毕后  第一个执行,
     *  所以在绝大多数时候,我们都直接返回thisJoinPoint.proceed();的返回值;
     *  如果此方法返回null,那么@AfterReturning方法获取到的返回值 就会是null
     */
    @Around("excudeAdvice()")
    public Object aroundAdvice(ProceedingJoinPoint thisJoinPoint) {
        String threadName = Thread.currentThread().getName();
        Object obj = null;
        System.err.println(threadName + " -> 环绕增强aroundAdvice --> before proceed()执行了");
        try {
            // 执行被增强方法,并获取到返回值
            // 类似于 过滤器的chain.doFilter(req,resp)方法
            obj = thisJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.err.println(threadName + " -> 环绕增强aroundAdvice --> after proceed()执行了");
        return obj;
    }

}

测试一下:

以debug模式启动项目,使用postman访问测试;依次放行断点,控制台输出:

注:之所以要以debug方式运行访问,来查看控制台输出;是因为如果直接运行访问的话,可能控制台打印出的就不是真实的
         运行顺序,这是因为CPU会对无关的语句进行重排序。

 

通过注解来指定切点:

第一步:自定义一个注解

注:这是一个标志性的注解,因此不需要任何属性。

第二步:在要被增强的方法上使用该注解

第三步:定义切点时,使用注解来定义

注:此时即为:被@annotation(com.aspire.annotation.AdviceOne注解标注的方法,作为切点excudeAdvice()。

注:相比起使用execution来匹配方法,将匹配上的方法作为节点的方式;使用注解@annotation来标记方法的方式更为灵活。

第四步:编写增强器

注:同上,略。

第五步:使用测试(测试方式同上),控制台输出:

 

由此可见,这几个增强器的执行顺序依次为:

 

微笑如有不当之处,欢迎指正
微笑参考链接:
           
https://blog.csdn.net/qq_21033663/article/details/73137207
微笑测试代码托管链接:
           
https://github.com/JustryDeng/CommonRepository
微笑本文已经被收录进《程序员成长笔记(四)》,笔者JustryDeng

猜你喜欢

转载自blog.csdn.net/justry_deng/article/details/85065656