spring基于注解的aop

前提:

1.导入依赖:

<!-- spring-aspects会帮我们传递过来aspectjweaver -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.1</version>
        </dependency>

2.准备被代理的目标资源

接口:

public interface Calculator {
   int add(int i, int j);
   int sub(int i, int j);
   int mul(int i, int j);
   int div(int i, int j);
}

实现类:

@Component
public class CalculatorPureImpl implements Calculator {
   @Override
   public int add(int i, int j) {
     int result = i + j;
     System.out.println("方法内部 result = " + result);
     return result;
    }
   @Override
   public int sub(int i, int j) {
     int result = i - j;
     System.out.println("方法内部 result = " + result);
     return result;
    }
   @Override
   public int mul(int i, int j) {
     int result = i * j;
     System.out.println("方法内部 result = " + result);
     return result;
    }
   @Override
   public int div(int i, int j) {
     int result = i / j;
     System.out.println("方法内部 result = " +result);
     return result;
    }
}

操作:

1.配置文件(aop-annotation.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--AOP的注意事项:
    切面类和目标类都要交给ioc容器管理
    切面类必须通过@Acpect注释标识为切面
    在spring的配置文件中设置<aop:aspectj-autoproxy/>来开启基于注解的aop功能
    -->
    <context:component-scan base-package="com.atguigu.spring.aop.annotation"></context:component-scan>
    <!--开启基于注解的aop功能-->
    <aop:aspectj-autoproxy/>
</beans>

1.创建切面类

@Component
@Aspect//将当前组件标识为切面
public class LoggerAspect {
    //切入点的重用使用@PointCut注解,如下:
    @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}
    //前置通知
    //@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int,int))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint){
        //获取连接点对应的方法的方法名
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("before(前置通知)"+"LoggerAspect,方法"+signature.getName()+",LoggerAspect,参数"+ Arrays.toString(args));
    }

    //后置通知
    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint) {
        //获取连接点对应的方法的方法名
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("After(后置通知)"+"LoggerAspect,方法:"+signature.getName()+"执行完毕");
    }

    //返回通知
    //在返回通知中若想获取目标对象方法的返回值
    // 只需要通过@AfterReturning注解的returning属性
    // 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result) {
        //获取连接点对应的方法的方法名
        Signature signature = joinPoint.getSignature();
        System.out.println("AfterReturning(返回通知)"+"LoggerAspect,方法:"+signature.getName()+"结果:"+result);
    }

    //异常通知
    //在返回通知中若想获取目标对象方法的异常
    //只需要通过@AfterThrowing注解的throwing属性
    //就可以将通知方法的某个参数指定为接收目标对象方法的出现的异常的参数
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint,Exception e){
        //获取连接点对应的方法的方法名
        Signature signature = joinPoint.getSignature();
        System.out.println("AfterThrowing(异常通知),"+"LoggerAspect,方法:"+signature.getName()+",异常:"+e);
    }

    //环绕通知
    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象方法的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        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;
    }
}

总结

1.在切面中,要通过指定的注解将方法标识为通知方法

  • @Before:前置通知,在目标方法执行之前执行
  • @After:后置通知,在目标对象方法的finally子句中执行
  • @AfterReturning:返回通知,在目标对象方法返回值之后执行
  • @AfterThrowing:异常通知,在目标对象方法的catch子句中执行,即出现异常才执行

2.切入点表达式:execution(com.atguigu.spring.aop.annotation.CalculatorImpl.(..))

设置在标识统治的注解的value属性中

第一个 * 表示任意的访问修饰符和返回值类型

第二个 * 表示任意的方法

..表示任意的参数

类的地方也可以使用*,表示包下的所有的类

3.重用切入点表达式:

切入点的重用使用@PointCut注解,如下:

@Pointcut("execution(com.atguigu.spring.aop.annotation.CalculatorImpl.(..))")

public void pointCut(){}

使用方式: @Before("pointCut()")

切入点表达式

4.获取连接点的信息:

在通知方法的参数位置,设置joinPoint类型的参数,就可以获取连接点所对应方法的信息

//获取连接点对应的方法的方法名 Signature signature = joinPoint.getSignature(); //获取连接点所对应的方法的参数 Object[] args = joinPoint.getArgs(); 例:

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

5.获取目标方法的返回值:

@AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值

例:

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

6.获取目标方法的异常:

@AfterThrowing中的属性throwing,用来将通知方法的某个形参,接收目标方法的异常

例:

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

7.切面的优先级:

可以通过@Order注解的value属性设置优先级。

默认为integer的最大值

@Order注解的value属性的值越小,优先级越高

8.各种通知执行顺序: Spring版本5.3.x以前:

  • 前置通知
  • 目标操作
  • 后置通知
  • 返回通知或异常通知

Spring版本5.3.x以后:

  • 前置通知
  • 目标操作
  • 返回通知或异常通知
  • 后置通知

9.切面的优先级:

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序:

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用@Order注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

本文章使用limfx的vscode插件快速发布