java 基于JDK动态代理简单示例源码以及详细解析

一、代理相关概念:被代理类(真实类),代理类
二、代理的作用:
1、无侵入式扩展代码,既增强功能,且做到解耦,代理逻辑与业务逻辑是互相独立,在不改变真实类代码情况下做一些额外处理(Spring AOP 事务控制、日志管理)。
2、保护真实类(被代理类):代理类拦截对真实类的直接访问,对真实类做访问检查和控制,调用者只需要和代理类进行交互即可,不必关心真实类的实现细节。
3、懒加载,程序启动时加载的是轻量级代理类,真实类只有在通过代理类调用的时候才会创建(Hibernate 的延迟加载(lazy load)本质上就是代理模式的应用,我们就经常通过代理模式来降低系统的内存开销、提升应用的运行性能。Hibernate 充分利用了代理模式的这种优势,并结合了 Javassist 或 CGLIB 来动态地生成代理对象,这更加增加了代理模式的灵活性,Hibernate 给这种用法一个新名称:延迟加载)。

三、动态代理静态代理区别
动态代理是一对多,静态代理一对一
动态代理是在运行时动态成代理类放在内存中,静态代理运行前就已经生成代理类的.class文件

四、jdk动态代理简单实现以及底层原理
4-1 jdk动态代理简单实现:
接口:Cooker.java

public interface Cooker {
	public void cook();
}

真实类(被代理类):ElectricCooker.java

public class ElectricCooker implements Cooker {
	@Override
	public void cook() {
		System.out.println("电饭锅煮饭");
	}
}

InvocationHandler接口实现类:DynamicProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Class cl = getProxyClass0(loader, intfs);生成代理类
 */
public class DynamicProxy implements InvocationHandler {
	private Cooker target;
	public Cooker getInstance(Cooker cooker ) {
		this.target = cooker;
		Class clazz = target.getClass();
		return (Cooker) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		Object obj = method.invoke(this.target, args);
		after();
		return obj;
	}
	private void before() {
		System.out.println("before。。。插上电源");
	}
	private void after() {
		System.out.println("after。。。拔掉电源");
	}
}

测试类:DynamicProxyTest.java

public class DynamicProxyTest {
	String basePath = "C:/Users/zhou/Desktop/Jad/";

	@Test
	public void testCook() {
		Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
		cooker.cook();
	}

	@Test
	public void testGetClassFile() {
		Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
		byte[] classFile = sun.misc.ProxyGenerator.generateProxyClass(cooker.getClass().getName(),
				cooker.getClass().getInterfaces());
		String path = basePath + cooker.getClass().getSimpleName() + ".class";
		try (FileOutputStream fos = new FileOutputStream(path)) {
			fos.write(classFile);
			fos.flush();
			System.out.println("代理类class文件写入成功,classNme:" + cooker.getClass().getName());
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("写文件错误");
		}
	}
}

测试结果 testCook():

before。。。插上电源
电饭锅煮饭
after。。。拔掉电源

测试结果 testGetClassFile(),通过jad反编译得到动态生成的代理类$Proxy4代码:

package com.uniz.study.proxy.source;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

import com.uniz.study.proxy.Cooker;

public final class $Proxy4 extends Proxy implements Cooker {
	private static Method m1;
	private static Method m2;
	private static Method m3;
	private static Method m0;

	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]);
			m3 = Class.forName("com.uniz.study.proxy.Cooker").getMethod("cook", new Class[0]);
			m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public $Proxy4(InvocationHandler paramInvocationHandler) {
		super(paramInvocationHandler);
	}

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

	public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

	public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
	public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
}

Proxy.java
java 基于JDK动态代理简单示例源码以及详细解析_第1张图片
java 基于JDK动态代理简单示例源码以及详细解析_第2张图片

4-2 通过上面testGetClassFile()测试方法分析动态代理底层逻辑实现:

Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
此处cooker的实际类型是动态生成的代理类$Proxy4,

1、$Proxy4的实现了Cooker 接口:

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

2、方法中this.h是$Proxy4类的属性protected InvocationHandler h,在生成动态代理类时,DynamicProxy 把自己作为参数传给了代理类,因此此处this.h = DynamicProxy对象,而DynamicProxy类刚好实现了InvocationHandler接口的invoke方法,并且invoke方法中执行method.invoke(target, args)时,参数target是真实类的对象引用,因而能执行真实对象的方法

五、debug跟踪动态代理类的详细创建过程:
java 基于JDK动态代理简单示例源码以及详细解析_第3张图片
java 基于JDK动态代理简单示例源码以及详细解析_第4张图片
java 基于JDK动态代理简单示例源码以及详细解析_第5张图片
java 基于JDK动态代理简单示例源码以及详细解析_第6张图片
java 基于JDK动态代理简单示例源码以及详细解析_第7张图片
java 基于JDK动态代理简单示例源码以及详细解析_第8张图片
java 基于JDK动态代理简单示例源码以及详细解析_第9张图片
java 基于JDK动态代理简单示例源码以及详细解析_第10张图片
java 基于JDK动态代理简单示例源码以及详细解析_第11张图片
java 基于JDK动态代理简单示例源码以及详细解析_第12张图片
至此,由上面debug过程发现,代理类$Proxy4的关键创建过程如下:

Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)
Class cl = getProxyClass0(loader, intfs);
valueFactory.apply(key, parameter)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
最后发现创建代理类的方法 ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
测试方法testGetClassFile()正是调用此方法得到代理对象$Proxy4

小结:
1、真实类(被代理类)把自己委托给代理类,在代理类中可以对其功能增强,同时做到代理逻辑与业务逻辑的解耦,当真实类比较臃肿时,初始化时用轻量级的代理类替代直接初始化真实类,从而实现真实类的延迟加载。
2、jdk的动态代理简单实现逻辑:

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); 
 
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 
     new Class[] { Interface.class }, 
     handler );

3、常见的代理场景:
远程代理:为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销
缓存代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间
保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
智能引用代理:当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理

参考:
Java 动态代理机制分析及扩展

java动态代理实现与原理详细分析

JAVA动态代理

代理模式 | 菜鸟教程

JDK动态代理|ProxyGenerator生成代理类的字节码文件解析

你可能感兴趣的:(java学习)