上篇讲到了代理模式出现的原因,实现方式以及跟其他相似设计模式的区别。传送门@_@ http://blog.youkuaiyun.com/wonking666/article/details/79497547
1.静态代理的不足
设计模式里面的代理模式,代理类是需要手动去写的。但是手写代理的问题颇多
1.如果不同类型的目标对象需要执行同样一套代理的逻辑,比如说在方法调用前后打印参数和结果,那么仍然需要为每一个类型写一个代理类,将会产生大量的样板代码,书写起来非常枯燥
2.一个类中会有多个方法,对于不需要拦截的方法,我们还是要手动调一下目标对象对应的方法,虽然只有一行代码,但写多了还是感觉蠢蠢哒
3.还有,手写的代理类是静态代理,编译之后就代码就定死了,没办法做到动态的变化,缺少一些灵动
2.对现存问题的思考
回想我们使用代理类最初的目的,就只是为了拦截某个方法的调用,在其前后执行一些额外逻辑,额外逻辑和目标方法并不具有强关联。方法调用会形成一个调用栈,可以把代理想象为是在这个栈上切了一刀,在切口上做的逻辑,我们叫切面逻辑
于是聪明的程序员想到,将纯净的切面逻辑抽出来,再定义一套切入规则,然后让工具自动生成代理类,岂不是美滋滋
所以说代理几乎天生是用来做切面的,二者有着剪不断理还乱的关系
PS: 从分析来看,AOP只需要关注3点即可:切谁?什么时候切?在切口上做什么?
最讨厌那些整一大套花里胡哨的理论东西了,什么Joinpoint,Pointcut,Advice,Before/After/Around Advice,取的名字这么不形象,故作高深只会误导吃瓜群众,哼,我掀了你的小板凳 (╯—﹏—)╯(┷━━━┷
3.如何自动生成代理
我们知道,一个类从编写,到运行时调用,中间大概会经过这几个步骤
所以生成代理可以有三个思路,一,在编译期修改源代码;二,在字节码加载前修改字节码;三,在字节码加载后动态创建代理类的字节码
类别 | 机制 | 原理 | 优点 | 缺点 | 技术 |
静态AOP | 静态织入 | 在编译期,切面直接以字节码的形式编译到目标字节码文件中 | 对系统无性能影响 | 灵活性不够 | AspectJ |
动态AOP | 动态代理 | 在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中 | 相对于静态AOP更加灵活 | 切入的关注点需要实现接口。对系统有一点性能影响 | JDK dynamic proxy |
动态字节码生成 | 在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中 | 没有接口也可以织入 | 扩展类的实例方法为final时,则无法进行织入 | cglib | |
自定义类加载器 | 在运行期,目标加载前,将切面逻辑加到目标字节码里 | 可以对绝大部分类进行织入 | 代码中如果使用了其他类加载器,则这些类将不会被织入 | ||
字节码转换 | 在运行期,所有类加载器加载字节码前,前进行拦截 | 可以对所有类进行织入 |
4.AspectJ生成静态代理
AspectJ 是 Java 语言的一个 AOP 实现,其主要包括两个部分:第一个部分定义了如何表达、定义 AOP 编程中的语法规范,通过这套语言规范,我们可以方便地用 AOP 来解决 Java 语言中存在的交叉关注点问题;另一个部分是工具部分,包括编译器、调试工具等
下载、安装 AspectJ 比较简单,读者登录 AspectJ 官网(http://www.eclipse.org/aspectj),即可下载到一个可执行的 JAR 包,使用 java -jar aspectj-1.x.x.jar 命令、多次单击“Next”按钮即可成功安装 AspectJ
AspectJ 的用法非常简单,就像我们使用 JDK 编译、运行 Java 程序一样,下面是一个简单的示例
编写一个POJO业务类
public class HelloAspectJ { public void sayHello() { System.out.println("Hello AspectJ"); } public static void main(String[] args)