Spring系列四:AOP切面编程第三部分
🐋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();//获取目标方法名 runString simpleName = joinPoint.getSignature().getDeclaringType().getSimpleName();//获取目标方法所属类的简单类名 CarString declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.CarString fullName = joinPoint.getSignature().getDeclaringType().getName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.Carint modifiers = joinPoint.getSignature().getModifiers();//获取目标方法声明类型(public, private, protected) 1Object[] 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 {@Testpublic 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 {@Overridepublic 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 {@Testpublic 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 {@Testpublic 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 {@Testpublic 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过滤器链式调用类似
🐧方法调用机制如下图所示
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇