目录
什么是AOP
AOP的实现方式
静态代理
动态代理
不使用匿名内部类
使用匿名内部类
两种动态代理
AOP相关术语
切点表达式
复用
JoinPoint
开启AOP功能
快速入门
类
接口
通知类型
切面优先级
后置通知中获取结果
异常通知中获取异常
基于XML配置AOP
什么是AOP
面向切面编程,将交叉业务封装成切面,跟核心业务分离,减少重复代码和降低模块之间的耦合度交叉业务:与核心业务无关,但所有业务模块都会调用的公共逻辑
AOP的实现方式
静态代理
实现类实现这个接口,这个实现类的实例就是代理类的属性
代理类在编译阶段生成,在编译阶段将通知织入Java字节码中,也称编译时增强。
AspectJ使用的是静态代理。
缺点:一个接口就要写一个代理类,会类爆炸
动态代理
代理类在程序运行时创建,动态生成class字节码,这个字节码就是代理类,不会生成新类
将目标对象作为代理对象的一个属性
不使用匿名内部类
需要实现InvocationHandler接口
使用匿名内部类
两种动态代理
jdk的Proxy
面向接口,只能增强实现类中接口中存在的方法,得到的对象是JDK代理对象的实例
生成的代理对象只能转换成接口,不能转换成被代理类JDK动态代理的核心是
InvocationHandler
接口和Proxy
类。缺点:目标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
Spring的CGlib
面向父类,可以增强父类的所有方法,得到的对象是被代理对象的子类
优点:目标类不需要实现特定的接口,更加灵活。
什么时候采用哪种动态代理?
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库
两者的区别:
-
jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:asm.jar,它使用字节码增强技术来实现。
-
当目标类实现了接口的时候Spring Aop默认使用jdk动态代理方式来增强方法,没有实现接口的时候使用cglib动态代理方式增强方法。
AOP相关术语
切点表达式
切点表达式用来定义通知(Advice)往哪些方法上切入
切点表达式可以指向类的方法,也可以指向接口的方法,也可以对没有实现接口的类进行切入
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
访问控制权限修饰符:
- 可选项。
- 没写,就是4个权限都包括。
- 写public就表示只包括公开的方法。
返回值类型:
- 必填项。
- * 表示返回值类型任意。
全限定类名:
- 可选项。
- 两个点“..”代表当前包以及子包下的所有类。
- 省略时表示所有的类。
方法名:
- 必填项。
- *表示所有方法。
- set*表示以set开始的所有方法 。
形式参数列表:
- 必填项
- () 表示没有参数的方法
- (..) 参数类型和个数随意的方法
- (*) 只有一个参数的方法
- (*, String) 第一个参数类型随意,第二个参数是String的。
异常:
- 可选项。
- 省略时表示任意异常类型。
复用
//定义一个切入点, 在后面使用时可以直接引用, 提高了复用性@Pointcut(value = "execution(public float spring.aspectj.SmartDog.getSum(float, float)))")public void myPointCut() {}@Before(value = "myPointCut()")public void showBeginLog(JoinPoint joinPoint) {}@AfterReturning(value = "myPointCut()", returning = "res")public void showSuccessEndLog(JoinPoint joinPoint, Object res) {}@AfterThrowing(value = "myPointCut()", throwing = "throwable")public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {}
JoinPoint
JoinPoint joinPoint在底层执行时,由 AspectJ 切面框架给该切入方法传入 joinPoint对象
joinPoint.getSignature()获取方法签名
开启AOP功能
开启基于注解的AOP功能,spring容器在扫描类的时候,查看该类上是否有@Aspect注解,如果有,则给这个类生成代理对象
如果不开启,.getClass()不会得到代理对象,只会得到当前运行的对象(相当于new)
proxy-target-class,ture表示强制使用CGLIB动态代理,默认false,使用JDK动态代理使用注解配置类
默认是false
如果是true,不管目标类是否实现了接口,始终使用CGlib
如果是false,目标类实现接口用jdk proxy,没有实现接口使用CGlib
如果用了jdk proxy,那么生成的代理对象只能转换成接口,不能转换成被代理类
结合前面的两种动态代理思考
快速入门
类
接口
通知类型
确保环绕通知中的返回值与目标方法的返回类型匹配
环绕通知可以完成其他四个通知的功能
环绕是最大的通知,发生异常不会有后置通知和后环绕
切面优先级
如果同一个方法,有多个切面在同一个切入点切入,优先级如何控制
注解@Order(value = n) 来控制,n 越小,优先级越高
前置通知通过@Order来判断顺序
后置通知中获取结果
在@AfterReturning 增加属性 , 比如 returning = "res",同时在切入方法增加 Object res
注意: returning = "res" 和 Object res 的 res名字一致
异常通知中获取异常
基于XML配置AOP
接口、实现类、切面类(注解全部去掉)
<?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"><!--使用XML配置,完成AOP编程--><!--配置一个切面类对象-bean--><bean class="com.hspedu.spring.aop.xml.SmartAnimalAspect" id="smartAnimalAspect"/><!--配置一个SmartDog对象-bean--><bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/><!--配置切面类, 细节一定要引入 xmlns:aop--><aop:config><!--配置切入点--><aop:pointcut id="myPointCut" expression="execution(public float com.hspedu.spring.aop.xml.SmartDog.getSum(float, float)))"/><!--配置切面的前置,返回, 异常, 最终通知--><aop:aspect ref="smartAnimalAspect" order="10"><!--配置前置通知--><aop:before method="showBeginLog" pointcut-ref="myPointCut"/><!--返回通知--><aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/><!--异常通知--><aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/><!--最终通知--><aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/><!--配置环绕通知--><!--<aop:around method=""/>--></aop:aspect></aop:config>
</beans>