一、代理相关概念:被代理类(真实类),代理类
二、代理的作用:
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);
}
}
}
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跟踪动态代理类的详细创建过程:
至此,由上面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生成代理类的字节码文件解析