weave机制。它的含义其实是将aspect与它join point选择到的java类编织起来,形成最终的字节码。本质是修改class字节码。
1、分类
1.1 时机
weaving机制根据何时可以分为两类。Build time(编译阶段), load time(加载阶段)。
引用原著中build time的概念:
Build time weaving classes and aspects together during the build process before deploying the application
在编译阶段将classes和aspect编织在一起。它对编译器有要求,必须使用ajc,使用javac无法达到预期的效果。
引用原著中load time的概念:
Load time weaving weaves just in time as the classes and loaded by the VM, obviating any pre-deployment weaving
在加载阶段实时的将classes和aspect编织在一起。避免任何加载之前的编织。即编织过的无需再次编织。
1.2 输入源
Weaving机制根据输入源可以分为两类。Source code, byte code。
引用原著中source code的概念:
source code weaving accepts input in the source code form
输入类型为源码,即.java和.aj文件。
引用原著中byte code的概念:
byte code(binary) weaving that accepts input in byte code form produced by a compiler
输入类型为字节码文件,即javac编译java文件生成的.class文件和ajc编译aspect之后生成的.class文件。
2、build time
build time是指weave发生在编译阶段,它需要ajc编译器,而不是java编译器。
build time根据输入源分为两类,source code, byte code。
2.1 source code
输入源为java文件和aspect文件(包含@Aspect标注的类)。使用ajc进行编译的语法为
ajc javaClass aspectClass
支持批量,即javaClass是jar,aspectClass也是jar,此时它的语法为:
ajc -inpath java.jar -aspectpath aspect.jar
inpath后面紧跟的是Java的jar文件,-aspectpath紧跟的是aspect的jar文件。
2.2 combined
当输入源是混合形式时,即可以在一次编译中,同时传入source code和byte code。它的语法格式为:
ajc java文件 aspect文件 -inpath java.jar -aspectpath aspect.jar
可以同时批量进行,等价于将两条命令拼接一起,去掉第二个ajc。
3、load time
Load time weaving发生的时机在加载类的过程中,它的输入源是已编译的class。它的具体实现技术有两种
- 在JDK1.5以后, 使用JVMTI工具,它的全称是Java virtual machine tools interface。
- 在JDK1.5之前,只能提供特别的类加载器。通常适用于application-server。 略。
下面介绍JVMTI。
3.1 步骤
它需要用户提供额外的配置信息,若不指定,加载每个类都检查是否需要weave,效率会非常低。
配置信息指定aspect,以及join point选择的Java类,配置信息可以存在多份,在加载时,JVMTI会把配置信息整合。
步骤如下:
第一步,使用-javaagent选项,它的语法格式为:
java -javaagent: aspectjweaver.jar [other options] <Main.class>
与普通的java指令多了-javaagent:aspectjweaver.jar命令行。
第二步,虚拟机初始化agent,检查编译路径下的META-INF/aop.xml配置文件,当存在多份时,它会对这些配置文件进行整合。
第三步,Agent列出所有包含和排除条件,类似于@ComponentScan中的include和exclude。
第四步,JVM在加载类时,agent会监听,当类需要编织时,会根据aspect中的规则修改类的字节码文件。此时需aspect在java类之前加载,这个顺序也无需我们指定。
第五步,VM使用编织后的字节码文件。
无论哪种方式,最后生成的字节码是一致的。
3.2 配置信息
配置信息的目的是指定输入源,aspect和Java。aop配置文件的格式分为两个部分,<aspects>对应aspect部分,<weaver>对应java部分。
<aspectj> <aspects></aspects> <weaver></weaver> </aspectj>
3.2.1 aspects
用于定义1到多个aspect,以及它们的顺序。
- 普通的aspect, 设置name属性为aspect类全名。<aspect name=”ch2.SecurityAspect”/>
- 抽象的aspect,若抽象aspect包含抽象的pointcut,可以使用<pointcut>子标签指定,若包含抽象的advice,不能定义这样的抽象aspect
<aspect name=”childAspect” extends=”abstractParentAspect”> <pointcut name=”pointcutName” expression=” execution(* MessageCommunicator.deliver(..))”> </aspect>
在XML中定义pointcut时需要注意两点
- 在xml中定义的pointcut无法收集上下文,即无法使用non kinded pointcut
- 在xml中定义的pointcut必须使用类全名,无法使用import关键字
3. aspect顺序, 设置precedence属性,指定aspect的顺序。Include, exclude子标签类似于@ComponentScan注解中的include,exclude。Include表示包含,exclude表示排除。
<aspect name=”OrderAspect” precedence=”aspectA, aspectB”/>
3.2.2 weaver
用于指定java类的字节码文件。
3.2.2.1 options
编织过程中的选项,例如
- verbose:显示编织的过程
- showWeaverInfo:
- nowarn:不显示警告信息
- -XmessageHandlerClass
- -Xlint:启动建议的警告
大部分都是JVM的options。
3.2.2.2 include & exclude
Include,exclude子标签,含义与同名的子标签相同。
3.2.2.3 dump
生成日志文件,或者称为诊断文件。估计生成出来也看不懂。
示例如下:
<dump within="banking..*" beforeandafter="true"/>
通常情况下是使用spring集成AOP,所以aop.xml基本不会用到,在查看源码时有可能会看到,便于理解源码。