预备知识
- 反射:可以在运行期间分析某个类,每个类在JVM方法区都有一个Class对象,用来描述这个类的信息,比如这个类有什么属性,什么方法,修饰符等,通过Class对象可以创建对应类的实例,比如通过newInstance();
代理
- 为某个对象提供一个代理,以控制对这个对象的访问,好比明星的经纪人是明星的代理,和明星商量事之前可能需要经过他的经纪人。
- 代理可以将一些非业务逻辑代码交由代理对象进行处理,比如记录日志、记录某个操作耗时。
静态代理
- 通过事先写好的代理类的源代码,在编译阶段生成相应的字节码文件,即在程序运行之前就确定了代理类。
- 创建一个类的代理对象可以通过创建该类的子类,然后重写要代理的方法。
1 |
|
- 如果被代理类实现了接口,还可以通过创建一个实现了被代理类实现的接口的代理类对象。
1 | // 客户端 |
- 优点:将非业务代码抽取出来,减少耦合性。
- 缺点:如果被代理的方法数量较多的话,工作量会较大。
动态代理
- 在编译阶段不会生成字节码文件,在运行期间通过反射创建代理对象。
- 在Java里有JDK动态代理或者CGLIB动态代理两种实现。
JDK动态代理
- 在运行期间,Proxy会生成目标类的代理类的字节码,通过类加载器加载进虚拟机,最终通过反射来创建代理类的实例。代理类默认继承了Proxy,并且实现了目标类实现的接口。
- 要求
- 被代理对象所属的类需要实现接口
- 创建一个处理器InvocationHandler用于集中处理方法调用
- 被代理对象和代理对象是兄弟关系
1 | public interface ITarget { |
CLGLIB动态代理
- 在运行期间通过ASM解析目标类的字节码,进行修改(为了生成被代理对象的子对象),形成新的字节数组,重新通过类加载器ClassLoader加载到虚拟机中,通过反射创建代理对象,此代理对象是被代理对象的子对象。
- 要求
- 被代理对象不能用final修饰,因为需要创建被代理对象的子对象作为代理对象
- 被代理对象和代理对象之间是父子关系
1 | // 创建一个增强器,用来在运行时生成类 |