Spring系列四:AOP切面编程第三部分

发布于:2025-05-01 ⋅ 阅读:(20) ⋅ 点赞:(0)

🐋AOP-JoinPoint

1.通过JoinPoint可以获取到调用方法的签名
2.其他常用方法

●代码实现
1.com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {
    //给Car配置一个最终通知
    @After(value = "execution(public void Car.run())")
    public void ok4(JoinPoint joinPoint) {
        //演示joinPoint常用的方法
        
        String methodName = joinPoint.getSignature().getName();//获取目标方法名 run

        String simpleName = joinPoint.getSignature().getDeclaringType().getSimpleName();//获取目标方法所属类的简单类名 Car

        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.Car
        String fullName = joinPoint.getSignature().getDeclaringType().getName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.Car

        int modifiers = joinPoint.getSignature().getModifiers();//获取目标方法声明类型(public, private, protected) 1

        Object[] args = joinPoint.getArgs();//获取传入目标方法的参数, 返回一个数组

        Object target = joinPoint.getTarget();//获取被代理的对象 Car对象

        Object aThis = joinPoint.getThis();//获取代理对象自己 代理对象

        System.out.println("ok");

        System.out.println("ok");     
    }
}

🐋返回通知获取结果

1.修改切面类com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {
    //返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方
    //解读
    //1.如果我们希望把目标方法, 执行的结果, 返回给切入方法
    //2.可以在 @AfterReturning 增加属性,  比如 returning = "res"
    //3.同时在切入方法增加 Object res
    //4.注意: returning = "res" 和 Object res 的 res名字一样
    @AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }
}

2.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {

    @Test
    public void smartDogTestByProxy() {

        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans08.xml");

        //这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
        SmartAnimalAble smartAnimalAble =
                ioc.getBean(SmartAnimalAble.class);


        smartAnimalAble.getSum(1f, 2f);


        //System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16
    }
}

🐋异常通知获取异常信息

如何在异常通知方法中获取异常信息

1.修改com.zzw.spring.aop.aspectj.SmartDog

@Component //使用@Component 当spring容器启动时, 将 SmartDog 注入到容器
public class SmartDog implements SmartAnimalAble {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        int res = 1 / 0;//模拟一个算数异常
        System.out.println("方法内部打印result = " + result);
        return result;
    }
}

2.修改切面类com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {
    //异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}    //解读
    //1.如果我们希望把目标方法, 异常信息, 返回给切入方法
    //2.可以在 @AfterThrowing 增加属性,  比如 throwing = "throwable"
    //3.同时在切入方法增加 Throwable throwable
    //4.注意: throwing = "throwable" 和 Throwable throwable 的 throwable名字一样
    @AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }
}

3.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {

    @Test
    public void smartDogTestByProxy() {

        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans08.xml");

        //这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
        SmartAnimalAble smartAnimalAble =
                ioc.getBean(SmartAnimalAble.class);


        smartAnimalAble.getSum(1, 2);


        //System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16
    }
}

🐋环绕通知

需求: 如何使用环绕通知完成其它四个通知的功能.

1.新建com.zzw.spring.aop.aspectj.SmartAnimalAspect2切面类, 并把SmartAnimalAspect切面类注解注释, 避免干扰; 去掉SmartDog的异常代码.

//切面类
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect2注入到容器
public class SmartAnimalAspect2 {

    //演示环绕通知的使用
    //1. @Around 表示这是一个环绕通知[可以完成其它四个通知的功能]
    //2. (value = "execution(public float getSum(float, float))") 切入点表达式
    //3. doAround 表示要切入的方法 - 调用的基本结构 try-catch-finally
    @Around(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        String methodName = joinPoint.getSignature().getName();
        try {
            //1.相当于前置通知完成的事情
            Object[] args = joinPoint.getArgs();
            List<Object> argList = Arrays.asList(args);
            System.out.println("AOP环绕通知[=前置通知]--" + methodName + "方法开始了--参数有: " + argList);
            //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
            result = joinPoint.proceed();
            //2.相当于返回通知完成的事情
            System.out.println("AOP环绕通知[=返回通知]--" + methodName + "方法结束了--结果是: " + result);
        } catch (Throwable throwable) {
            //3.相当于异常通知完成的事情
            System.out.println("AOP环绕通知[=异常通知]--" + methodName + "方法抛出异常--异常对象: " + throwable);
        } finally {
            //4.相当于最终通知完成的事情
            System.out.println("AOP环绕通知[=最终通知]--" + methodName + "方法最终结束了...");
        }
        return result;
    }
}

2.测试com.zzw.spring.aop.aspectj.AopAspectjTest

public class AopAspectjTest {
    @Test
    public void testDoAround() {
        //获取Spring容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans08.xml");

        SmartAnimalAble smartAnimalAble = ioc.getBean(SmartAnimalAble.class);

        smartAnimalAble.getSum(10f, 2f);
    }
}

3.结果

Connected to the target VM, address: '127.0.0.1:60160', transport: 'socket'
AOP环绕通知[=前置通知]--getSum方法开始了--参数有: [10.0, 2.0]
方法内部打印result = 12.0
AOP环绕通知[=返回通知]--getSum方法结束了--结果是: 12.0
AOP环绕通知[=最终通知]--getSum方法最终结束了...

🐋切入点表达式重用

为了统一管理切入点表达式, 我们可以使用切入点表达式重用技术

1.对com.zzw.spring.aop.aspectj.SmartAnimalAspect.java稍作修改

//切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {

    //定义一个切入点, 在后面使用时可以直接引用, 提高了复用性
    @Pointcut(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")
    public void myPointCut() {

    }
 
    //@Before(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")
    //这里我们使用定义好的切入点
    @Before(value = "myPointCut()")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint, 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

    //返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方
    //@AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")
    //使用切入点
    @AfterReturning(value = "myPointCut()", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }

    //异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}
    //@AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
    //直接使用切入点表达式
    @AfterThrowing(value = "myPointCut()", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

    //最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}
    //@After(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")
    //直接使用切入点表达式
    @After(value = "myPointCut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }
}

2.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {

    @Test
    public void smartDogTestByProxy() {

        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans08.xml");

        //这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
        SmartAnimalAble smartAnimalAble =
                ioc.getBean(SmartAnimalAble.class);


        smartAnimalAble.getSum(1, 2);


        //System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16
    }
}

🐋切面类执行顺序

如果同一个方法, 有多个切面在同一个切入点切入, 那么执行的优先级如何控制

●基本语法
import org.springframework.core.annotation.Order;
通过@order(value=n) 来控制. n值越小, 优先级越高

●代码实现
1.新建com.zzw.spring.aop.aspectj.SmartAnimalAspect3.java, 将SmartAnimalAspect2.java注销, 保留SmartAnimalAspect.java

//切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect3 {

    @Before(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint, 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

    //返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方
    @AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }

    //异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}
    @AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect3-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

    //最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}
    @After(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }
}

2.如果这两个类不加@order注解, 那么执行结果如下

SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result = 3.0
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum

3.如果两个类加了@order注解

@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect
@Component 
public class SmartAnimalAspect {
	//内容省略
}
@Order(value = 1)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect
@Component 
public class SmartAnimalAspect3 {
	//内容省略
}

4.那么执行结果如下

SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result = 3.0
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum

💧注意事项和细节

🐧不能理解成: 优先级高的每个消息通知都先执行. 这个方法调用机制和Filter过滤器链式调用类似

🐧方法调用机制如下图所示
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到