Springboot Aop使用

1、什么是AOP

AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)

个人理解:Aop是在业务流程中增加新的通用流程时,如添加日志,统计方法运行时间等时,做一个拦截,在方法执行前后,都可以做额外的动作,可以做一些无关业务流程的工作如添加日志,也可以对业务流程进行干涉,如对参数进行修改,返回值进行修改等。类似python的装饰器的作用。

2、Aop工作流程

1、定义一个切点Pointcut,切点主要是用来定义在哪里切入,比如对所有的get请求进行请求前添加日志,那么就需要针对所有@GetMapping注解的方法进行拦截

2.定义一个针对切点的处理方法Advice,这个主要来定义什么时候进行处理,方法执行前,方法执行后(专业术语:前置和后置处理)

3.代码实现

1、引入依赖


    org.springframework.boot
    spring-boot-starter-aop

2、创建AOP类

 使用@Aspect进行注解,来标记是一个切面类

3、定义切点方法

@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")

可以使用上边注解直接加切入点表达式的方式,也可以使用下边方式进行切入点表达式重用

使用@Pointcut进行方法注解,注解参数表明是对什么方法或类等进行拦截

该注解有两种类型的参数:一个是使用 execution(),另一个是使用 annotation()

execution表达式可以详细参考

execution表达式详解

execution (* com.sample.service.impl..*.*(..))

annotation() 方式是针对某个注解来定义切点

@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

Springboot Aop使用_第1张图片

4、定义处理方法

有5个注解

1.@Before  标注的方法在切面切入目标方法之前执行

2.@After   标注的方法在切面切入目标方法之后执行

3.@Arround  可以自由选择增强动作与目标方法的执行顺序,也就是说可以在增强动作前后,甚至过程中执行目标方法,因为被@Arround标注的方法第一个形参必须是ProceedingJoinPoint类型,调用ProceedingJoinPoint参数的procedd()方法才会执行目标方法,这个方法的调用时机自己可以进行控制,没有调用ProceedingJoinPointproceed方法,则目标方法不会执行。

@Around可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值

调用ProceedingJoinPointproceed方法时,还可以传入一个Object[ ]对象,该数组中的值将被传入目标方法作为实参,所以可以改变执行目标方法的参数。传入的Object[ ]数组长度与目标方法所需要的参数必须相等。

4、@AfterReturning 注解和 @After 有些类似,区别在于 @AfterReturning 注解可以用来捕获切入方法执行完之后的返回值,对返回值进行业务逻辑上的增强处理

5、当被切方法执行过程中抛出异常时,会进入 @AfterThrowing 注解的方法中执行,在该方法中可以做一些异常的处理逻辑

6、多个

5、示例

// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
    
    @Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
    }

    @After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }

    @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
    }

    @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
    }
    
    @Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标对象(连接点)方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }
    
}

 
package com.ljx.splearn.AopDemo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author lijianxi
 * @date 2022年11月09日 10:58 上午
 */
@Order(0)
//标注该类是切面类
@Aspect
//交给spring容器
@Component
public class LogAdvice {
    //@Pointcut 注解定义什么时机进行拦截,要拦截的是什么东西,一个是使用 execution(),另一个是使用 annotation()。
    //annotation() 方式是针对某个注解来定义切点,execution指明哪些类或包或方法被执行的表达式
    //表明什么时机进行切入,本例中是被getmapping注解到的方法被调用时(参数是一个注解,表示被该注解标注的方法调用时进行切入)
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void pointCut(){}
    public long t1 ;
    public long t2;

    //指定的方法在切面切入目标方法之前执行,注入后做什么动作,参数是切点方法
    @Before("pointCut()")
    public void logAdvice(JoinPoint joinPoint){

        // 获取签名
        Signature signature = joinPoint.getSignature();
        // 获取切入的包名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取即将执行的方法名
        String funcName = signature.getName();
        System.out.println("执行前开始记录");
        t1 = System.currentTimeMillis();
    }

    //指定的方法在切面切入目标方法之后执行
    @After("pointCut()")
    public void logAdvice1(JoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        String name =(String) args[0];
        System.out.println("结束记录1"+name);
        t2=System.currentTimeMillis();
        System.out.println("执行时间"+(t2-t1));
    }
    //自由选择增强动作与目标方法的执行顺序
    @Around("pointCut()")
    //方法参数必须是ProceedingJoinPoint,而不能是JoinPoint
    public Object logAdvice2(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取请求参数
        Object[] args = joinPoint.getArgs();
        String name =(String) args[0];
        System.out.println("结束记录2"+name);
        t2=System.currentTimeMillis();
        System.out.println("执行时间1"+(t2-t1));
        //修改参数
        args[0]="王五";
        joinPoint.proceed(args);
        //修改返回值
        return "hello ,zhangsan";
    }

    /**
     * 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
     * @param joinPoint joinPoint
     * @param result result
     * 属性 returning 的值必须要和参数保持一致
     */
    @AfterReturning(pointcut = "pointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {

        Signature signature = joinPoint.getSignature();
        String classMethod = signature.getName();
        System.out.println((String.format("方法%s执行完毕",classMethod)));
        System.out.println(result);
        // 实际项目中可以根据业务做具体的返回值增强
        System.out.println("对返回参数进行业务上的增强:{}"+result + "增强版");
    }
    /**
     * 在上面定义的切面方法执行抛异常时,执行该方法
     * @param joinPoint jointPoint
     * @param ex ex
     */
    @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        Signature signature = joinPoint.getSignature();
        String method = signature.getName();
        // 处理异常的逻辑
        System.out.println((String)(String.format("执行方法{}出错,异常为:{}", method, ex)));
    }



}
package com.ljx.splearn.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Time;

/**
 * @author lijianxi
 * @date 2022年11月09日 10:55 上午
 */
@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/hello")
    String sayHello(String name) throws InterruptedException {
        System.out.println("传递过来参数是"+name);
        Thread.sleep(2000);
        return "hello"+name;
    }
}

发起请求,参数是lisi,中间对参数修改传递到controller层时是王五,最终结果被@Around修改

最终返回是zhangsan

Springboot Aop使用_第2张图片

 日志打印

Springboot Aop使用_第3张图片

你可能感兴趣的:(Springboot,spring,boot,spring,java)