共计 6031 个字符,预计需要花费 16 分钟才能阅读完成。
<img src=”https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/20210925233820.png” width = “500” height = “400” alt=” 图片名称 ” align=center />
cglib 动静代理
cglib 介绍
CGLIB 是一个开源我的项目,一个弱小高性能高质量的代码生成库,能够在运行期拓展 Java 类,实现 Java 接口等等。底层是应用一个小而快的字节码解决框架 ASM,从而转换字节码和生成新的类。
实践上咱们也能够间接用 ASM 来间接生成代码,然而要求咱们对 JVM 外部,class 文件格式,以及字节码的指令集都很相熟。
这玩意不在 JDK 的包外面,须要本人下载导入或者 Maven 坐标导入。
我抉择 Maven
导入, 加到 pom.xml
文件:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
Student.java
:
public class Student {public void learn() {System.out.println("我是学生,我想学习");
}
}
MyProxy.java
(代理类)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class StudentProxy implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("代理前 -------");
proxy.invokeSuper(obj, args);
System.out.println("代理后 -------");
return null;
}
}
测试类(Test.java
)
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Test {public static void main(String[] args) {
// 代理类 class 文件存入本地磁盘不便咱们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/aphysia/Desktop");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
enhancer.setCallback(new StudentProxy());
Student student = (Student) enhancer.create();
student.learn();}
}
运行之后的后果是:
CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
代理前 -------
我是学生,我想学习
代理后 -------
在咱们抉择的文件夹外面,生成了代理类的代码:
源码剖析
咱们先要代理的类,须要实现 MethodInterceptor
(办法拦截器) 接口, 这个接口只有一个办法 intercept
, 参数别离是:
- obj:须要加强的对象
- method:须要拦挡的办法
- args:要被拦挡的办法参数
- proxy:示意要触发父类的办法对象
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
再看回咱们要创立代理类的办法 enhancer.create()
, 这个办法的意思:如果须要,生成一个新类,并应用指定的回调 (如果有的话) 来创立一个新的对象实例。应用超类的无参数构造函数。
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();}
次要的办法逻辑咱们得看 createHelper()
,除了校验,就是调用 KEY_FACTORY.newInstance()
办法生成 EnhancerKey
对象,KEY_FACTORY
是动态 EnhancerKey
接口,newInstance()
是接口外面的一个办法,重点在 super.create(key)
外面,调用的是父类的办法:
private Object createHelper() {
// 校验
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
AbstractClassGenerator
是 Enhancer
的父类,create(key)
办法的次要逻辑是获取类加载器,缓存获取类加载数据,而后再反射结构对象,外面有两个发明实例对象的办法:
fistInstance()
: 不应该在惯例流中调用此办法。从技术上讲,{@link #wrapCachedClass(Class)}
应用{@link EnhancerFactoryData}
作为缓存值,后者反对比一般的旧反射查找和调用更快的实例化。出于向后兼容性的起因,这个办法放弃不变: 只是以防它已经被应用过。(我的了解是目前的逻辑不会走到这个分支,因为它比较忙,然而为了兼容,这个 case 还保留着),外部逻辑其实用的是ReflectUtils.newInstance(type)
。nextInstance()
: 真正的创立代理对象的类
protected Object create(Object key) {
try {ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {return firstInstance((Class) obj);
}
// 真正创建对象的办法
return nextInstance(obj);
} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);
}
}
这个办法定义在 AbstractClassGenerator
,然而实际上是调用子类 Enhancer
的实现,次要是通过获取参数类型,参数,以及回调对象,用这些参数反射生成代理对象。
protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;
if (classOnly) {return data.generatedClass;}
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
// 结构
return data.newInstance(argumentTypes, arguments, callbacks);
}
外部实现逻辑, 调用的都是ReflectUtils.newInstance()
, 参数品种不一样:
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);
try {
if (primaryConstructorArgTypes == argumentTypes ||
Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {return ReflectUtils.newInstance(primaryConstructor, arguments);
}
return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
} finally {setThreadCallbacks(null);
}
}
跟进去到底,就是获取结构器办法,反射形式结构代理对象,最终调用到的是 JDK 提供的办法:
public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {return newInstance(getConstructor(type, parameterTypes), args);
}
public static Object newInstance(final Constructor cstruct, final Object[] args) {boolean flag = cstruct.isAccessible();
try {if (!flag) {cstruct.setAccessible(true);
}
Object result = cstruct.newInstance(args);
return result;
} catch (InstantiationException e) {throw new CodeGenerationException(e);
} catch (IllegalAccessException e) {throw new CodeGenerationException(e);
} catch (InvocationTargetException e) {throw new CodeGenerationException(e.getTargetException());
} finally {if (!flag) {cstruct.setAccessible(flag);
}
}
}
关上它主动生成的代理类文件看看,就会发现其实也是生成那些办法,加上了一些加强办法:
生成的代理类继承了原来的类:
public class Student$$EnhancerByCGLIB$$929cb5fe extends Student implements Factory {...}
看看生成的加强办法, 其实是调用到 intercept()
办法,这个办法由咱们后面本人实现,因而就实现了代理对象加强的性能:
public final void learn() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {var10000.intercept(this, CGLIB$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
} else {super.learn();
}
}
cglib 和 jdk 动静代理有什么区别
- jdk 动静代理是利用拦截器加上反射生成了一个代理接口的匿名类,执行办法的时候交给 InvokeHandler 解决。CGLIB 动静代理是应用了 ASM 框架,批改原来的字节码,而后生成新的子类来解决。
- JDK 代理须要实现接口,然而 CGLIB 不强制。
- 在 JDK1.6 之前,cglib 因为用了字节码生成技术,比反射效率高,然而之后 jdk 也进行了一些优化,效率上曾经晋升了。
【作者简介】:
秦怀,公众号【秦怀杂货店 】作者,技术之路不在一时,山高水长,纵使迟缓,驰而不息。集体写作方向:Java 源码解析
,JDBC
,Mybatis
,Spring
,redis
, 分布式
, 剑指 Offer
,LeetCode
等,认真写好每一篇文章,不喜爱题目党,不喜爱花里胡哨,大多写系列文章,不能保障我写的都完全正确,然而我保障所写的均通过实际或者查找材料。脱漏或者谬误之处,还望斧正。
剑指 Offer 全副题解 PDF
2020 年我写了什么?
开源编程笔记