AOP:选择正确的时机进行编织

Posted feng9exe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP:选择正确的时机进行编织相关的知识,希望对你有一定的参考价值。

在本文中,我们将采用三种重要的实现的例子,来实践本文提出的概念。这三种 AOP 实现是 AspectJ,Spring 和 JBoss。通过比较他们在 Weave 时机方面的不同,来获得对于如何选择 Weave 时机进行判定的准则。由于 AspectWerk 已经合并到 AspectJ 中,我们将不再对其进行单独的评论。

对于 AOP 编程而言,程序的主要逻辑部分和 Aspect 功能部分的具体实现都可以采用传统的 OO 技术等实现,这里没有什么新东西。AOP 最为特别并使其相对其它方法具有明显优点的部分就在于它能够以多样的方式将程序中用到的多个方面灵活的 Weave 到一起,形成一个完整的应用程序。因而在学习 AOP 编程时,如何以准确、简洁、灵活的方式将各个不同的方面 Weave 到一起,就成为了我们最需要注意的关键点。接下来,我们将阐述 Weave 操作发生的不同时机,并介绍其适用的场合。

大致上,Weave 操作可以发生在如下几个阶段:

  • 编译时:在对源代码进行编译时,特殊的编译器允许我们通过某种方式指定程序中各个方面进行 Weave 的规则,并根据这些规则生成编译完成的应用程序。
  • 编译后:根据 Weave 规则对已经完成编译的程序模块进行 Weave 操作。
  • 载入时:在载入程序模块的时候进行 Weave 操作
  • 运行时:在程序运行时,根据程序运行时的情况 Weave 程序中的对象和方面

在表 1 中列出了目前几种主流的 AOP 系统所支持的 Weave 操作时机。

技术图片

编译时 Weave

对于普通应用程序而言,在编译时进行 Weave 操作是最为直观的做法。由于源程序中包含了应用的所有信息,因此这种方式通常支持最多种类的联结点。利用编译时 Weave,我们能够使用 AOP 系统进行细粒度的 Weave 操作,例如读取获写入字段。源代码编译之后形成的模块将丧失大量的信息,因此通常采用粗粒度的 AOP 方法。同时,对于传统的编译成为本地代码的语言如 C++、Fortran 等来说,编译完成后的模块往往跟操作系统平台相关,这就给建立统一的编译后、载入时以及运行时 Weave 机制造成了困难。对于编译成为本地代码的语言而言,只有在编译时进行 Weave 最为可行。尽管编译时 Weave 具有功能强大、适应面广泛等优点,但他的缺点也很明显。首先,它需要程序员提供所有的源代码,因此对于模块化的项目就力不从心了。即使能够提供所有模块的源代码,它也造成了程序不能进行增量编译、编译时间变慢等不利之处。

后编译时 Weave

为了解决模块化编程的要求,有些 AOP 框架开始支持后编译时 Weave 的功能。程序员只需要获得编译完成之后的模块,就能进行 Weave 操作。在 AspectJ 中,不管是程序的主逻辑部分还是方面都可以先编译成为模块之后进行 Weave,而且主逻辑部分完全可以采用普通的 JavaC 编译。而在 AspectC 中,进行后编译时 Weave 的要求是所有的程序模块都采用 AspectC 进行编译。可以看出,使用 Java 这样基于虚拟机的语言对于编写 AOP 程序是有优势的。

载入时 Weave

尽管后编译时 Weave 已经解决了不能获得所有源代码时进行 AOP 编程的需要,但是在这个框架流行的时代,我们需要更为灵活的安排我们的 Weave 操作。如果程序的主逻辑部分和 Aspect 作为不同的组件开发,那么最为合理的 Weave 时机就是在框架载入 Aspect 代码之时。因此我们可以看到,在 JBOSS 和 Spring 中都提供了这样的方式进行 Weave 操作。在进行载入时 Weave 时,Weave 操作之后的结果不会被保存。程序的主逻辑部分和 Aspect 部分可以分别进行开发和编译,而 Weave 操作在程序别载入时发生。AspectJ、Spring 和 JBoss 都支持载入时 Weave。在 Spring 和 JBoss 的 AOP 实现中,框架先于应用程序启动,由框架来负责 Weave 操作的进行。而在 AspectJ 中,一个特殊的类加载器被用于这个目的。这个类加载器可以方便的嵌入到框架应用程序中,从而能够为任意的框架提供 AOP 支持。使用 AspectJ 进行载入时 Weave 需要几个步骤:

1. 在编译时为编译器指定 -Xreweavable 选项来使得 AspectJ 编译器在 .class 文件中保存额外的 Weave 相关信息。

2. 在 .jar 文件中添加 META-INF/aop.xml 来指定 Weave 策略。

3. 在运行时指定 AspectJ 提供的类加载器。对于 jdk 5,我们可以为 java 虚拟机指定 -javaagent 选项。而 JDK 1.4 可以通过系统属性 -Djava.system.class.loader 来指定类加载器。

在下图中给出了一个 AspectJ 中配置运行时 Weave 的配置文件 aop.xml,我们在图中给出了详细的注释,感兴趣的读者朋友可以很容易的了解这段代码的用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1:<aspectj>
2:    <aspects>
3:        <!-- 声明两个将要使用的 Aspects -->
4:        <aspect name="com.MyAspect"/>
5:        <aspect name="com.MyAspect.Inner"/>
6:
7:        <!-- 在配置文件中定义一个 Aspect -->
8:        <concrete-aspect name="com.xyz.tracing.MyTracing"
              extends="tracing.AbstractTracing">
9:         <pointcut name="tracingScope" expression="within(org.maw.*)"/>
10:        </concrete-aspect>
11:             
12:        <!-- 使用任何匹配"com..*"的 Aspect 进行 Weave-->
13:        <include within="com..*"/>
14:             
15:        <!-- 不使用任何具有 @CoolAspect 注解的 Aspect 进行 Weave -->
16:        <exclude within="@CoolAspect *"/>
17:
18:    </aspects>
19:
20:    <weaver options="-verbose -XlazyTjp">
21:    <!-- 对 javax.* 包和 org.aspectj.* 包中的内容进行 Weave 操作。并对 foo 包中
22:    所有不具有 @NoWeave 注解的类型进行 Weave 操作。-->
23:        <include within="javax.*"/>
24:        <include within="org.aspectj.*"/>
25:        <include within="([email protected] foo.*) AND foo.*"/>
26:    </weaver>
27:</aspectj>
28:

在载入时进行 Weave 的过程中,AspectJ 有一些必须遵守的限制:

1. 要求所有将要被 Weave 的代码通过 AspectJ 提供的类加载器载入。

2. Aspect 代码必须对 Weave 类加载器可见,也就是说 Aspect 必须由 Weave 类加载器自身或其父加载器载入。

3. 在 Weave 操作发生之前,所有的 Aspect 代码都已经被载入

运行时 Weave

运行时 Weave 可能是所有 Weave 方式中最为灵活的,程序在运行过程中可以为单个的对象指定是否需要 Weave 特定的 Aspect。在 JBoss 项目中,利用运行时 Weave 的特性完成了 JBoss Cache 项目。在 JBoss Cache 中,如果一个对象被放置到 Cache 中,它的状态就将被 CacheAOP 监视并且它的状态会被自动同步到一个分布式的缓存中。如果这个对象不需要被缓存,那么它就和 AOP 不发生任何关系。对它的修改不会引发 Cache 的同步操作。 值得一提的是,尽管 AspectJ 没有明确提供运行时 Weave 的能力,在 AspectJ 中可以通过一个简单的 pattern 实现运行时 Weave。具体请参见 Adrian 的 Blog:http://www.aspectprogrammer.org/blogs/adrian/2005/03/perinstance_asp.html

小结

选择合适的 Weave 时机对于 AOP 应用来说是非常关键的。针对具体的应用场合,我们需要作出不同的抉择。可以看到,AspectJ 为我们提供了最多的选择,即时没有直接支持的运行时 Weave 也可以通过一个简单的模式来实现。在使用 Spring 或 JBoss 提供的 AOP 框架时,我们可以利用 AspectJ 来补足这两个框架的不足之处,从而获得更为灵活的 Weave 策略。

参考资料

  1. 本文中的例子和讨论基于以下 AOP 实现:
  2. Ramnivas Laddad 撰写的 AspectJ in Action(Manning,2003 年)是一本非常好的关于 AspectJ 的书籍。
  3. Adrian 的 Blog是一个深入了解 AOP 以及 AspectJ 的好地方。
  4. developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。
  5. 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

https://www.ibm.com/developerworks/cn/java/j-aop-weave/index.html

以上是关于AOP:选择正确的时机进行编织的主要内容,如果未能解决你的问题,请参考以下文章

AOP 有哪些实现方式?

aop

快速了解阿里开源中间件动态非侵入AOP解决方案 JVM-Sandbox

SpringSpringMVC之详解AOP

Vue3官网-高级指南(十七)响应式计算`computed`和侦听`watchEffect`(onTrackonTriggeronInvalidate副作用的刷新时机`watch` pre)(代码片段

Spring AOP代理对象创建时机