Spring:AOP简介
AOP概述
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善。
AOP使用的设计模式是代理模式。
AOP将公共逻辑(缓存,日志, 事务管理)封装成切面,与业务代码分离,可以减少系统的重复代码和减低耦合度。
切面就是指那些与业务无关,但是业务模块都需要调用的公共逻辑。
AOP实现方式(静态与动态)
静态代理和动态代理
静态代理:其实就是代理类和原来的类都实现了一个公共的接口,在代理类调用了被代理类的方法,在调用之前加上一些代码逻辑达到增强的效果,静态代理的代理类在编译阶段就生成了,也称编译时增强。
缺点:代理对象要与目标对象实现同一个接口;一旦这个接口增加方法,目标对象和代理对象都需要维护。
动态代理:代理类在程序运行的时候创建,AOP框架不会去修改字节码,而是在内存之中生成一个代理对象,在运行期间对业务方法进行增强,不会生成新的类。
场景模拟
假设现在有一个计算器接口Calculator,包含加减乘除的抽象方法
1 | public interface Calculator { |
此接口的实现类:
1 | public class CalculatorPureImpl implements Calculator { |
如果此时要给这个计算机加上日志功能呢 ?
正常思维 : 在加减乘除的内部加上日志的输出,例如:
1 | public int add(int i, int j) { |
问题 :
- 对核心业务功能有干扰,日志属于非核心代码。
- 附加功能分散在各个业务功能方法中,不利于统一维护(不利于封装)
解决思路 : 解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
困难 : :要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术 : 代理模式。
代理模式 : 通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来
关于代理模式,后面再详细出一篇文章吧。
静态代理
代理模式为目标对象创建代理对象,调用目标方法通过调用代理对象的方法实现,因此目标对象有啥,代理对象就有啥.
创建静态代理类 :
1 | //由于代理对象拥有目标对象的所有方法,因此代理对象一定要和目标对象实现相同的接口. |
静态代理确实实现了解耦,将核心代码和非核心代码分开.
测试
1 | public class Proxytest{ |
缺点 : 由于代码都写死了,完全不具备任何的灵活性。
就拿日志功能来说,将来其他类也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
动态代理
动态代理的动态 : 指不需要手动创造代理类, 会动态生成目标类的代理类
这里实现的是jdk动态代理(要求必须有接口,最终生成的代理类在com.sun.proxy下,类名为$proxy2)
1 | //ProxyFactory是一个工厂类 |
测试
1 |
|
AOP相关术语
首先明确一下AOP要做的事情 :
- 把非核心代码抽取出来,交给切面管理
- 把它作用到目标对象的方法中
横切关注点
从目标对象(核心代码)中抽取出来的同一类非核心业务。(指前面计算器中的日志功能)
一个方法中,可以有多个横切关注点.
横切关注点是对于目标对象来说的.
通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
我们要把横切关注点封装到切面中,而在切面中,每一个横切关注点都表示为一个通知方法.
通知是针对切面而言的.
通知分为5种 :
前置通知:使用@Before注解标识,在被代理的目标方法前执行
返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行
异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(
后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行
环绕通知:使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包
括上面四种通知对应的所有位置
切面
切面是用来封装横切关注点(对于目标对象而言)或封装通知方法(对于切面而言)的类 .
AOP叫做面向切面编程,就是切面或切面中的通知重要.
目标
被代理的目标对象\
要进行功能增强的对象\
要被抽取非核心代码的对象
代理
向目标对象应用通知之后创建的代理对象。
注意 : 代理是AOP帮助我们创建的,不需要通过上面的静态或动态代理方法自己创建)
连接点
抽取横切关注点的位置.
比如从 方法开始 / 捕获异常 / 方法结束的位置抽取出来的
知道连接点的作用 : 从哪儿抽取出来,就要套到哪儿
切入点
定位连接点的方式。
每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。
知道连接点之后,通过切入点将通知在代码层面套到连接点 .
切入点是一个表达式,可以用切入点定位连接点
小结
AOP到底应该怎么做?
首先,要想实现AOP,那么目标对象一定是提前就有的.(即我们分析之后,有了需要进行功能增强的目标对象,才会去使用AOP)
其次是代理对象,代理对象不需要自己创建
所以我们要做的是在目标对象中把非核心代码抽取出来,这里的非核心代码就是横切关注点
抽完之后把它放在一个类中 ,.这个类叫做切面
在切面中如何封装横切关注点 ? 每一个横切关注点都是一个方法,而这个方法就叫通知
然后再通过切入点,定位到连接点
这时就可以在不改变目标对象代码的同时,把我们切面中的这些通知,通过切入点表达式,来套到连接点上,来实现功能增强
AOP举例
1 | import org.aspectj.lang.annotation.Aspect; |
AOP的作用
- 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
- 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。
AOP的应用场景
- 日志场景:诊断上下文 log4j ; 辅助信息 :方法执行时间等
- 统计场景:方法调用次数, 执行异常次数, 数值累加
- 安防场景:限流降流(阿里的sentinel)
- 性能场景:超时控制