1.Spring AOP含义
面向切面编程也就是面向特定方法编程。
eg:一些登录校验就是一类特定的问题,在这个过程中会使用到登录校验拦截器,它就是对登录校验这类问题的统一处理,所以拦截器就是AOP的一种应用。还有就是统一返回数据,统一处理异常。
AOP是一种思想(Spring两大思想之一),上述都是对AOP思想的实现。它的实现方式有很多,比如:Aspect,CGLIB
2.Spring AOP详解
使用Spring AOP需要引入依赖。
(1)概念:
一个切面包含切点,通知,连接点。
(2)通知类型:
a.前置通知(Before advice):
在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
b.后置返回通知(After returning advice):
在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
c.后置异常通知(After throwing advice):
在方法抛出异常退出时执行的通知。
d.后置通知(总会执行)(After (finally) advice):
当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
e.环绕通知(Around Advice):
环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。需要有返回值。
eg:定义如下的切面
controller层:
访问/test/t1结果:(没有异常抛出)
可以看到执行顺序:先执行around,再执行before;先执行after,再执行around。
访问/test/t2结果:(有异常抛出)
可以看到round方法没有完全执行完毕。在抛异常后AfterReturning不会执行和around不会完全执行完毕(通知中环绕后的代码不会再执行了)。
(3)切点:
在上面用例中,就用到了切点。
通过@PointCut定义切点,其实就是一个共性的提取。在其他类/包中也可以使用这个切点(只需要将属性设置为public),使用时需要使用全限定名称。
eg1:
pt这个切点就表示作用于 com.example.demo.controller包中所有的类。
eg2:当一个包中有多个切面类时,执行顺序默认是按照名称来进行匹配。
这些类中都有doAfter和doBefore方法
eg3:多个切面类时可以指定执行顺序
通过@Order注解:
访问结果:
(4)切点表达式:
a.execution:根据方法签名来匹配
eg:表示作用于com.example.demo.controller包下所有类,返回值为所有类型(最前面的*)
b.annotation:根据注解进行匹配(自定义)
eg:
controller层:
切面1(AspectDemo1):(实现了注解自定义)
切面2(AspectDemo2):(未实现自定义注解)
访问/test/t1:
访问/test/t2:
很明显只有实现了自定义注解的才会执行对应的after和before方法。
3.Spring AOP 原理
Spring AOP是基于动态代理实现的。这里涉及到一个模式:代理模式
什么是代理模式:
为其他对象生成一个代理类以控制对该对象的访问,它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不是直接对方法进行调用,而是通过代理来进行间接调用。
好处:在某些情况下,一个对象不能直接调用另一个对象,但可以通过代理类来进行调用,可以在客户端和目标对象之间起到中介的作用。
代理模式根据代理创建的时期分为两种:
a.静态代理
手动创建代理类/特定工具自动生成源代码再对其进行编译,在程序运行前代理类的.class文件就已经存在了。
这样的方式有个坏处就是,代码写死了,一旦一个目标对象中的目标方法进行增加或减少,那么代理类也会增加或减少目标方法,这样非常不灵活,几乎不用。
eg:我把房子委托给房屋中介,让他帮我租出去,而我现在又告诉他房子也可以买(还可能有其他的业务),这样对中介来说不太方便。
b.动态代理
不需要针对每一个目标对象都单独创建一个代理对象,而是在类加载的时候,由JVM去加载对应的代理对象,也就是说程序在运行时,由JVM动态创建代理对象。
eg:房屋中介不需要提前预测有哪些业务,而是等到业务来了再根据情况创建。
Java中主要有两种方式:
1)jdk动态代理:(只能代理接口,不能代理类)
使用jdk动态代理时,只能代理接口并且同时会设置@EnableAspectJAutoProxy(proxyTargetClass = false(源码中实现)。但是通过配置properties文件设置spring.aop.proxy-target-class=false,此时默认使用jdk动态代理。
2)CGLIB动态代理:(只能代理类,不能代理接口)
使用CGLIB动态代理时,只能代理类并且同时会@EnableAspectJAutoProxy(proxyTargetClass = true。从SpringBoot2.X开始就是默认使用CGLIB代理。