动态代理,一般都是通过生成字节码来完成,只是生成的时机不同,而字节码的动态生成技术,不只是应用于动态代理,比如jar包,热替换等技术都是动态字节码的应用。
生成字节码的库比如ASM,这个库基本就是按照字节码规范定义的格式来操作,比较底层,像cglib这种工具是基于asm开发的。再往上的一些工具比如Javassist已经抽象
到和直接编写java文件没太大区别了。
1. 运行时代理
在运行时创代理类的字节码,这种技术有jdk原生的proxy,还有用cglib实现代理
1.1 Jdk代理
这两种代理都是在运行时生成代理类,首先jdk动态代理的用法,比如对XX接口代理,
XX x = (XX)Proxy.newProxyInstance(classLoader, XX, handler);
这句就是创建一个XX的代理类,handler里面一般会组合进XX的一个实现,这样在调用handler的invoke方法的时候就可以调用具体实现的方法。
newProxyInstance动态生成的类其实是一个实现XX接口的类,而且这个类接收handler作为构造方法,这样调用生成的XX实现类的方法时候就可以调用handler的方法,
和写一个简单代理的代码是完全相同的,唯一不同的就是Proxy类是由jdk在运行时动态生成的。
1.2 cglib代理
从原理上讲,cglib代理和jdk代理没有区别,同样是在运行时生成字节码,通过classloader创建实例,不过cglib解决了被代理的target必须是接口的问题
比如现在有个OO类需要被代理,
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OO);
enhancer.setCallback(handler);
OO o = (OO) enhancer.create();
动态生成不是接口的实现类,而是一个继承了OO这个类的代理类,这个代理类重写了OO的public方法,当调用OO的方法的时候,通过调用组合进去的handler的相应方法
来实现,因为是通过继承,重写的方式,所以像static method, protected method, finnal class都没法代理。
1.3 通过jvmti或者jvmagent来实现
jvmagent也是基于jvmti来实现的,jvmti是jvm运行时的一些hook,aspectJ的其中一种织入方式是基于这个实现的,启动的时候必须加载一个javaagent
2. 编译时
java类都需要编译成.class字节码文件,这种动态代理的方式,就是在编译的时候修改了字节码,但是需要特殊的编译器,代表的是aspectJ
2.1 AspectJ
首先,有自己的语法,之前都是用javac编译,现在需要特殊的编译工具,这个maven有aspectj的插件。编译出来的东西也没必要弄个代理类了,直接把代理做的一些事情编码到之前代码的上下文里就可以了。这种方法基本什么都能代理。
然后还需要说明一下,编译时又可以分为两种,一种是和java源文件一起织入,还有一种是织入已经存在的.class字节码文件,本质上没有太大区别