这是我参与更文挑战的第11天,活动详情查看:更文挑战
AOP走向现实
AOP是一种理念,要实现这种理念通常需要一种实现的方式。与OOP需要相应的语言支持一样,AOP也需要某种语言以帮助实现相应的概念实体,我们统称这些实现AOP的语言为AOL,即Aspect-Oriented Language。
AOL可以与系统实现语言相通,比如,如果系统实现语言为Java,那么,相应AOL也可以为Java。但AOL并非一定要与系统语言相同,它也可以是其他语言,比如AspectJ是扩展自Java的一种AOL,与系统实现语言属于不同的两种语言。
静态AOP
静态AOP,也称第一代AOP,以最初的AspectJ为杰出代表,其特点是,相应的横切关注点以Aspect形式实现之后,会通过特定的编译器,将实现后的Aspect编译并织入到系统的静态类中。比如,AspectJ会使用ajc编译器将各个Aspect以Java字节码的形式编译到系统的各个功能模块中,以达到融合Aspect和Class的目的。
静态AOP的优点是,Aspect直接以Java字节码的形式编译到Java类中,Java虚拟机可以像通常一样加载Java类运行(因为编译完成的Aspect是完全符合Java类的规范的),不会对整个系统的运行造成任何的性能损失。
缺点就是,不够灵活。如果横切关注点需要改变织入到系统的位置,就需要重新修改Aspect定义文件,然后使用编译器重新编译Aspect并重新织入到系统中。
动态AOP
动态AOP,又称为第二代AOP,该时代的AOP框架,大都通过Java语言提供的各种动态特性来实现Aspect织入到当前系统的过程,如JBoss AOP、Spring AOP等框架。在AspectJ融合了AspectWerkz框架之后,也引入了动态织入的行为,从而成为现在Java界唯一一个同时支持静态AOP和动态AOP特性的AOP实现产品。
第二代APO的AOL大都采用Java语言实现,AOP的各种概念实体全部都是普通的Java类,所以很容易开发和集成。Aspect跟Class一样最终 以Class身份作为系统的一等公民存在,与静态AOP最大的不同就是,AOP的织入过程在系统运行开始之后进行,而不是预先编译到系统类中,而且织入信息大都采用外部XML文件格式保存,可以在调整织入点以及织入逻辑单元的同时,不必变更系统其他模块,甚至在系统运行的时候,也可以动态更改织入逻辑。
但动态AOP在引入灵活性以及易用性的同时,也会不可避免地引入相应的性能问题。因为动态AOP的实现产品大都在类加载或者系统运行期间,采用对系统字节码进行操作的方式来完成Aspect到系统的织入,难免会造成一定的运行时性能损失。但随着JVM版本的提升,对反射以及字节码操作技术的更好支持,这样的性能损失在逐渐减少,大多数情况下,这种性能损失是可以容忍的。
Java平台上的AOP实现机制
1.动态代理
JDK1.3之后,引入了动态代理机制,可以在运行期间,为相应的接口动态生成对应的代理对象。所以,我们可以将横切关注点逻辑封装到动态代理的InvocationHandler中,然后在系统运行期间,根据横切关注点需要织入的模块位置,将横切逻辑织入到相应的代理类中。
这种方式实现的缺点就是,所有需要织入横切关注点逻辑的模块类都得实现相应的接口,因为动态代理机制只针对接口有限。
Spring AOP默认情况下采用这种机制实现AOP机能。
2.动态字节码增强
我们知道,Java虚拟机加载的class文件都是符合一定规范的,所以,只要交给Java虚拟机运行的文件符合Java class规范,程序的运行就没有问题。通常的class文件都是从Java源代码文件使用Javac编译器编译而成的,但只要符合Java class规范,我们也可以使用ASM或者CGLIB等Java工具库,在程序运行期间,动态构建字节码的class文件。
使用动态字节码增强技术,即使模块类没有实现相应接口,我们依然可以对其进行扩展,而不用像动态代理那样受限于接口。不过,这种实现机制依然存在不足,如果需要扩展的类以及类中的实现方法等声明为final的话,则无法对其进行子类化的扩展。
自定义类加载器
所有的Java程序的class都要通过相应的类加载器(Classloader)加载到Java虚拟机之后才可以运行。默认的类加载器会读取class文件字节码文件,然后按照class字节码规范,解析并加载这些class文件到虚拟机运行。如果我们能够在这个class文件加载到虚拟机运行期间,将横切逻辑织入到class文件的话,是不是就完成了AOP和OOP的融合呢?
我们可以通过自定义类加载器的方式完成横切逻辑到系统的织入,自定义类加载器通过读取外部文件规定的织入规则和必要信息,在加载class文件期间就可以将横切逻辑添加到系统模块类的现有逻辑中,然后将改动后的class交给Java虚拟机运行。偷梁换柱得漂亮,不是吗
AOP国家的公民
1.Joinpoint
在系统运行之前,AOP的功能模块都需要织入到OOP的功能模块中。所以,要进行这种织入过程,我们需要知道在系统的哪些执行点上进行织入操作,这些将要在其之上进行织入操作的系统执行点就称为Joinpoint。
2.Pointcut
Pointcut概念代表的是Joinpoint的表述方式。将横切逻辑织入当前系统的过程中,需要参照Pointcut规定的Joinpoint信息,才可以知道应该往系统的哪些Joinpoint上织入横切逻辑。
Pointcut指定了系统中符合条件的一组Joinpoint。
3.Advice
Advice是单一横切关注点逻辑的载体,它代表将会织入到Joinpoint的横切逻辑。如果将Aspect比作OOP中的Class,那么Advice就相当于Class中的Method。
按照Advice在Joinpoint位置执行时机的差异或者完成功能的不同,Advice可以分成多种具体形式:1.before Advice 2.After returning Advice 3.After throwing Advice 4. Finally 5.Around Advice 6.Introduction
4.Aspect
Aspect是对系统中的横切关注点逻辑进行模块化封装的AOP概念实体。通常情况下,Aspect可以包含多个Pointcut以及相关Advice定义。
5.织入和织入器
织入过程就是“飞架”AOP和OOP的那座桥,只有经过织入过程之后,以Aspect模块化的横切关注点才会集成到OOP的现存系统中,而完成织入过程的那个“人”就称之为织入器。
Spring AOP使用一组类来完成最终的织入操作,ProxyFactory类则是Spring AOP中最通用的织入器。