深入解析静态代理模式的不足与改进思考
上篇讲述了代理模式出现的缘由,也阐述了它的实现方式,还说明了它与其他相似设计模式的差异。传送门@_@

1.静态代理的不足
设计模式中的代理模式,其代理类需要通过手动的方式来编写。然而,手写代理存在着诸多问题。
如果不同类型的目标对象需执行相同一套代理的逻辑,比如在方法调用前后打印参数和结果,那么依然需要为每一个类型撰写一个代理类,这样就会产生大量的样板代码,书写过程非常枯燥。
一个类中存在多个方法。对于那些不需要拦截的方法,我们依然需要手动去调用目标对象所对应的方法。即便这仅仅只有一行代码,但写得多了,还是会让人觉得有些蠢蠢的。
还有,手写的代理类属于静态代理。这种代理类在编译之后,其代码就固定下来了,无法实现动态的变化,缺少一些灵活性。
2.对现存问题的思考
回想我们使用代理类的最初目的,只是为了拦截某个方法的调用。在这个方法调用前后,会执行一些额外逻辑。这些额外逻辑与目标方法没有强关联。方法调用会形成一个调用栈,我们可以把代理想象成是在这个栈上切了一刀,而在切口处做的逻辑,我们称之为切面逻辑。
聪明的程序员想到,把纯净的切面逻辑抽取出来,接着定义一套切入规则,之后让工具自动生成代理类,这样做是很美妙的。
所以说代理通常是天生用于做切面的,代理和切面之间存在着紧密且复杂的关系
从分析得知,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 官网()后,能够下载到一个可执行的 JAR 包。接着,通过使用 java -jar aspectj-1.x.x.jar 命令,并且多次单击“Next”按钮,就可以成功安装 AspectJ。
AspectJ 的用法较为简单。就如同我们运用 JDK 来编译以及运行 Java 程序那样。以下是一个较为简单的示例。
编写一个POJO业务类
public class HelloAspectJ { public void sayHello() { System.out.println("Hello