Spring 框架自诞生之日就拯救我等程序员于水火之中, 它有两大法宝, 一个是 IoC 控制反转, 另一个便是 AOP 面向切面编程. 今日我们就来破一下它的 AOP 法宝, 以便以后也能自由使出一手 AOP 大法.
AOP 全名 Aspect-oriented programming 面向切面编程大法, 它有很多兄弟, 分别是经常见的面向对象编程, 朴素的面向过程编程和神秘的函数式编程等. 所谓 AOP 的具体解释, 以及和 OOP 的区别不清楚的同学可以自行去了解.
AOP 实现的关键在于 AOP 框架自动创建的 AOP 代理, AOP 代理主要分为静态代理和动态代理. 本文就主要讲解 AOP 的基本术语, 然后用一个例子让大家彻底搞懂这些名词, 最后介绍一下 AOP 的两种代理方式:
以 AspectJ 为代表的静态代理.
以 Spring AOP 为代表的动态代理.
基本术语
(1) 切面 (Aspect)
切面是一个横切关注点的模块化, 一个切面能够包含同一个类型的不同增强方法, 比如说事务处理和日志处理可以理解为两个切面. 切面由切入点和通知组成, 它既包含了横切逻辑的定义, 也包括了切入点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
可以简单地认为, 使用 @Aspect 注解的类就是切面
(2) 目标对象 (Target)
目标对象指将要被增强的对象, 即包含主业务逻辑的类对象. 或者说是被一个或者多个切面所通知的对象.
(3) 连接点 (JoinPoint)
程序执行过程中明确的点, 如方法的调用或特定的异常被抛出. 连接点由两个信息确定:
方法 (表示程序执行点, 即在哪个目标方法)
相对点 (表示方位, 即目标方法的什么位置, 比如调用前, 后等)
简单来说, 连接点就是被拦截到的程序执行点, 因为 Spring 只支持方法类型的连接点, 所以在 Spring 中连接点就是被拦截到的方法.
(4) 切入点 (PointCut)
切入点是对连接点进行拦截的条件定义. 切入点表达式如何和连接点匹配是 AOP 的核心, Spring 缺省使用 AspectJ 切入点语法.
一般认为, 所有的方法都可以认为是连接点, 但是我们并不希望在所有的方法上都添加通知, 而切入点的作用就是提供一组规则 (使用 AspectJ pointcut expression language 来描述) 来匹配连接点, 给满足规则的连接点添加通知.
上边切入点的匹配规则是 com.remcarpediem.test.aop.service 包下的所有类的所有函数.
(5) 通知 (Advice)
通知是指拦截到连接点之后要执行的代码, 包括了 "around","before" 和 "after" 等不同类型的通知. Spring AOP 框架以拦截器来实现通知模型, 并维护一个以连接点为中心的拦截器链.
(6) 织入 (Weaving)
织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程. 织入可以在编译时, 类加载时和运行时完成. 在编译时进行织入就是静态代理, 而在运行时进行织入则是动态代理.
(7) 增强器 (Adviser)
Advisor 是切面的另外一种实现, 能够将通知以更为复杂的方式织入到目标对象中, 是将通知包装为更复杂切面的装配器. Advisor 由切入点和 Advice 组成.
Advisor 这个概念来自于 Spring 对 AOP 的支撑, 在 AspectJ 中是没有等价的概念的. Advisor 就像是一个小的自包含的切面, 这个切面只有一个通知. 切面自身通过一个 Bean 表示, 并且必须实现一个默认接口.
深入理解
看完了上面的理论部分知识, 我相信还是会有不少朋友感觉 AOP 的概念还是很模糊, 对 AOP 的术语理解的还不是很透彻. 现在我们就找一个具体的案例来说明一下.
简单来讲, 整个 aspect 可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作 . 我们来看下边这个例子.
上边这段代码是一个简单的日志相关的切面, 依次定义了切入点和通知, 而连接点作为 log 函数的参数传入进来, 进行一定的操作, 比如说获取连接点函数的名称, 参数等.
静态代理模式
静态代理是指 AOP 框架在编译阶段生成 AOP 代理类, 因此也称为编译时增强. ApsectJ 是静态代理的实现之一, 也是最为流行的. 静态代理由于在编译时就生成了代理类, 效率相比动态代理要高一些. AspectJ 可以单独使用, 也可以和 Spring 结合使用.
动态代理模式
与静态代理不同, 动态代理就是说 AOP 框架不会去修改编译时生成的字节码, 而是在运行时在内存中生成一个 AOP 代理对象, 这个 AOP 对象包含了目标对象的全部方法, 并且在特定的切点做了增强处理, 并回调原对象的方法.
Spring AOP 中的动态代理主要有两种方式: JDK 动态代理和 CGLIB 动态代理.
JDK 代理通过反射来处理被代理的类, 并且要求被代理类必须实现一个接口. 核心类是 InvocationHandler 接口 和 Proxy 类.
而当目标类没有实现接口时, Spring AOP 框架会使用 CGLIB 来动态代理目标类. CGLIB(Code Generation Library), 是一个代码生成的类库, 可以在运行时动态的生成某个类的子类.
CGLIB 是通过继承的方式做的动态代理, 因此如果某个类被标记为 final, 那么它是无法使用 CGLIB 做动态代理的. 核心类是 MethodInterceptor 接口和 Enhancer 类
后记
AOP 的基础知识都比较枯燥, 本人也不擅长概念性的文章, 不过下一篇文章就是 AOP 源码分析了, 希望大家可以继续关注.
来源: http://www.tuicool.com/articles/6RVveqF