老生常谈系列之Aop--CGLIB动态代理的底层实现原理

前言

上一篇老生常谈系列之Aop--JDK动态代理的底层实现原理简单讲解了JDK动态代理的实现,动态代理常用实现里面的双子星还有另一位--CGLIB,那么这一篇就会介绍CGLIB动态代理。这篇文章还是复用之前老生常谈系列之Aop--Spring Aop原理浅析文章的CGLIB部分的代码例子,CGLIB的使用是非常简单的,只需要自己实现一个MethodInterceptor,然后使用Enhancer#create()方法就可以创建一个动态代理处理,然后通过生成的代理类调用方法,即可实现Aop的效果。是不是很好奇为什么可以这么简单,接下来我们来分析CGLIB帮我们做了什么。

那么接下来文章主要分为两部分去解析

  • 动态代理的生成过程
  • 动态代理的调用过程

动态代理的生成

这一部分回答了动态代理是怎么生成的,CGLIB底层帮我们做了什么。可以看到创建代理对象离不开Enhancer类,那么这个类的作用是什么呢?摘取类上的注释如下:

  Generates dynamic subclasses to enable method interception. This
class started as a substitute for the standard Dynamic Proxy support
included with JDK 1.3, but one that allowed the proxies to extend a
concrete base class, in addition to implementing interfaces. The dynamically
generated subclasses override the non-final methods of the superclass and
have hooks which callback to user-defined interceptor
implementations.

翻译一下:通过生成动态子类让方法拦截生效。此类开始是作为 JDK 1.3 中包含的标准动态代理支持的替代品,但它允许代理扩展具体的基类,除了实现接口。动态生成的子类覆盖超类的非final方法,并具有回调到用户定义的拦截器实现的钩子。

简而言之,就是生成一个代理子类,调用方法的时候回调到自定义实现的拦截器里。

首先我们来看简单的示例代码

    @Test
public void cglibProxyTest(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CalculateServiceImpl.class);
enhancer.setCallback(new MyMethodInterceptor());
CalculateService calculateService = (CalculateService) enhancer.create();
calculateService.calculate();
}

可以看到我们只需要设置superClasscallback后调用create()方法就可以生成一个想要的对象。

在开始分析代码之前,先来看一下执行的时序图。

接下来就看一下create()发生了什么。

    /**
* Generate a new class if necessary and uses the specified
* callbacks (if any) to create a new object instance.
* Uses the no-arg constructor of the superclass.
* @return a new instance
*/
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}

可以看到这里利用KEY_FACTORY生成一个key,这个key封装了多个值,属于multi-valued keys的实现。我们来看一下KeyFactory的用法。

    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;
}

KeyFactory是类库中重要的唯一标识生成器,用于CGLIB实现缓存时的key,比较底层的基础类。

摘取类上的注释如下:

Generates classes to handle multi-valued keys, for use in things such as Maps and Sets.
To generate a <code>KeyFactory</code>, you need to supply an interface which
describes the structure of the key. The interface should have a
single method named <code>newInstance</code>, which returns an
<code>Object</code>. The arguments array can be
<i>anything</i>--Objects, primitive values, or single or
multi-dimension arrays of either. For example:
<p><pre>
private interface IntStringKey {
public Object newInstance(int i, String s);
}
</pre><p>
Once you have made a <code>KeyFactory</code>, you generate a new key by calling
the <code>newInstance</code> method defined by your interface.
<p><pre>
IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
Object key1 = factory.newInstance(4, "Hello");
Object key2 = factory.newInstance(4, "World");
</pre><p>

翻译一下:KeyFactory可以生成处理多值键的类,可以用于诸如 Maps 和 Sets 之类的东西。KeyFactory的使用也非常简单,只需要提供一个接口,定义一个newInstance()方法,调用(IntStringKey)KeyFactory.create(IntStringKey.class)就可以生成一个key类。

接下来通过super.create(key)调用父类的create(key)方法。

    protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
// 先尝试通过缓存获取ClassLoaderData
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);
}
// 省略异常
}

进入ClassLoaderData#get()方法

        public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
} else {
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}

跟进generatedClasses.get(gen)方法,这里第一次进来,前面都会为空,所以会进行节点创建createEntry()

    public V get(K key) {
final KK cacheKey = keyMapper.apply(key);
Object v = map.get(cacheKey);
if (v != null && !(v instanceof FutureTask)) {
return (V) v;
} return createEntry(key, cacheKey, v);
}

可以看到这里使用了FutureTask去异步执行创建,用于提升创建时候的性能。

    protected V createEntry(final K key, KK cacheKey, Object v) {
FutureTask<V> task;
boolean creator = false;
if (v != null) {
// Another thread is already loading an instance
task = (FutureTask<V>) v;
} else {
// 创建一个FutureTask
task = new FutureTask<V>(new Callable<V>() {
public V call() throws Exception {
return loader.apply(key);
}
});
// 校验这个任务是否已经存在
Object prevTask = map.putIfAbsent(cacheKey, task);
if (prevTask == null) {
// creator does the load
// 执行FutureTask
creator = true;
task.run();
} else if (prevTask instanceof FutureTask) {
task = (FutureTask<V>) prevTask;
} else {
return (V) prevTask;
}
} V result;
try {
// 走到这里说明是有正常执行的FutureTask,尝试获取FutureTask的结果
result = task.get();
}
// 省略部分异常
if (creator) {
map.put(cacheKey, result);
}
return result;
}

上面的代码跟着注释看一下,重点在这里loader.apply(key),这个loadernew LoadingCache()的时候传入的。

task = new FutureTask<V>(new Callable<V>() {
public V call() throws Exception {
return loader.apply(key);
}
});

loader的逻辑如下:

            Function<AbstractClassGenerator, Object> load =
new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
// 这里生成代理class
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);

跟进gen.generate(ClassLoaderData.this)方法

    protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
//省略部分逻辑和日志,重点在这里,这里会生成代理类的字节码
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
}
// 省略异常
}

默认的实现是DefaultGeneratorStrategy,可以看到这里先获取了一个DebuggingClassWriter,CGLIB封装ASM的处理类,用于生成class的byte流,通过GeneratorStrategy回调ClassGenerator.generateClass(DebuggingClassWriter),将自定义的class对象的byte处理回调给具体的CGLIB上层操作类,比如由具体的BeanCopier去控制字节码的生成。

    public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
transform(cg).generateClass(cw);
return transform(cw.toByteArray());
}

FastClassEmitter类的构造函数里,通过上面传入的DebuggingClassWriter封装了ASM的相关操作,用于动态生成代理类的字节码,这里不再深入ASM的原理,感兴趣可以看字节码操作框架ASM的实现原理

        public void generateClass(ClassVisitor v) throws Exception {
new FastClassEmitter(v, getClassName(), type);
}

最后再通过transform(cw.toByteArray())得到一个byte[]数组。好了,到这里已经可以得到一个代理的字节码了。接下来回到AbstractClassGenerator#create()方法里。

// obj为已经获取到的代理类,这里是一个Class对象
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);

只需要把对象实例化返回,至此,已经获取了一个代理类。

        protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type,
new Class[]{ Class.class },
new Object[]{ this.type });
}

动态代理的调用

代码样例

这里搞个例子HelloServiceHelloMethodInterceptor对它增强。

public class HelloService {
public void sayHello(){
System.out.println("hello");
}
} public class HelloMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before say hello...");
return methodProxy.invokeSuper(object,objects);
}
}

测试方法,把生成的代理类存下来。

public class HelloTest {

    @Test
public void cglibProxyTest(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C:\\my_study_project");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloService.class);
enhancer.setCallback(new HelloMethodInterceptor());
HelloService helloService = (HelloService) enhancer.create();
helloService.sayHello();
} }

那么上面的步骤生产了什么呢?我们来看一下生成的类有哪些。

HelloService$$EnhancerByCGLIB$$91933e33是生成的代理类,HelloService$$FastClassByCGLIB$$a685f36d是目标类的FastClassHelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd是代理类的FastClass

反编译后的代码

下面看一下每个类反编译后的代码

代理类

每个类的代码都很长,这里就不全部贴出来了,为了方便阅读,这里只留存sayHello()hashCode()方法做比对阐述。如果想查看全部的代码,自己把测试代码跑一下就能在相应的路径下找到这三个class文件。

package io.codegitz.service;

public class HelloService$$EnhancerByCGLIB$$91933e33 extends HelloService implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$sayHello$0$Method;
private static final MethodProxy CGLIB$sayHello$0$Proxy; private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy; // 初始化该类的所有方法
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("io.codegitz.service.HelloService$$EnhancerByCGLIB$$91933e33");
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$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "()V"}, (var1 = Class.forName("io.codegitz.service.HelloService")).getDeclaredMethods())[0];
CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$0");
} // 调用目标类方法
final void CGLIB$sayHello$0() {
super.sayHello();
}
// 代理逻辑的方法
public final void sayHello() {
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$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
} //hashCode方法也类似
final int CGLIB$hashCode$3() {
return super.hashCode();
} public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
} // 生成MethodProxy,通过MethodProxy调用会生成fastClass,这是实现高性能调用的关键
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case 1535311470:
if (var10000.equals("sayHello()V")) {
return CGLIB$sayHello$0$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$3$Proxy;
}
}
return null;
}
// 初始化方法
static {
CGLIB$STATICHOOK1();
}
}

代理类fastClass

fastClass为所有的方法都建立了索引,在调用的时候通过传入索引来寻找方法,进而避免反射的性能开销,这是一种典型的空间换时间实现。

package io.codegitz.service;

import io.codegitz.service.HelloService..EnhancerByCGLIB..91933e33;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass; public class HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd extends FastClass {
public HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd(Class var1) {
super(var1);
} // 通过方法签名获取方法索引
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1411842725:
if (var10000.equals("CGLIB$hashCode$3()I")) {
return 16;
}
break;
case 291273791:
if (var10000.equals("CGLIB$sayHello$0()V")) {
return 14;
}
break;
case 1535311470:
if (var10000.equals("sayHello()V")) {
return 7;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 2;
}
}
return -1;
} // 通过方法名获取索引
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 0:
return 7;
}
}
break;
case -1983192202:
if (var1.equals("CGLIB$sayHello$0")) {
switch(var2.length) {
case 0:
return 14;
}
}
break;
case -29025555:
if (var1.equals("CGLIB$hashCode$3")) {
switch(var2.length) {
case 0:
return 16;
}
}
break;
case 147696667:
if (var1.equals("hashCode")) {
switch(var2.length) {
case 0:
return 2;
}
}
break;
}
return -1;
} public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
} // 通过传入方法的索引var1获取方法执行
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
91933e33 var10000 = (91933e33)var2;
int var10001 = var1; try {
switch(var10001) {
case 2:
return new Integer(var10000.hashCode());
case 7:
var10000.sayHello();
return null;
case 14:
var10000.CGLIB$sayHello$0();
return null;
case 16:
return new Integer(var10000.CGLIB$hashCode$3());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
} public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
91933e33 var10000 = new 91933e33;
91933e33 var10001 = var10000;
int var10002 = var1; try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
} public int getMaxIndex() {
return 20;
}
}

目标类fastClass

CGLIB不仅对代理类生成fastClass,会对原有的目标类也会生成一个fastClass,原理是类似的,都是通过建立方法的索引,通过传入索引寻找到方法,执行方法,避免了反射获取方法的性能开销。

package io.codegitz.service;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass; public class HelloService$$FastClassByCGLIB$$a685f36d extends FastClass {
public HelloService$$FastClassByCGLIB$$a685f36d(Class var1) {
super(var1);
}
// 通过方法签名获取索引
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case 1535311470:
if (var10000.equals("sayHello()V")) {
return 0;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 3;
}
}
return -1;
} // 通过方法名获取方法索引
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 0:
return 0;
}
}
break;
case 147696667:
if (var1.equals("hashCode")) {
switch(var2.length) {
case 0:
return 3;
}
}
}
return -1;
} public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
} // 根据传入的var1获取对应的方法执行
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
HelloService var10000 = (HelloService)var2;
int var10001 = var1; try {
switch(var10001) {
case 0:
var10000.sayHello();
return null;
case 3:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
} public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
HelloService var10000 = new HelloService;
HelloService var10001 = var10000;
int var10002 = var1; try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
} public int getMaxIndex() {
return 3;
}
}

调用过程分析

这些码看起来是不是很乱?完全不知道从哪里开始执行?

在开始代码分析之前,先看一下执行流程图,步骤还是比较简单明了

接着下一步,这里就是进入动态代理类的逻辑,可以看HelloService$$EnhancerByCGLIB$$91933e33#sayHello()方法。

    public final void sayHello() {
// 获取拦截器,这里就是HelloMethodInterceptor
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
} // 不为空,则执行拦截器
if (var10000 != null) {
// 注意这里传入的参数,这里传入了一个method和一个MethodProxy,这里就会进入到自定义的HelloMethodInterceptor里面的逻辑
var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
}

这里是通过methodProxy.invokeSuper(object,objects),调用invokeSuper()方法,注意这里methodProxy还有个invoke()方法可以调用,那么这两者有什么区别呢?显而易见invokeSuper()就是调用父类的方法,而invoke()是调用代理经过拦截器的方法,如果调用invoke()那么每次都会走到拦截器,会造成死循环。

跟进methodProxy.invokeSuper()方法,根据注释可以看到,这里就是调用了原有的没有经过代理的方法。

    /**
* Invoke the original (super) method on the specified object.
* @param obj the enhanced object, must be the object passed as the first
* argument to the MethodInterceptor
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @see MethodInterceptor#intercept
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
*/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}

可以看到,这里的调用跟反射调用是有区别的。反射调用一般是直接把方法传入,然后直接invoke(),而这里会先进行init(),初始化一个FastClassInfo,再通过fci.f2.invoke(fci.i2, obj, args)去调用方法,这里就是前面说的实现高性能调用的关键,这里会为代理类方法和实现类的FastClass,然后在调用时通过传入方法的下标索引直接获取方法执行,从而实现了空间换时间操作。

来看init()方法,这个方法是用来初始化fastClassInfo类的,详细的初始化过程就不解析了,这里只是最终生成了什么就好。

    private void init()
{
/*
* 使用 volatile 不变量允许我们以原子方式初始化 FastClass 和方法索引对
* Using a volatile invariant allows us to initialize the FastClass and
* method index pairs atomically.
*
* 双重检查锁定在 Java 5 中使用 volatile 是安全的。在 1.5 之前,此代码可能允许多次实例化 fastClassInfo,这似乎是良性的。
* Double-checked locking is safe with volatile in Java 5. Before 1.5 this
* code could allow fastClassInfo to be instantiated more than once, which
* appears to be benign.
*/
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo; FastClassInfo fci = new FastClassInfo();
// 生成目标类fastClass
fci.f1 = helper(ci, ci.c1);
// 生成代理类fastClass
fci.f2 = helper(ci, ci.c2);
// 生成目标类index
fci.i1 = fci.f1.getIndex(sig1);
// 生成代理类index
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}

以下是初始化时各个属性的赋值

初始化完成后,就可以回到fci.f2.invoke(fci.i2, obj, args)调用上了,可以看到fci.f2的类型是HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd,也就是上面贴出来的代理类的fastClass,来看一下这个类的invoke()方法

查看反编译的invoke()方法代码

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
91933e33 var10000 = (91933e33)var2;
int var10001 = var1; try {
switch(var10001) {
case 2:
return new Integer(var10000.hashCode());
case 7:
var10000.sayHello();
return null;
case 16:
var10000.CGLIB$sayHello$0();
return null;
case 17:
return new Integer(var10000.CGLIB$hashCode$3());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
}

这里switch会匹配到16,然后执行var10000.CGLIB$sayHello$0(),这个var10000的类型是

找到HelloService$$EnhancerByCGLIB$$91933e33#CGLIB$sayHello$0()方法,可以看到,这里直接调用了HelloService#sayHello()方法。同样,methodProxy#invoke()方法逻辑也是类似,注意区分调用的时候不要死循环就是了。

    final void CGLIB$sayHello$0() {
super.sayHello();
}

到这里,可以看到CGLIB生成的代理方法调用时,先经过调用拦截器,然后再调用到目标方法,其中methodProxy调用目标方法时,会生成fastClassfastClass中存有代理类和目标类的所有方法以及匹配的下标,通过传入的下标就可以寻找到对应的方法,这里的方法调用只需要第一次进来初始化fastClass,后续可以直接调用,从而提高执行的性能,这也是CGLIB执行效率比JDK动态代理高的关键。这里空间换时间的思想值得我们借鉴,适当地消耗内存来提升执行效率是完全值得的。

总结

回顾一下这篇文章,前半部分通过一个例子,大概讲解了CGLIB生成一个代理类的步骤,但是具体集成ASM部分的字节码操作被略过,水平有效,不敢造次。挖了个坑,以后有能力再填。后半部分结合反编译的class文件,解释了调用的过程,这部分很简单,自己调试一下应该很快就能理清。

如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。

老生常谈系列之Aop--CGLIB动态代理的底层实现原理的更多相关文章

  1. 老生常谈系列之Aop--JDK动态代理的底层实现原理

    老生常谈系列之Aop--JDK动态代理的底层实现原理 前言 在Aop系列里面有两篇文章,分别是老生常谈系列之Aop--Spring Aop原理浅析和老生常谈系列之Aop--Spring Aop源码解析 ...

  2. 代理模式 & Java原生动态代理技术 & CGLib动态代理技术

    第一部分.代理模式  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...

  3. 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】

    一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...

  4. Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC

    一.为什么需要代理模式 假设需实现一个计算的类Math.完成加.减.乘.除功能,如下所示: package com.zhangguo.Spring041.aop01; public class Mat ...

  5. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  6. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  7. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  8. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

  9. Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

    一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...

随机推荐

  1. 【SpringBoot学习一】开发入门--快速创建springboot程序

    前言 本片博客记录快速创建springboot工程的两种方式.一种是使用maven创建,一种是使用spring initializr创建.开发环境JDK1.8.IDEA.maven. SpringBo ...

  2. python学习笔记(五)——静态方法、类方法、运算符重载

    我们都知道类名是不能够直接调用类方法的.在C++中,把成员方法声明为 static 静态方法后可以通过类名调用.同样的在python中也可以通过定义静态方法的方式让类名直接调用. 静态方法 使用 @s ...

  3. char向wchar的转换-MultiByteToWideChar

    问题产生 使用CreateFile函数,如下: CreateFile(lpcTheFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NO ...

  4. poj_2386_dfs

    描述 由于最近的一场雨,农夫john的田地里很多地方流入了水,由一个N*M的矩形表示.每个方格要么有水(W)要么是干的(.).农夫想要知道他的田地里形成了多少池塘. 一个池塘由有水的方块相连,每个方块 ...

  5. Python窗口学习之使窗口变得更高清

    初学tkinter发现窗口并不像成熟软件那么清楚 在实例化window后加这一行代码 #使窗口更加高清 # 告诉操作系统使用程序自身的dpi适配 ctypes.windll.shcore.SetPro ...

  6. java中如何能知道应该捕获什么样的异常?举例

    我怎么知道应该捕获什么样的异常? 马克-to-win:如上例1.1:开始没加try时,程序崩溃,系统打印的是如下的错误,Exception in thread "main" jav ...

  7. 类其中的变量为final时的用法

    类其中的变量为final时的用法:   类当中final变量没有初始缺省值,必须在构造函数中赋值或直接当时赋值.否则报错. public class Test {     final int i;   ...

  8. JWT介绍及使用

    目录 JWT-JSON WEB TOKEN JWT组成 header payload signature JWT校验 JWT获取信息 JJWT(java jwt api)使用 导入依赖 测试代码 JW ...

  9. 微信小程序时间戳转为日期格式

    通常后台传递过来的都是时间戳,但是前台展示不能展示时间戳.就需要转化了. 功能说明: 微信小程序里,时间戳转化为日期格式,支持自定义. 拷贝至项目utils/utils.js中,并注意在js中声明下: ...

  10. vue引入echarts

    效果图: 1.安装Echarts :     npm install echarts -S 或者使用国内的淘宝镜像: 安装: npm install -g cnpm --registry=https: ...