AOP织入时机

AOP最为特别并使其相对其他方法具有明显优点的部分就在于,它能够以多样的方式将程序中用到的多个方面灵活地织入(Weave)到一起,形成一个完整的应用程序。因而在学习AOP编程时,如何以准确、简洁、灵活的方式将各个不同的方面织入到一起,就成为了我们最需要注意的关键点。

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

织入时机

AspectJ

Spring

JBoss

AspectC

编译时

  

编译后

  

加载时

 

运行时

  

 

表1 目前几种主流的AOP系统所支持的织入时机。

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

  • 不同的织入时机的优缺点
    1. 编译时Weave

编译时织入,即在编译Java源代码时,将Aspect源代码织入到Class代码中,如图所示。

 

对于普通应用程序而言,在编译时进行Weave操作是最为直观的做法。由于在源程序中包含了应用的所有信息,因此这种方式通常支持最多种类的联结点。利用编译时Weave,我们能够使用AOP系统进行细粒度的Weave操作,例如读取或写入字段。在源代码编译之后形成的模块将丧失大量的信息,因此通常采用粗粒度的AOP方法。同时,对于传统的编译成为本地代码的语言如C++、Fortran等来说,编译完成后的模块往往跟操作系统平台相关,这就给建立统一的编译后、载入时及运行时Weave机制造成了困难。对于编译成为本地代码的语言而言,只有在编译时进行Weave才最为可行。

尽管编译时Weave具有功能强大、适应面广泛等优点,但它的缺点也很明显。首先,它需要程序员提供所有的源代码,因此对于模块化的项目就有点力不从心了。即使能够提供所有模块的源代码,它也造成了程序不能进行增量编译、编译时间变慢等不利之处。

  1. 编译后Weave

编译后织入,即是将Aspect的源代码,织入到编译后的Class二进制代码中,以形成新的Class代码。

 

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

  1. 加载时Weave

尽管编译后Weave已经解决了在不能获得所有源代码时进行AOP编程的需要的问题,但是在这个框架流行的时代,我们需要更为灵活地安排我们的Weave操作。如果程序的主逻辑部分和Aspect作为不同的组件开发,那么最为合理的Weave时机就是在框架载入Aspect代码之时。如果在进行载入时Weave,则Weave操作之后的结果将不会被保存。程序的主逻辑部分和Aspect部分可以分别进行开发和编译,而Weave操作则在程序载入时发生。

AspectJ、Spring和JBoss都支持载入时Weave。在Spring和JBoss的AOP实现中,框架先于应用程序启动,由框架来负责Weave操作的进行。而在AspectJ中,一个特殊的类加载器被用于这个目的。这个类加载器可以方便地嵌入到框架应用程序中,从而能够为任意的框架提供AOP支持。

    1. 运行时Weave

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

  • AspectJ织入方式

AspectJ是AOP的一种实现,主要原理是用asm做字节码替换来达到AOP的目的,它有三个时机可以切入:

  • 编译期:把aspect类(aop的切面)和目标类(被aop的类)放在一起使用ajc进行编译;
  • 编译后期:目标类已经被编译成class文件,这时候可以使用ajc命令织入aop代码;
  • 类加载期:在jvm加载类的时候,做字节码替换。

其中前两个时间点我们可以理解为静态织入,因为在class文件生成好以后,就已经植入好了。类加载期织入可以理解为动态织入,这个类替换是在jvm加载类的时候完成的。类加载期的织入分为三种:

  1. Agent方式:使用jdk5 JVMTI技术,将aspectjweaver.jar作为JVMTI的agent启动jvm。该方式平台入侵型小,只需要修改java的启动参数, -javaagent:<pathoaspectjlib>/aspectjweaver.jar,然后把写好的aspect编译成class文件,并和配置文件放在classpath里面,这样在加载类的时候就会做织入,不管有多少种自定的classloader都会生效的,这在OSGI平台做织入就特别方便。
  2. 替换类加载器:启动时加参数替换系统默认的类加载器(AppClassLoader->WeacvingURLCalssloader):Djava.system,class.loader = org.aspectj.weaver.loadtime. WeacvingURLCalssloader。该方式不适用于OSGI平台,因为OSGI每个bundle都有自己的clasloader。
  3. 自定义类加载器:可以自定义类加载器,比如tomcat在加载war的时候,可以指定特定的。自定义的类加载器。该方式实现复杂一些,但是好处是可以更加精细化控制织入那些应用的类,比如tomcat加载多个war包,如果自定义类加载器,就可以控制某些war做aop织入。
  • Spring AOP

Spring AOP是基于代理机制的。上文说到,Spring AOP通过JDK Proxy和CGLIB Proxy两种方法实现代理。

如果目标对象没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

如果目标对象实现了一个以上的接口,那么Spring将使用JDK Proxy来实现动态代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值