🐋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过滤器链式调用类似
🐧方法调用机制如下图所示
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇