动静代理在 Java 中有着宽泛的利用,例如:Spring AOP 面向切面编程,Hibernate 数据查问、以及 RPC Dubbo 近程调用等,都有十分多的理论利用@mikechen
目录
- Java 动静代理原理
- JDK 原生动静代理
- CGLib 动静代理实现
- JDK 动静代理与 CGLib 的区别
Java 动静代理原理
依照代理的创立期间,代理类能够分为两种:
- 动态代理:由程序员创立或特定工具主动生成源代码,再对其编译,在程序运行前,代理类的 .class 文件就曾经存在了。
- 动静代理:在程序运行时,能够使用反射机制动态创建代理类的 .class 文件。
动静代理类与动态代理类最次要的不同点是:代理类的字节码不是在程序运行前生成的,而是在程序运行时再虚拟机中程序主动创立的。
动静代理的实现形式很多。 例如:JDK 本身提供的动静代理,就利用了下面提到的反射机制。除了反射,动静代理还能够通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。
简略来说,动静代理是一种行为形式,而 反射或 ASM 只是它的一种实现伎俩而已。
本文我次要详解 Java 动静代理的 2 种支流现形式:JDK 原生动静代理与 CGLib 。
JDK 原生动静代理
JDK Proxy 动静代理的实现无需援用第三方类,只须要实现 InvocationHandler 接口,重写 invoke() 办法即可,整个实现代码如下所示:、
import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy; /*** JDK Proxy 相干示例*/public class ProxyExample {static interface Car {void running();} static class Bus implements Car {@Overridepublic void running() {System.out.println("The bus is running.");}} static class Taxi implements Car {@Overridepublic void running() {System.out.println("The taxi is running.");}} /*** JDK Proxy*/static class JDKProxy implements InvocationHandler {private Object target; // 代理对象 // 获取到代理对象public Object getInstance(Object target) {this.target = target;// 获得代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);} /*** 执行代理办法* @param proxy 代理对象* @param method 代理办法* @param args 办法的参数* @return* @throws InvocationTargetException* @throws IllegalAccessException*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws InvocationTargetException, IllegalAccessException {System.out.println("动静代理之前的业务解决.");Object result = method.invoke(target, args); // 执行调用办法(此办法执行前后,能够进行相干业务解决)return result;}} public static void main(String[] args) {// 执行 JDK ProxyJDKProxy jdkProxy = new JDKProxy();Car carInstance = (Car) jdkProxy.getInstance(new Taxi());carInstance.running();
以上程序的执行后果是:
动静代理之前的业务解决。
The taxi is running.
能够看出, JDK Proxy 实现动静代理的外围是实现 Invocation 接口,咱们查看 Invocation 的源码,会发现外面其实只有一个 invoke() 办法,源码如下:
public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}
这是因为在动静代理中有一个重要的角色,也就是代理器,它用于对立治理被代理的对象,显然 InvocationHandler 就是这个代理器。而 invoke() 办法,则是触发代理的执行办法,咱们通过实现 Invocation 接口来领有动静代理的能力。
CGLib 动静代理实现
CGLIB (Code Generation Library) 是一个基于 ASM 的字节码生成库,它容许咱们在运行时对字节码进行批改、和动静生成 CGLIB 通过继承形式实现代理。
在应用 CGLib 之前,咱们要先在我的项目中引入 CGLib 框架,在 pom.xml 中增加如下配置:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>
CGLib 的实现代码:
package com.mikechen.proxydemo; import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGLibExample { static class Car {public void running() {System.out.println("The car is running.");}} /*** CGLib 代理类*/static class CGLibProxy implements MethodInterceptor {private Object target; // 代理对象 public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();// 设置父类为实例类enhancer.setSuperclass(this.target.getClass());// 回调办法enhancer.setCallback(this);// 创立代理对象return enhancer.create();} @Overridepublic Object intercept(Object o, Method method,Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("办法调用前业务解决.");Object result = methodProxy.invokeSuper(o, objects); // 执行办法调用return result;}} // 执行 CGLib 的办法调用public static void main(String[] args) {// 创立 CGLib 代理类CGLibProxy proxy = new CGLibProxy();// 初始化代理对象Car car = (Car) proxy.getInstance(new Car());// 执行办法car.running();
以上程序的执行后果是:
办法调用前业务解决。
The car is running.
能够看出:
CGLib 和 JDK Proxy 的实现代码比拟相似,都是通过实现代理器的接口,再调用某一个办法实现动静代理的。
惟一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类,来实现动静代理的。
因而,被代理类不能被关键字 final 润饰,如果被 final 润饰,再应用 Enhancer 设置父类时会报错,动静代理的构建会失败。
JDK 动静代理与 CGLib 的区别
1. JDK 动静代理具体实现原理
- 通过实现 InvocationHandler 接口,创立本人的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface ,来创立动静代理;
- 通过反射机制获取动静代理类的构造函数,其惟一参数类型就是调用处理器接口类型;
- 通过构造函数创立动静代理类实例,结构时调用处理器对象作为参数参入。
2. CGLib 动静代理
CGLib 是一个弱小、高性能的 Code 生产类库,能够实现运行期动静扩大 java 类,Spring 在运行期间通过 CGlib 继承要被动静代理的类,重写父类的办法,实现 AOP 面向切面编程。
3. 两者比照
- JDK 动静代理是面向接口的。
- CGLib 动静代理是通过字节码底层继承要代理类来实现(如果被代理类被 final 关键字所润饰,会失败)。
4. 性能比照
- CGLib 所创立的动静代理对象,在理论运行时候的性能要比 JDK 动静代理高不少,有钻研表明,大略要高出10倍;
- CGLib 在创建对象的时候所破费的工夫,比 JDK 动静代理要多很多,有钻研表明,大略要高出8倍。
因而,对于 singleton 的代理对象或者具备实例池的代理,因为无需频繁的创立代理对象,更适宜采纳 CGLib 动静代理,反之,则比拟实用 JDK 动静代理。
以上,是对于 Java 动静代理原理、以及动静代理2 种实现形式的解析。
心愿有所帮忙,谢谢【关注+ 点赞+ 转发】 反对。
作者简介
陈睿 | mikechen , 10年+大厂架构教训,「mikechen 的互联网架构」系列文章作者,专一于互联网架构技术。
浏览「mikechen 的互联网架构」40W 字技术文章合集
Java并发 | JVM | MySQL | Spring | Redis | 分布式 | 高并发
以上合集,关注「mikechen 的互联网架构」公众号,回复 【架构】 即可支付。