初步探究jdk动态代理的原理

2019 精帖
7 199

初步探究jdk动态代理

1.先看代码实现

package club.jiajiajia.test.staticproxy;

public interface BuyHouse {
    void buyHosue();//买房子
}
package club.jiajiajia.test.staticproxy;

public class BuyHouseImpl implements BuyHouse {

    @Override
    public void buyHosue() {
        System.out.println("买房子");
    }
}
package club.jiajiajia.test.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
    private Object object;
    public DynamicProxyHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("买房前准备");
        Object result = method.invoke(object, args);
        System.out.println("买房后装修");
        return result;
    }
}
public static void main(String[] args) {
    try {
	//生成$Proxy0的class文件
	System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//必须在main函数中才能生成
	//获取动态代理类字节码文件
	Class proxyClazz = Proxy.getProxyClass(BuyHouse.class.getClassLoader(),BuyHouse.class);
	//获得代理类的构造函数
	Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
	//通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
	BuyHouse proxyBuyHouse = (BuyHouse) constructor.newInstance(new DynamicProxyHandler(new BuyHouseImpl()));
	//通过代理对象调用目标方法
	proxyBuyHouse.buyHosue();
	System.out.println();
	BuyHouse buyHouse = new BuyHouseImpl();//未被代理对象
	System.out.println("proxyBuyHouse 对象是否是 Proxy 的实例:"+(proxyBuyHouse instanceof Proxy));
	System.out.println("buyHouse 对象是否是  BuyHouse 的实例:"+(buyHouse instanceof BuyHouse));
	System.out.println("proxyBuyHouse 对象是否是  BuyHouse 的实例:"+(proxyBuyHouse instanceof BuyHouse));
	System.out.println("proxyBuyHouse 对象是否是 BuyHouseImpl 的实例:"+(proxyBuyHouse instanceof BuyHouseImpl));
	System.out.println("代理对象的全类名:"+proxyBuyHouse.getClass().getName());
	System.out.println("代理类的父类:"+proxyBuyHouse.getClass().getSuperclass().getName());
	Class<?>[] c=proxyBuyHouse.getClass().getInterfaces();
	System.out.println();
	for (Class<?> inte : c) {//打印
	    System.out.println("代理类实现接口:"+inte);
	}
    } catch (Exception e) {
		e.printStackTrace();
    }
}

运行打印:

买房前准备
买房子
买房后装修

proxyBuyHouse 对象是否是 Proxy 的实例:true
buyHouse 对象是否是  BuyHouse 的实例:true
proxyBuyHouse 对象是否是  BuyHouse 的实例:true
proxyBuyHouse 对象是否是 BuyHouseImpl 的实例:false
代理对象的全类名:com.sun.proxy.$Proxy0
代理类的父类:java.lang.reflect.Proxy

代理类实现接口:interface club.jiajiajia.test.staticproxy.BuyHouse

2.运行原理

    动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。 

也就是说我们用的代理类的对象已经不是BuyHouseImpl的类模板所生成的对象了,而是jdk在运行期间由JVM根据反射等机制动态生成代理类的字节码文件 类似 $Proxy0.class,代理类的对象就是以这个.class文件为类模板创建的对象,but我们看不到这个.class文件,但是java提供了一个方法可以把这个class文件保存在本地。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//必须在main函数中才能生成

QQ截图20181224180339.png

然后用反编译工具反编译这个文件如下:

package com.sun.proxy;

import club.jiajiajia.test.staticproxy.BuyHouse;
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 BuyHouse {
	private static Method m1;
	private static Method m2;
	private static Method m0;
	private static Method m3;
	
	public $Proxy0(InvocationHandler paramInvocationHandler) {
		super(paramInvocationHandler);
	}
	
	public final boolean equals(Object paramObject) {
		try {
			return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}
	
	public final String toString() {
		try {
			return (String) this.h.invoke(this, m2, null);
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}
	
	public final int hashCode() {
		try {
			return ((Integer) this.h.invoke(this, m0, null)).intValue();
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}
	
	public final void buyHosue() {
		try {
			this.h.invoke(this, m3, null);
			return;
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}
	
	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });
			m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
			m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
			m3 = Class.forName("club.jiajiajia.test.staticproxy.BuyHouse").getMethod("buyHosue", new Class[0]);
		} catch (NoSuchMethodException localNoSuchMethodException) {
			throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
		} catch (ClassNotFoundException localClassNotFoundException) {
			throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
		}
	}
}

观察这个$Proxy0.java类会发现有如下几点:

1.这个类继承了Proxy类

2.实现了我们所定义的BuyHouse接口

3.它的构造方法中需要传入InvocationHandler实例

4.它重写了buyHosue方法以及其他方法

5.用反射获取了目标方法的引用(用于反射调用)

那么由这几个特点我绘制了下面这个图:


QQ截图20181218141917.png

代理对象的创建

从上图中可以发现 Proxy 类中有 InvocationHandler 属性,(注意:这个属性与 我们定义的DynamicProxyHandler类有着直接的关系,)

Class proxyClazz = Proxy.getProxyClass(BuyHouse.class.getClassLoader(),BuyHouse.class);
这一行代码是在帮我们创建代理类的类文件
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
这个是获得该类的构造方法,并由该方法创建代理对象
BuyHouse proxyBuyHouse = (BuyHouse) constructor.newInstance(new DynamicProxyHandler(new BuyHouseImpl()));
这一步才是真正的根据代理类的构造方法创建代理对象,会发现参数我们传入了DynamicProxyHandler的实例

在创建代理类的过程中 $Proxy0 的构造方法有调用了父类的构造方法 并传入DynamicProxyHandler的实例,并且可以看到,

父类的InvocationHandler属性引用了该对象.....其他的不重要.....至此,代理对象创建完成

代理方法的调用

当在调用BuyHouse的buyHosue()方法的时候,实际是调用了代理对象重写了BuyHouse接口的buyHosue()方法即:

	public final void buyHosue() {
		try {
			this.h.invoke(this, m3, null);
			return;
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

此方法紧接着调用了父类的InvocationHandler属性(即 h对象)的invoke方法(即我们定义的DynamicProxyHandler类的invoke方法,),在其方法内又通过反射调用的我们在创建DynamicProxyHandler对象时传入的目标对象buyHosue()方法

所以我们可以在其调用前后做一些其他的事情。

到此方法调用结束。


这就是我理解的jdk动态代理

对于代理类时如何生成的,这还得继续研究。


留言(0)
加载更多
猜你喜欢