1. 什么是 AOP?
Aspect Orentied Programming:面向切面编程
Object Orentied Programming:面向对象编程
AOP 编程是以 OOP 为基础,OOP 侧重点是对象抽象和封装,AOP 侧重点时共通处理部分的封装和使用,用于改善共通组件。是对 OOP 的补充和完善
拦截器
功能分离系统中的各种关注点,将核心关注点和横切关注点分离开来。举个例子:开发中为了调试,或在生产环境后为了对系统进行监控,需要为业务需求的实现对象添加日志记录功能,或者,业务方法的执行需要一定的权限限制。如果以面向对象的方式实现,就需要把系统中的每个业务对象都加入日志记录,加入相应的安全检查,那么,这些实现代码就会遍及所有的业务对象中。如果对这些代码进行模块化的组织,简化系统需求与实现之间的对比关系,通过某种方式在适当的时候调用这些代码,那么就不用每个业务方法里都写着相同的代码了
3. 什么时候用AOP
日志功能
安全检查
事务管理
缓存
二、AOP 相关概念
1.Joinpoint
Joinpoint 就是进行织入操作的系统执行点
2.Pointcut
是 Joinpoint的表述方式,指定了系统中符合条件的一组 Joinpoint
3.Advice
Advice 代表的是织入到 Joinpoint 的横切逻辑
分类:
Before Advice:在指定位置之前执行,可以做一些初始化工作,比如设置系统初始值,获取必要系统资源
After Advice
After returning Advice :当 当前 Joinpoint 正常执行完后,比如方法正常返回而没有抛出异常
After throwing Advice:当 当前 Joinpoint 执行过程中抛出异常的情况下,才会执行
After Advice:不管是否正常返回,都会执行,相当于 finally 块一样
Around Advice:可以在 Joinpoint 之前 或者 之后都能执行相应的逻辑,Filter功能就是其中的一种实现。可以用来系统安全验证及检查,简单的日志记录以及系统附加行为的添加
4.Aspect
是对系统中的横切关注点逻辑进行模块化封装的 AOP 概念实体,其可以包含多个 Pointcut 以及相关 Advice定义。
如果将 Aspect 比作 OOP 中的Class,那么 Advice 就相当于 CLass 中的 Method
5.织入和织入器
AspectJ 有专门的编译器来完成织入操作,ProxyFactory 是 Spring AOP 中最通用的织入器
6.目标对象
符合 Pointcut 所指定的条件,在织入过程中被织入横切逻辑的对象
三、Spring AOP 的实现机制
1. 实现机制
spring AOP 属于第二代AOP(动态AOP),采用动态代理机制和字节码生成技术实现,与最初的 AspectJ采用编译器将横切逻辑织入目标对象不同,动态代理机制和字节码生成都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象
2.动态代理
一般的代理模式是有多少需要代理的对象就要写多少代理类,但是jdk 1.3 之后引入了一种称之为动态代理的机制,使用该机制,可以为指定的接口在系统运行期间动态的生成代理对象
实现:
java.lang.reflect.Proxy 类 和 java.lang.reflect.InvocatingHandler 接口。当 Proxy动态生成的代理对象上的相应的接口方法被调用时,对应的InvocationHandler就会拦截相应的方法调用,并进行相应的处理。InvocationHandler 就是实现横切逻辑的地方,是横切逻辑的载体
适用:
只能对实现了相应 Interface 的类使用。如果目标对象没有实现任何 Interface,spring AOP 会尝试使用一个称为 CGLIB 的开源的动态字节码生成类库,为目标对象生成动态的代理对象实例
3.动态字节码生成
原理:对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过重写类扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就OK了。(在系统运行期间动态为目标对象生成相应的扩展子类)
四、静态 AOP
1. 静态 AOP 的优缺点
优点:直接以 Java字节码的形式编译到 Java 类中,java虚拟机可以像通常一样加载 java类运行
缺点:不够灵活,如果需要进行修改,就需要重新编译
2.spring AOP中的 Joinpoint
在spring AOP中,只支持方法级别的 Joinpoint
3.spring AOP中的Pointcut
public interface Pointcut {
Pointcut TRUE;
ClassFilter getClassFilter();//匹配对象
MethodMatcher getMethodMatcher();//匹配方法
static default {
TRUE = TruePointcut.INSTANCE;
}
}
//ClassFilter 接口的作用是对Joinpoint所处的对象进行Class级别的类型匹配
public interface ClassFilter{
boolean maches(Class clazz);//当织入的目标对象的Class类型与Pointcut所规定的类型相符时,将返回true
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
//方法级别的拦截
public interface MethodMatcher {
MethodMatcher TRUE;
boolean matches(Method var1, Class> var2);
boolean isRuntime();// 当不用检查参数时,返回false
boolean matches(Method var1, Class> var2, Object... var3);
static default {
TRUE = TrueMethodMatcher.INSTANCE;
}
}
1.Pointcut的族谱
StaticMethodmacher :isRuntime 返回false,可以进行缓存提高性能
DynamicMethodMacher:isRuntime 返回true,因为每次都要对方法参数进行检查,无法对匹配结果进行缓存
4. spring AOP 中的 Advise
1.Advice类图
per-class 类型的advice就是以下中的advice,这种advice实例可以在目标对象类的所有实现之间共享,通常只是提供方法级别的拦截功能,不会为目标对象类保存任何状态或者添加新的特性
5. spring AOP 中的 Aspect
1.Advisor
在Spring中,Advisor 代表的是Aspect,理论上,Aspect可以有多个 Pointcut 和 多个 Advice,但是,Advisor 通常支持有一个Pointcut 和 一个 Advice
2.PointcutAdvisor 族谱
任何类型的 Pointcut、Advice 都可以通过DefaultPointcutAdvisor来使用
NameMatchMethodPointcutAdvisor 限定了自身可以使用的 Pointcut 类型为 NameMatchMethodPointcut,当通过 Advisor 的 setMappedName 方法设置将被拦截的方法名称的时候,实际上是在操作 NameMatchMethodPointcutAdvisor 所持有的 NameMatchMethodPointcut实例
RegexpmethodPointcutAdvisor 通过正则表达式为其设置相应的Pointcut
DefaultBeanFactoryPointcutAdvisor:其作用是可以通过容器中的Advice注册的beanName来关联对应的Advice,只有当对应的Pointcut匹配成功之后,才去实例化对应的Advice,减少了容器启动初期Advisor 和 Advice之间的耦合性
6.spring AOP 的织入
1. ProxyFactory织入器
根据AdvisedSupport 创建AOPProxy
2.AdvisedSupport:生成代理对象所需要信息的载体
ProxyConfig:定义了生成代理对象应该才去的行为措施
Advised:针对哪些类生成哪种横切逻辑
3.ProxyFactory
ProxyFactory 集AopProxy 和 AdvisedSupport于一身,可以通过ProxyFactory设置生成代理对象所需要的相关信息,也可以通过ProxyFactory 取得最终生成的代理对象。前者是AdvisedSupport的职责,后者是AopProxy的职责
为了重用相关逻辑,Spring Aop 框架在实现的时候,将一些共用的逻辑抽取到了ProxyCreatorSupport 中,它自身就继承了 AdvisorSupport ,所以生成的代理对象的必要信息从其自身就可以拿到。为了简化子类生成不同类型 AopProxy ,ProxyCreatorSupport 内部持有一个 AopProxyFactory 实例。
五、动态AOP
1. 动态AOP 的优缺点
优点:AOP的织入过程在系统运行开始之后进行,而不是预先编译到系统类中,而织入信息大都采用外部 xml 文件格式保存
缺点:因为动态 AOP 的实现大都在类加载或者系统运行期间,采用对系统字节码进行操作的方式来完成织入,难免会造成一定的性能损失
2. 使用
在 Aspect 类上加上 @Aspect 注解
用 @Pointcut 定义切入点
@Before、@AfterThrowing、@AfterReturning、@After、@Around: 用value 指定 Pointcut,指定Aspect
配置文件中要添加
3. 基于Aop 命名空间