动态代理-JDK/CGLIB Proxy.newProxyInstance() InvocationHandler 动态代理的原理

转载自:https://www.jianshu.com/p/95970b089360

一个程序员Developer,他会开发code,他调试debug。

 动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理
 

程序员有很多分类,其中有Java程序员JavaDeveloper,他会开发Java代码,会调试Java代码。
 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

但是呢,有个叫Zack的程序员它在开发之前,会祈祷一下,这样他开发的代码就不会有bug。

Zack的这种“特异功能”是后天练出来的,并没有哪种程序员有这种特性。虽然我们也可以定义一个拥有这样特性的程序员,但是拥有各种乱七八糟特性的程序千千万。我们什么时候才能定义完,而能保证不漏呢?

其实我们没有必要去定义他,因为他是后天养成的,我们应该在这个程序员的成长期去实现这个特性,而不是在他出生之前定义。

我们来看下代码是怎么实现的


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

如果Zack只是一个普通的Java程序员,那么他的开发结果是
Zack is coding java
Zack is debugging java

但是真正的Zack(代理后)
Zack is praying for the code!
Zack is coding java
Zack's have no bug!No need to debug!

回看下上面是如何使用动态代理的使用。生成一个实例对象,然后用Proxy的newInstance方法对这个实例对象代理生成一个代理对象。


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

这里有一个非常关键的人,也是比较少人去理解它的。为什么要传zack的类加载和zack的接口呢?
有没有留意到zackProxy的类型是Developer接口,而不是一个实现类。因为zack在被代理后生成的对象,并不属于Developer接口的任何一个实现类。但是它是基于Developer接口和zack的类加载代理出来的。

看下newProxyInstance()的接口定义


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

这三个参数具体的含义来看看注解是怎么描述的


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理 
  • loder,选用的类加载器。因为代理的是zack,所以一般都会用加载zack的类加载器。
  • interfaces,被代理的类所实现的接口,这个接口可以是多个。
  • h,绑定代理类的一个方法。

loder和interfaces基本就是决定了这个类到底是个怎么样的类。而h是InvocationHandler,决定了这个代理类到底是多了什么功能。所以动态代理的内容重点就是这个InvocationHandler。

InvocationHandler

 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理
 

根据注解描述可知,InvocationHandler作用就是,当代理对象的原本方法被调用的时候,会绑定执行一个方法,这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。

InvocationHandler接收三个参数

  • proxy,代理后的实例对象。
  • method,对象被调用方法。
  • args,调用时的参数。

在上面的例子里,


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

如果最后的return语句改成

return method.invoke(proxy, agrs);

invoke的对象不是zack,而是proxy,根据上面的说明猜猜会发生什么?
是的,会不停地循环调用。因为proxy是代理类的对象,当该对象方法被调用的时候,会触发InvocationHandler,而InvocationHandler里面又调用一次proxy里面的对象,所以会不停地循环调用。并且,proxy对应的方法是没有实现的。所以是会循环的不停报错

动态代理的原理

通过上面的讲解,相信大家对动态代理的使用理解得比较深刻了。那动态代理到底是怎么实现的呢,我们来看看源码其中关键的地方。
在newProxyInstance()发放中有这样几段。


 
动态代理-JDK/CGLIB
Proxy.newProxyInstance()
InvocationHandler
动态代理的原理

其实大概就是把接口复制出来,通过这些接口和类加载器,拿到这个代理类cl。然后通过反射的技术复制拿到代理类的构造函数(这部分代码在Class类中的getConstructor0方法),最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。

CGLIB 动态代理:
JDK 动态代理,JDK 动态代理的类必须实现一个接口,而且生成的代理类是其接口的实现类,也就是被代理的类的兄弟类,由JDK内部实现,
cglib代理的类,无需强制实现接口,其生成的代理类 是 被代理类的子类,并且重写的被代理类的方法,而且需要额外的引入Jar

简单实现:
计算一个 sql 调用总时间

代码逻辑:

创建一个需要被代理的类 SqlService:

public class SqlService {
public void executeSql1() throws InterruptedException {
System.out.println("Sql 开始执行.....");
Thread.sleep(1000);
System.out.println("Sql 执行结束.....");
}
}

创建代理类: SqlFacadeCglib

public class SqlFacadeCglib implements MethodInterceptor {

/**
* 被代理的对象
*/
private Object target;

public SqlFacadeCglib(Object target) {
this.target = target;
}

/**
* 实现回调方法
* @param obj 代理的对象
* @param method 被代理对象的方法
* @param args 参数集合
* @param proxy 生成的代理类的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 开始执行时间
Long startTime = System.currentTimeMillis();
// //调用业务类(父类中)的方法
Object result = proxy.invokeSuper(obj, args);
// 执行结束
Long endTime = System.currentTimeMillis();
System.out.println(target.getClass().getName()+"执行executeSql耗时"+(endTime-startTime)+"ms");
return result;
}
}

创建一个Test:

public class Test {

public static void main(String[] args) throws InterruptedException, IOException {
// 将class 文件保存
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:/mycode/Git/spring-mvc/learn/design-pattern-learn/src/main/java/com/lot/cglibproxy");
SqlService sqlService = new SqlService();
SqlFacadeCglib sqlFacadeCglib = new SqlFacadeCglib(sqlService);

//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(sqlService.getClass());

//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(sqlFacadeCglib);
// 创建动态代理类对象并返回
SqlService sqlServiceProxy = (SqlService)enhancer.create();
// 调用
sqlServiceProxy.executeSql1();

}
}


执行结果:
控制台打印:

CGLIB debugging enabled, writing to 'F:/mycode/Git/spring-mvc/learn/design-pattern-learn/src/main/java/com/lot/cglibproxy'
Sql 开始执行.....
Sql 执行结束.....
com.lot.cglibproxy.SqlService执行executeSql耗时1018ms

Debug 时 看到 sqlServiceProxy 定义:

根据Debug的显示 结合 System.setProperty() 写入文件的位置,我们找到 生成的代理类:SqlService¥¥EnhancerByCGLIB¥¥ecb184ce : (简略后代码) :
public class SqlService$$EnhancerByCGLIB$$ecb184ce extends SqlService implements Factory {

static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
// 参数集合
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.lot.cglibproxy.SqlService$$EnhancerByCGLIB$$ecb184ce");
Class var1;
Method[] var10000 = 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());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
// 被代理类的 executeSql1() 方法
CGLIB$executeSql1$0$Method = ReflectUtils.findMethods(new String[]{"executeSql1", "()V"}, (var1 = Class.forName("com.lot.cglibproxy.SqlService")).getDeclaredMethods())[0];
// 生成代理类的 executeSql1() 方法
CGLIB$executeSql1$0$Proxy = MethodProxy.create(var1, var0, "()V", "executeSql1", "CGLIB$executeSql1$0");
}
public final void executeSql1() throws InterruptedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

if (var10000 != null) {
// 执行 SqlFacadeCglib.intercept() 方法
var10000.intercept(this, CGLIB$executeSql1$0$Method, CGLIB$emptyArgs, CGLIB$executeSql1$0$Proxy);
} else {
super.executeSql1();
}
}

总结:

在 Test.main() 方法中,使用 Enhancer 加强器和 我们自己定义的类SqlService、SqlFacadeCglib组合,再通过enhancer.create()创建代理类。从代理类的源码中,我们可以看到代理类其实是我们业务类的子类
当我们在执行 sqlServiceProxy.executeSql1()时 实际上是访问 CGLIB生成的代理类的 executeSql1() 方法,然后在调用 SqlFacadeCglib.intercept() 做一些公共逻辑处理,其中 proxy.invokeSuper(obj, args) 这段代码是执行 我们业务逻辑类中的方法:SqlService.executeSql1()
其实动态代理的目的就是帮我们整合业务逻辑类,和共有的方法,并生成代理类,大大简化了我们开发工作量,其中 Spring 中对动态代理有很深的使用。


原文链接:https://blog.****.net/zhangyong01245/article/details/90644933