美文网首页
SpringBoot AOP的使用

SpringBoot AOP的使用

作者: 砒霜拌辣椒 | 来源:发表于2020-07-27 19:36 被阅读0次

Spring框架核心之一,面向切面编程。

1、使用场景

  1. 系统日志处理
  2. 系统事务处理
  3. 系统安全验证
  4. 系统数据缓存

2、优点

  1. 在不改变原有功能代码的基础上扩展新的功能实现——OCP原则。
  2. 可以简化代码开发提高效率。
  3. 可以将非核心业务代码将业务层抽离。

3、相关概念

  • 切面(aspect):横切面对象,一般为一个具体类对象(可以借助@Aspect声明),可以理解为要植入的新的业务功能,这个功能交给某个类负责,这个类就是切面。
  • 切入点(pointcut):对连接点拦截内容的一种定义,在原有的哪些业务方法上扩展新的业务,可以将切入点理解为方法的集合,可以是1个类或某些类。
  • 连接点(joinpoint):程序执行过程中某个特定的点,一般指被拦截到的的方法,可以简单理解为切入点中的一个具体方法。
  • 通知(advice):拦截到连接点之后只要执行的方法,可以理解为一个业务中的扩展逻辑的若干步骤,先做什么(before),再做什么(afterReturn),最后做什么。
  • 目标对象(target):封装原业务逻辑的对象。
  • 代理对象(proxy):负责调用切面中的方法为目标对象植入新的功能。

4、通知注解

注解 用途
@Pointcut 定义切入点
@Before 目标方法执行之前执行
@After 目标方法执行之后必定执行,无论是否报错
@AfterReturning 目标方法有返回值且正常返回后执行
@AfterThrowing 目标方法抛出异常后执行
@Around 可以获取到目标方法的入参和返回值

5、实现原理

  • 默认使用 Java 动态代理来创建 AOP 代理,这样就可以为任何接口实例创建代理了。
  • 当需要代理的类不是代理接口的时候,Spring 会切换为使用 CGLIB 代理。

6、Maven导包

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

7、定义切面

  1. 创建1个切面类。
  2. 在类上添加注解@Aspect@Component
  3. 如果有多个切面可以使用@Order注解指定顺序,指定的值越小越先执行。

8、定义切入点

可以为指定的beanId或者通过execution表达式来给1个类或某些类定义切入点。并且这两种方式还可以和指定注解一起通过逻辑运算符(||、&&)来决定对连接点是否启用某个通知方法。

  • beanId定义切入点
@Order(10)
@Aspect
@Component
@Slf4j
public class LogAspect {
    /*
    beanId方式定义切入点
     */
    @Pointcut("bean(aopController)")
    public void methods(){}

    /*
    目标方法执行之前执行
     */
    @Before("methods()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("=====Before=====");

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        log.info("URL:[{}]", request.getRequestURL().toString());
        log.info("HTTP_METHOD:[{}]", request.getMethod());
        log.info("IP:[{}]", request.getRemoteAddr());
        log.info("CLASS_METHOD:[{}]", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("ARGS:\r\n{}", JSON.toJSONString(joinPoint.getArgs(), true));
    }  
}
  • execution表达式定义切入点
@Order(1)
@Aspect
@Component
@Slf4j
public class TimeAspect {
    /*
    execution表达式定义切入点
     */
    @Pointcut("execution(public * net.zhaoxiaobin.aop.web..*.*(..))")
    public void methods(){}

    @Around("methods()")
    public Object doAround(ProceedingJoinPoint point) {
        long start = System.currentTimeMillis();

        Object[] args = point.getArgs();
        log.info("=====环绕通知开始=====");

        Object result;
        try {
            result = point.proceed(args);
        } catch (Throwable e) {
            log.error("=====切面捕获异常=====", e);
            JSONObject response = new JSONObject();
            response.put("returnCode", "999999");
            response.put("returnMsg", "失败");
            response.put("timestamp", DateUtil.formatDateTime(new Date()));
            result = response;
        }

        log.info("环绕通知结束:\r\n{}", JSON.toJSONString(result, true));
        long end = System.currentTimeMillis();
        log.info("=====耗时:[{}]ms=====", end - start);
        return result;
    }
}
  • execution表达式解释
符号 含义
第一个 * 符号 表示返回值的类型任意
net.zhaoxiaobin.aop.web AOP所切的服务的包名,即需要进行横切的业务类
包名后面的 .. 表示当前包及子包
第二个 * 表示类名
.*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型
  • 配合使用注解来启用某个通知方法
/*
目标方法执行之后必定执行,无论是否报错
目标方法同时需要@NeedAspect注解的修饰,并且这里(通知)的形参名要与上面注解中的一致
 */
@After("methods() && @annotation(annot)")
public void doAfter(NeedAspect annot) {
    log.info("=====After=====");
    log.debug("=====注解值[{}]=====", annot.value());
}
@RestController
@Slf4j
@RequestMapping("/aop")
public class AopController {

    @PostMapping("/add")
    @NeedAspect("add")
    public JSONObject add(@RequestBody JSONObject request) {
        log.info("=====请求报文=====\r\n{}", JSON.toJSONString(request, true));
        return success();
    }
}

9、说明

  • 不需要添加@EnableAspectJAutoProxy来启用切面,原来SpringMVC中需要在配置中添加<aop:aspectj-autoproxy/>才会使切面生效。SpringBoot只需要导入aop jar包,声明好切面,就自动会为切入点下的所有bean生成代理对象。

  • 环绕通知通过切入点的proceed方法向后调用,先执行Before前置通知方法,接着调用目标方法,如果有其它切面的Order比当前切面的Order更大,则执行其它切面的After、AfterReturning(AfterThrowing)方法,最后获取返回值,环绕通知结束后再执行当前切面的After、AfterReturning(AfterThrowing)相关方法。

执行顺序

代码地址

相关文章

  • Spring AOP实现

    使用SpringBoot实现AOP动态代理 1 使用CGLIB实现AOP动态代理 .properties .xml

  • SpringBoot开发随记--AOP的使用

    SpringBoot开发随记--AOP的使用 Aop原理 1、什么是Aop2、Aop常用术语3、AOP表达式4、A...

  • SpringBoot-AOP

    SpringBoot-AOP 使用AOP统一处理请求日志 1.AOP的概念 AOP:AOP是一种编程范式,与语言无...

  • springboot aop

    springboot怎样使用aop呢?我们知道aop的实现一种是jdk动态代理实现aop,一种是cglib动态代理...

  • SpringBoot使用AOP

    本文介绍SpringBoot中使用Spring AOP。 简介 AOP简介 AOP可能对于广大开发者耳熟能详,它是...

  • springboot 使用AOP

    @Aspect 将该注解标注到一个类,表示为切面类 @Pointcut("execution(public * c...

  • SpringBoot 使用AOP

    前言 AOP的全称是Aspect Oriented Programming,翻译成中文是面向切面编程。它的主要思想...

  • SpringBoot AOP的使用

    Spring框架核心之一,面向切面编程。 1、使用场景 系统日志处理 系统事务处理 系统安全验证 系统数据缓存 2...

  • SpringBoot AOP的使用

    AOP:面向切面编程,相对于OOP面向对象编程Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同...

  • 从redis到分布式架构,通过Redis学AKF划分原则、CAP

    基于SpringBoot AOP面向切面编程实现Redis分布式锁基于SpringBoot AOP面向切面编程实现...

网友评论

      本文标题:SpringBoot AOP的使用

      本文链接:https://www.haomeiwen.com/subject/adjtrktx.html