AOP的本质就是运用了动态代理
两种动态代理的方式:CGlib和JDK原生动态代理(两种区别在别的文章有记录)
强制全部使用CGLIB动态代理的两种方式:
spring.aop.proxy-target-class=true
@EnableAspectJAutoProxy(proxyTargetClass = true)
本篇主要记录AOP的具体运用
①定义切点
切点表达式(盗图)
表达式由六部分组成:
表达式主体 (方法访问权限(可省略) 返回类型 包路径 类名 方法参数类型)
其中表达式主体有以下这些:
举几个例子:
com.yuwenwen.controller包下的所有类的所有方法,所有方法参数,所有返回类型,都作为切点
@Pointcut("execution(* com.yuwenwen.controller.*.*(..))")
com.yuwenwen.controller包或者子包下的所有类的所有方法,所有方法参数,所有返回类型,都作为切点
@Pointcut("execution(* com.yuwenwen.controller..*.*(..))")
com.yuwenwen.controller包下的YuwenwenAop类的所有方法,所有方法参数,所有返回类型,都作为切点
@Pointcut("execution(* com.yuwenwen.controller.YuwenwenAop.*(..))")
任何包下的所有类的send所有方法参数,都作为切点
@Pointcut("execution(* *.send(..))")
对AnthCheck注解作为切点
@Pointcut("@annotation(com.syan.cloudbiz2.aop.AnthCheck)")
②通知
前置通知 Before:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常
后置通知 After returning:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行
异常通知 After throwing:在连接点抛出异常后执行
最终通知 After:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容
环绕通知 Around:环绕通知围绕在连接点前后,能在方法调用前后自定义一些操作,还需要负责决定是继续处理 join point (调用 ProceedingJoinPoint 的 proceed 方法)还是中断执行
可能很多人看到有这种写法,把切点给单提出来一个方法,也有不提出来方法的,其实他俩的效果是一样的,只不过可能有多种通知都对这一个切点进行操作时,提出来一个方法比较方便
@Pointcut("execution(* com.yuwenwen.controller.*.send(..))")
public void point() {
}
@After("point()")
public void after() {
System.out.println("after advice");
}
@After("execution(* com.yuwenwen.controller.*.send(..))")
public void after() {
System.out.println("after advice");
}
通知的执行顺序
这块@Before@After不做详细说明,就是一个在执行方法前,一个在执行方法后。
重点说一下@Around(环绕)这种通知:
@Around的定义的方法参数必须得有ProceedingJoinPoint
@Around("point()")
public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("@Around:执行目标方法之前...");
Object obj = proceedingJoinPoint.proceed();
System.out.println("@Around:执行目标方法之后...");
return obj
}
以proceedingJoinPoint.proceed()为界限
以上的逻辑就是在before之前执行的逻辑
以下就是执行真正方法后到after之前的逻辑
@AfterThrowing
如果目标方法抛出异常,会走around下部分逻辑,和after逻辑之后才会走afterThrowing的逻辑,而不是抛异常之后直接就走afterThrowing的逻辑
可以对抛出来的异常进行再次封装
@AfterThrowing(value = "point()", throwing = "ex")
public void AfterThrowing(Throwable ex) {
if (ex instanceof RuntimeException) {
throw new RuntimeException();
}
System.out.println("异常通知....");
}
③Joinpoint
Joinpoint在做切面的逻辑的时候是一个很重要的参数
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象
Joinpoint常用方法如下:
方法名 | 功能 |
---|---|
Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs(); | 获取传入目标方法的参数对象 |
Object getTarget(); | 获取被代理的对象 |
Object getThis(); | 获取代理对象 |
@Before("point()")
public void Before(JoinPoint joinPoint) {
String classname = joinPoint.getTarget().getClass().getSimpleName();
String proxy = joinPoint.getThis().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println(
"@before Execute! --class name: " + classname + ", method name: " + methodName + ", proxy name: "
+ proxy + " " + args);
}
打印结果:
@before Execute! --class name: OrderController, method name: send, proxy name: OrderController$$EnhancerBySpringCGLIB$$40fe67e4 [com.sbi.dto.DoIn@6c8ea82a]
问:getTarget和getThis的区别
AOP的本质就是生成代理类放到Spring容器去.
举例说明,对OrderController做AOP,其本质是生成了一个代理类(OrderControllerProxy)放入Spring容器里面,这样就会出现代理对象和被代理对象
getTarget则是OrderController
getThis就是为这个代理对象