代理模式——JDK动态代理与CGLib原理及对比分析
1.前言
首先回顾下代理模式(Proxy Pattern)的定义:代理模式指为其他对象提供一种代理,以控制这个对象的访问,属于结构型设计模式。其适用于在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端于目标对象之间起到中介的作用。
成都创新互联公司服务项目包括额济纳网站建设、额济纳网站制作、额济纳网页制作以及额济纳网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,额济纳网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到额济纳省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
代理模式主要分为静态代理和动态代理两种方式,静态代理需要手动创建代理类,代理的目标对象是固定的;动态代理使用反射机制,代理的目标对象是活动的,不需要创建代理类即可给不同的目标随时创建代理。本篇重点探究动态代理的实现。
2.JDK动态代理
JDK动态代理采用字节重组,重新生成对象来替代原始对象,以达到动态代理的目的。JDK动态代理生成对象的步骤如下:
- 获取被代理对象的引用,并且获取它的所有接口,反射获取。
- JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。
- 动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现)。
- 编译新生成的Java代码
.class
文件。 - 重新加载到JVM中运行。
2.1 JDK动态代理实现及原理源码解析
实现一个JDK动态代理,方式为实现java.lang.reflect.InvocationHandler
接口,并使用java.lang.reflect.Proxy.newProxyInstance()
方法生成代理对象。
/**
* 要代理的接口
*/
public interface IPerson {
void learn();
}
/**
* 真实调用类
*/
public class Zhangsan implements IPerson {
public void learn() {
System.out.println("==张三学习中间件==");
}
}
/**
* JDK代理类生成
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkInvocationHandler implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class> clazz = target.getClass();
return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target,args);
after();
return result;
}
private void before() {
System.out.println("事前做好计划");
}
private void after() {
System.out.println("事后回顾梳理");
}
}
/**
* 测试
*/
public class TestProxy {
public static void main(String[] args) {
try {
//把生成的字节码保存到本地磁盘,动态生成的类会保存在工程根目录下的 com/sun/proxy 目录里面
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
IPerson obj = (IPerson) new JdkInvocationHandler().getInstance(new Zhangsan());
obj.learn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
看下 Proxy.newProxyInstance
里面究竟发生了什么?
结合流程图,在生成字节码的那个地方,也就是 ProxyGenerator.generateProxyClass()
方法里面,通过代码可以看到(自行查阅,篇幅原因,这里不贴代码),里面是用参数 saveGeneratedFiles
来控制是否把生成的字节码保存到本地磁盘。代码中已经设置保存到本地,现在找到刚才生成的 $Proxy0.class
,反编译打开如下:
import com.zang.jdkproxy.IPerson;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements IPerson {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void learn() throws {
try {
// super.h 对应的是父类的h变量,也就是Proxy.newProxyInstance方法中的InvocationHandler参数
// 所以这里实际上就是使用了我们自己写的InvocationHandler实现类的invoke方法
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.zang.jdkproxy.IPerson").getMethod("learn");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到 $Proxy0
类继承了Proxy
类,里面有一个跟IPerson
一样签名的 learn
方法,方法实现中的super.h.invoke(this, m3, (Object[])null);
,super.h 对应的是父类的h变量,也就是Proxy.newProxyInstance
方法中的InvocationHandler
参数:
package java.lang.reflect;
//import略
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
//
所以这里实际上就是使用了我自己写的InvocationHandler
实现类JdkInvocationHandler
的invoke
方法,当调用 IPerson.learn
的时候,其实它是被转发到了 JdkInvocationHandler.invoke
。至此,整个魔术过程就透明了。
2.2 手写JDK动态代理
使用JDK动态代理的类名和方法名定义以及执行思路,下面来进行手写实现。
创建MyInvocationHandler
接口:
import java.lang.reflect.Method;
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
创建MyProxy类:
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* 自己实现的代理类,用来生成字节码文件,并动态加载到JVM中
*/
public class MyProxy {
public static final String ln = "\r\n";
/**
* 生成代理对象
* @param classLoader 类加载器,用于加载被代理类的类文件
* @param interfaces 被代理类的接口
* @param h 自定义的InvocationHandler接口,用于具体代理方法的执行
* @return 返回被代理后的代理对象
*/
public static Object newProxyInstance(MyClassLoader classLoader, Class>[] interfaces, MyInvocationHandler h) {
try {
//1、动态生成源代码.java文件
String src = generateSrc(interfaces);
//2、Java文件输出磁盘
String filePath = MyProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//3、把生成的.java文件编译成.class文件
//获取Java编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//标注Java文件管理器,用来获取Java字节码文件
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manage.getJavaFileObjects(f);
//创建task,通过java字节码文件将类信息加载到JVM中
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
//开始执行task
task.call();
//关闭管理器
manage.close();
//4、编译生成的.class文件加载到JVM中来
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
f.delete();
//5、返回字节码重组以后的新的代理对象
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成代理类的源代码
*/
private static String generateSrc(Class>[] interfaces) {
StringBuffer sb = new StringBuffer();
sb.append(MyProxy.class.getPackage() + ";" + ln);
sb.append("import " + interfaces[0].getName() + ";" + ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("GPInvocationHandler h;" + ln);
sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()) {
Class>[] params = m.getParameterTypes();
StringBuffer paramNames = new StringBuffer();
StringBuffer paramValues = new StringBuffer();
StringBuffer paramClasses = new StringBuffer();
for (int i = 0; i < params.length; i++) {
Class clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type + " " + paramName);
paramValues.append(paramName);
paramClasses.append(clazz.getName() + ".class");
if (i > 0 && i < params.length - 1) {
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);
sb.append("try{" + ln);
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})", m.getReturnType()) + ";" + ln);
sb.append("}catch(Error _ex) { }");
sb.append("catch(Throwable e){" + ln);
sb.append("throw new UndeclaredThrowableException(e);" + ln);
sb.append("}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
return sb.toString();
}
private static Map mappings = new HashMap();
static {
mappings.put(int.class, Integer.class);
}
private static String getReturnEmptyCode(Class> returnClass) {
if (mappings.containsKey(returnClass)) {
return "return 0;";
} else if (returnClass == void.class) {
return "";
} else {
return "return null;";
}
}
private static String getCaseCode(String code, Class> returnClass) {
if (mappings.containsKey(returnClass)) {
return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
}
return code;
}
private static boolean hasReturnValue(Class> clazz) {
return clazz != void.class;
}
private static String toLowerFirstCase(String src) {
char[] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
创建类加载器MyClassLoader:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassLoader extends ClassLoader {
private File classPathFile;
public MyClassLoader(){
String classPath = MyClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
/**
* 通过类名称加载类字节码文件到JVM中
* @param name 类名
* @return 类的Class独享
* @throws ClassNotFoundException
*/
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
//获取类名
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile != null){
//获取类文件
File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
if(classFile.exists()){
//将类文件转化为字节数组
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
//调用父类方法生成class实例
return defineClass(className,out.toByteArray(),0,out.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
return null;
}
}
实现并测试
/**
* 要代理的接口
*/
public interface IPerson {
void learn();
}
/**
* 真实调用类
*/
public class Zhangsan implements IPerson {
public void learn() {
System.out.println("==张三学习中间件==");
}
}
/**
* JDK代理类生成
*/
public class CustomInvocationHandler implements MyInvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class> clazz = target.getClass();
return (IPerson) MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target,args);
after();
return result;
}
private void before() {
System.out.println("事前做好计划");
}
private void after() {
System.out.println("事后回顾梳理");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
CustomInvocationHandler custom = new CustomInvocationHandler();
IPerson zhangsan = custom.getInstance(new Zhangsan());
zhangsan.learn();
}
}
至此,手写完成,读者也可自行参照实现。
3.CGLib动态代理API原理分析
3.1 CGLib动态代理的使用
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 CustomCGlib implements MethodInterceptor {
public Object getInstance(Class> clazz) throws Exception{
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before() {
System.out.println("事前做好计划");
}
private void after() {
System.out.println("事后回顾梳理");
}
}
这里有一个小细节,CGLib动态代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的,客户端测试代码如下:
public class CglibTest {
public static void main(String[] args) {
try {
Zhangsan obj = (Zhangsan) new CustomCGlib().getInstance(Zhangsan.class);
obj.learn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2 CGLib动态代理的实现原理
CGLib动态代理的实现原理又是怎样的呢?可以在客户端测试代码中加上一句代码,将CGLib动态代理后的.class
文件写入磁盘,然后反编译来一探究竟,代码如下:
//import net.sf.cglib.core.DebuggingClassWriter;
//使用CGLib的代理类可以将内存中的.class文件写入本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_classes");
Zhangsan obj = ···
//···
重新执行代码,再输出目录下会出现三个.class文件,一个是目标(被代理)类的FastClass,一个是代理类,一个是代理类的FastClass。如图:
其中,Zhangsan$$EnhancerByCGLIB$$3d23e0ea.class
就是CGLib动态代理生成的代理类,继承了Zhangsan
类。
package com.zang.cglibproxy;
import java.lang.reflect.Method;
import net.sf.cglib.*;
public class Zhangsan$$EnhancerByCGLIB$$3d23e0ea extends Zhangsan implements Factory {
//···
//传入的MethodInterceptor对象
private MethodInterceptor CGLIB$CALLBACK_0;
//目标类的learn方法对象
private static final Method CGLIB$learn$0$Method;
//代理类的learn方法对象
private static final MethodProxy CGLIB$learn$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
//初始化方法,其中部分代码略
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.zang.cglibproxy.Zhangsan$$EnhancerByCGLIB$$78b");
Class var1;
Method[] var = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
//···
//初始化目标类的learn方法对象
CGLIB$learn$0$Method = ReflectUtils.findMethods(new String[]{"learn", "()V"}, (var1 = Class.forName("com.zang.cglibproxy.Zhangsan")).getDeclaredMethods())[0];
//初始化代理类的learn方法对象
CGLIB$learn$0$Proxy = MethodProxy.create(var1, var0, "()V", "learn", "CGLIB$learn$0");
}
//这里直接调用Zhangsan#learn
final void CGLIB$learn$0() {
super.learn();
}
public final void learn() {
MethodInterceptor var = this.CGLIB$CALLBACK_0;
if (var == null) {
CGLIB$BIND_CALLBACKS(this);
var = this.CGLIB$CALLBACK_0;
}
if (var != null) {
//这里执行拦截器定义逻辑
var.intercept(this, CGLIB$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
} else {
super.learn();
}
}
//···
}
调用过程为:代理对象调用this.learn
方法→调用拦截器→methodProxy.invokeSuper()
→CGLIB$learn$0
→被代理对象learn
方法。
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
public class CustomCGlib implements MethodInterceptor {
//···
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
//···
}
MethodInterceptor
拦截器就是由MethodProxy
的invokeSuper
方法调用代理方法的,因此,MethodProxy
类中的代码非常关键,下面分析它具体做了什么:
package net.sf.cglib.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.*;
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//创建目标类的FastClass对象(在缓存中,则取出;没在,则重新生成)
fci.f1 = helper(ci, ci.c1);
//创建代理类的FastClass对象
fci.f2 = helper(ci, ci.c2);
//获取learn方法的索引
fci.i1 = fci.f1.getIndex(this.sig1);
//获取CGLIB$learn$0方法的索引
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
}
}
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//初始化,创建了两个FastClass类对象
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//这里将直接调用代理类的CGLIB$learn$0方法,而不是通过反射调用
//fci.f2:代理类的FastClass对象,fci.i2为CGLIB$learn$0方法对应的索引,obj为当前的代理类对象,args为learn方法的参数列表
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
上面代码调用获取代理类对应的FastClass,并执行代理方法。还记得之前生成的三个.class
文件吗?Zhangsan$$EnhancerByCGLIB$$78b$$FastClassByCGLIB$$a8f9873c.class
就是代理类的FastClass,Zhangsan$$FastClassByCGLIB$$bcf7b1f4.class
就是目标类的FastClass。
CGLib动态代理执行代理方法的效率之所以比JDK高,是因为CGlib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个index(int类型);这个index被当作一个入参,FastClass可以直接定位要调用的方法并直接进行调用,省去了反射调用,因此调用效率比JDK动态代理通过反射调用高(并不绝对,还需参考JDK版本及使用场景来说)。下面来反编译一个FastClass。
public class Zhangsan$$FastClassByCGLIB$$bcf7b1f4 extends FastClass {
public Zhangsan$$FastClassByCGLIB$$bcf7b1f4(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var = var1.toString();
switch(var.hashCode()) {
case :
if (var.equals("learn()V")) {
//learn方法返回0
return 0;
}
break;
case :
if (var.equals("equals(Ljava/lang/Object;)Z")) {
//···
}
}
}
//根据index获取方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
Zhangsan var = (Zhangsan)var2;
int var = var1;
try {
switch(var) {
case 0:
//传入index为0则执行learn方法
var.learn();
return null;
case 1:
return new Boolean(var.equals(var3[0]));
//···
FastClass并不是跟代理类一起生成的,而是在第一次执行MethodProxy
的invoke
或invokeSuper
方法时生成的,并被放在了缓存中。
4.总结
通过上面的分析,相信会对两种动态代理的实现原理有一个深入的认识,总结性比较两者的区别如下:
- JDK动态代理实现了被代理对象的接口,CGLib动态代理继承了被代理对象。
- JDK动态代理和CGLib动态代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib动态代理使用ASM框架写Class字节码。CGLib动态代理实现更复杂,生成代理类比JDK动态代理效率低。
- JDK动态代理调用代理方法是通过反射机制调用的,CGLib动态代理是通过FastClass机制直接调用方法的,CGLib动态代理的执行效率更高。
当前文章:代理模式——JDK动态代理与CGLib原理及对比分析
地址分享:http://abwzjs.com/article/dscgisj.html