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

硅谷探秘者 1138 0 7

初步探究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动态代理

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


猜你喜欢
java基础 1596 cglib之前我们说了一下jdkhttp://www.jiajiajia.club/weblog/blog/artical/60本章说一下cglib,做个
ASM,java基础 56   关于cglib概念和以及常用api,请参考:cglib:http://www.jiajiajia.club/blog/artical/yjw520
weblog 2293 :http://www.jiajiajia.club/search?str=asm jdkhttp://www.jiajiajia.club/weblog/blog/artical/60
java基础 1339 java静示例packageclub.jiajiajia.test.staticproxy;publicinterfaceBuyHouse{voidbuyHosue();//买房子
ASM,java基础 52 概念和,这里就不多说了,详细请参考以往博客:cglib:http://www.jiajiajia.club/blog/artical/yjw520/134cglib
其他 2270 1.编译技术从JDK1.6开始引入了用Java码重写编译器接口,使得我们可以在运行时编译Java源码,然后用类加载器进行加载,让Java语言更具灵活性,能够完成许多高级操作。2.本次要实现
weblog 2366 int不需要。 int默认值是0,而Integer默认值是null。 然而仅仅知道这些,对其两者了解还是远远不够。那么下面就继续索。 语法糖味道-自装箱和拆箱 什么是自装箱和
linux系统 3322 方案一:一般配置如果以html,htm,gif,jpg,jpeg,bmp,png,ico,txt,js,css结尾资源,均由nginx处,否则转发到相应服务即可server{listen80
归档
2018年11月  12 2018年12月  33 2019年01月  28 2019年02月  28 2019年03月  32 2019年04月  27 2019年05月  33 2019年06月  6 2019年07月  12 2019年08月  12 2019年09月  21 2019年10月  8 2019年11月  15 2019年12月  25 2020年01月  9 2020年02月  5 2020年03月  16 2020年04月  4 2020年06月  1 2020年07月  7 2020年08月  13 2020年09月  9 2020年10月  5 2020年12月  3 2021年01月  1 2021年02月  5 2021年03月  7 2021年04月  4 2021年05月  4 2021年06月  1 2021年07月  7 2021年08月  2 2021年09月  8 2021年10月  9 2021年11月  16
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议
目录
祝愿神州十三飞行乘组平安归来