Spring的AOP介绍
AOP(全称Aspect Oriented Programming) 即:面向切面编程。
通过预编译方式
和运行期间动态代理
实现程序功能的统一维护的一种技术。AOP是OOP的延续,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率
。
AOP的作用及优势
- 作用:在程序运行期间,不修改源码对已有方法进行增强。
- 优势:
- 减少重复代码。
- 提高开发效率。
- 维护方便。
- AOP的实现方式:动态代理。
- 主要应用在性能监控,事务管理,安全检查,缓存等等。
AOP相关术语
AOP名词 | 解释 |
---|---|
Aspect(切面) | 是切入点和通知的结合,被增强应用到切入点的过程。 |
Joinpoint(连接点) | 所谓连接点是指那些被拦截的点,在Spring中,这些点只能是方法,Spring中只支持方法类型的连接点(类的方法)被增强,这些方法称之为连接点。 |
Pointcut(切入点) | 切入点只能是我们需要对那些方法进行拦截的定义,在类中有很多的方法可以被增强。例:在实际操作过程中,只是增强类的add方法,实际增强的方法称之为切入点。 |
Advice(通知/增强) | 拦截到joinpoint连接点之后要做的事情就是通知,分为前置、后置、环绕、异常、最终通知。(这些通知就是切面要完成的功能) |
前置通知 | 在方法之前执行。 |
后置通知 | 在方法之后执行。 |
异常通知 | 在方法出现异常时通知。 |
最终通知 | 在后置之后执行。 |
环绕通知 | 在方法之前和之后通知。 |
Target(目标对象) | 代理的目标对象。 |
Weaving(织入) | 把切面应用到目标对象来创建新的代理对象的过程,切面在指定的连接点织入到目标对象。 |
Proxy(代理) | 一个类被AOP织入增强后,就产生一个结果代理类。 |
Introduction(引介) | 引介是一种特殊的通知,在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。 |
学习Spring的AOP需要明确的事情
1、开发阶段
- 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。
- 把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP 编程人员来做。
- 在配置文件中,声明切入点与通知间的关系,即切面。:AOP 编程人员来做。
2、运行阶段(Spring 框架完成的)
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
关于代理的选择:在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
AOP的设计原理和思想
AOP横向抽象技术的介绍
例如,我们有一个User类,其中包含一个add()方法,如:
public class User {
//添加用户方法
public void add() {
//业务逻辑
}
}
现在,我们需要对其扩展功能,在添加用户的同时添加日志功能,记录在什么时候添加了那个用户。我们可以采用纵向抽取机制
解决,例如,实现一个扩展类,并使用户User类继承于扩展类,这样我们就可以在添加用户时调用添加日志的方法,如下:
//纵向抽取机制解决
public class BaseUser {
//创建打印日志功能
public void writeLog() {
//添加日志的逻辑
}
}
public class User extends BaseUser {
public void add() {
//用户添加逻辑
//扩展功能,添加日志操作
// 调用已存在的日志打印的功能
super.writeLog();
}
}
不过,这种方法存在一个问题,那就是如果父类的中方法名发生改变,那么在子类中的方法名也需要改变。
为此,我们可以采取AOP横向抽象机制
实现扩展功能,它底层使用的是动态代理的方式实现。此时,为了解决功能扩展的问题,我们可以对add()方法提供一个相应的接口,并实现该接口,如下:
public interface UserDao {
public void add();
}
//该接口的真正实现类
public class UserIml implements UserDao {
public void add() {
//业务方法
}
}
然后,我们需要编写一个代理辅助类,并使其实现InvocationHandler
接口。我们可以在此处完成我们需要添加的扩展方法。
public class UserProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//日志功能添加
method.invoke(object,args);
return null;
}
}
使用动态代理的方式来实现日志添加功能,需要注意的是这里的代理类和实现类是平级关系。在有接口情况下,使用的是JDK的动态代理,在没有接口情况下,使用CGLib的形式进行功能扩展。
下面我们来看看AOP横向抽象机制的具体工作原理,在此之前,我们先来看看关于Java程序执行的相关流程。
Java程序的执行流
其实Java程序运行的过程就是方法调用的过程。
按照时间的序列,可以将方法的调用排成一条线,每个方法的调用则可以看成是Java执行流中一个结点,在AOP的术语中,这个结点称之为JoinPoint(连接点)
。一个Java程序的运行过程,就是若干个连接点连接起来依次执行的过程。
AOP中将每一个方法的调用,即连接点作为编程的入口,针对方法调用进行编程,相当于程序横向切割成若干个面,每个面被称之为横向切面。其中AOP的工作原理是针对方法调用的编程思路。
- AOP是针对切面进行编程的,那么需要选择哪些切面(连接点)作为编程的对象呢?
切面的本质是每一个方法的调用,选择切面实际是选择方法的过程,被选择的切面在AOP术语中被称之为切入点(Point Cut)
,切入点实际上是从所有的连接点(Join Point)
中挑选自己需要的连接点的过程。
在Spring中,AOP如何来捕获方法的调用呢?其实其本身是使用了代理模式的Java程序执行流。
使用代理模式的Java程序执行流
假设在Java代码中,实例对象是通过代理模式创建的代理对象,那么访问实例对象必须通过代理。
代理模式是Java中经常用到的,代理对象可以为某些对象提供除了本身功能外的其他的一些额外的功能。
加入了代理模式的Java程序执行流,使得所有的方法调用都经过了代理对象。对于Spring AOP框架而言,它负责控制着整个容器内部的代理对象
。当我们调用了某一个实例对象的任何一个非final的public方法
时,整个Spring框架都会知晓。既然Spring代理层可以察觉到你所做的每一次对实例对象的方法调用,那么,Spring就有机会在这个代理的过程中插入Spring的自己的业务代码
。
Spring中AOP的工作原理
AOP的编程首先要选择自己需要的连接点(即切入点),AOP对切入如何做编程呢?其实是在代理模式下通过找到某个连接点的细化来实现横向切面编程。
AOP是根据Proxy提供的类型名和方法签名,来确定感兴趣的切入点,则返回advice(通知),然后Proxy会得到这些通知并开始执行这些通知。