AOP 面向切面编程

AOP 面向切面编程

AOP 面向切面编程

概念

大家都知道OOP,即ObjectOriented Programming,面向对象编程。而本文要介绍的是AOP。AOP是Aspect Oriented Programming的缩写,中译文为面向切向编程。OOP和AOP是什么关系呢?首先:

在OOP的世界中,问题或者功能都被划分到一个一个的模块里边。每个模块专心干自己的事情,模块之间通过设计好的接口交互

OOP和AOP都是方法论。我记得在刚学习C++的时候,最难学的并不是C++的语法,而是C++所代表的那种看问题的方法,即OOP。同样,今天在AOP中,我发现其难度并不在利用AOP干活,而是从AOP的角度来看待问题,设计解决方法。这就是为什么我特意强调AOP是一种方法论的原因

###AOP

Java 语言中,从织入切面的方式上看,存在三种织入方式:编译期织入、类加载期织入和运行时织入。

编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载织入则指通过通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或者JDK动态代理进行切面的织入。

所以,AOP 代理则可分为静态代理和动态代理两大类

  • 静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;典型代表Aspectj。
  • 动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强

静态代理分为:编译时织入(特殊编译器实现)、类加载时织入(特殊的类加载器实现)。

动态代理有 : JDK动态代理(基于接口来实现)、CGlib(基于类实现,弥补JDK动态代理只能代理接口的不足)

名称 代理类型 基本原理 特性
AspectJ 静态代理 编译期织入、LTW
Cglib 动态代理 运行时动态生成,使用字节码处理框架ASM来转换字节码并生成新类 cglib可以动态代理类、接口
JDK动态代理 动态代理 代理类在运行时动态生成,源码级别会调用一个Native方法 只能代理接口

Aspectj

AspectJ 作为 AOP 编程的完全解决方案,主要原理是用asm做字节码替换达到AOP的目的。

AspectJ提供了两种切面织入方式,共三种织入时机,分别为

  1. 通过特殊编译器,在编译期,将Aspectj语言编写的切面织入到Java类中,可以通过Maven或者Ant任务来完成
    1. compile-time:编译期织入,将aspectj类和目标类放在一起用ajc 编译,直接编译出包含织入代码的 .class 文件
    2. post-compile:编译后织入,目标类已经被打成jar包了,这时候也可以用ajc命令将jar再织入一次。增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法
  2. 类加载期织入,也简称LTW(Load Time Weaving):在 JVM 进行类加载的时候做字节码的替换,完成织入

编译期织入可以理解为静态织入,因为在class文件生成后,就已经织入好了。类加载期织入,可以理解为“动态织入”(不同与java动态代理的“动态”),因为这个类替换是在jvm加载类时完成的。

类加载期织入,又有三种方式:

  1. agent方式:使用jdk5 JVMTI技术,将aspectjweaver.jar作为JVMTI的agent启动jvm

  2. 启动时加参数替换默认的系统类加载器(AppClassLoader -> WeavingURLClassLoader):

    -Djava.system.class.loader=org.aspectj.weaver.loadtime.WeavingURLClassLoader

  3. 自定义类加载器:可以自定义类加载器,比如tomcat在加载war包时,可以指定特定的,自定义的类加载器

其中第一种方式对平台的侵入性最小,其实现步骤:

  1. 通过JVM的-javaagent:<path_to_aspectj_lib>/aspectjweaver.jar参数设置LTW的织入器类包,以代理JVM默认的类加载器;
  2. LTW织入器需要一个aop.xml文件,在该文件中指定切面类和进行切面织入的目标类;
  3. 把写好的aspectj编译成class文件,并和xml文件一起放在classpath里面。这样在加载类的时候就会做织入

第二种方式只是替换了AppClassLoader,不适合osgi平台

第三种方式需要自定义类加载器,实现上复杂一些,但是好处是可以更精细化的控制织入哪些应用的类。比如tomcat加载多个war包,通过这种方式,就可以控制某些war做aop织入,这种粒度的控制则在第一种方式中比较难实现

Aspectj LTW 实践

  1. 安装ajc 编辑器

    http://www.eclipse.org/aspectj/downloads.php下载AspectJ(目前发布的最新版为1.8.0),双击下载下来的jar文件,完成AspectJ的安装;然后吧AspectJ安装目录下的lib中的“aspectjrt.jar”复制到JRE安装目录下的“lib\ext”目录中。

​ 至此,已经可以通过使用命令行的方式编写AspectJ程序了——AspectJ安装目录下的bin中的“ajc”为编译器(对应Java的“javac”)

  1. Add /Users/sloong/Library/aspectj/lib/aspectjrt.jarto your CLASSPATH. This small .jar file contains classes required by any program compiled with the ajc compiler.
  2. Modify your PATH to include /Users/sloong/Library/aspectj/bin. This will make it easier to run ajc and ajbrowser.
1
2
3
export PATH="/Users/sloong/Library/aspectj/bin:$PATH"
source ~/.zshrc
ajc -v #可以检测是否安装配置成功

​ -outxmlfile specify alternate destination output of -outxml

Spring AOP

Spring AOP通过JDK Proxy和CGLIB Proxy两种方法实现代理。

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

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

启用CGLib 实现代理,需要增加以下配置

1
spring.aop.proxy-target-class=true

Spring AOP 只能作用于 Spring 容器中的 Bean,它是使用纯粹的 Java 代码实现的,只能作用于 bean 的方法。

参考

深入理解Android之AOP https://blog.csdn.net/innost/article/details/49387395

export PATH=”/Users/sloong/Library/aspectj/bin:$PATH”source ~/.zshrcajc -v #可以检测是否安装配置成功properties


 

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×