cglib 动态代理详解

我们都知道jdk的动态代理内部调用切面无效的问题,而cglib则不会出现这种情况,这是为什么?cglib就一定不会出现内部调用切面无效的问题吗?cglib针对每一个类只创建了一个代理类吗?为什么cglib的效率要比jdk的动态代理低呢?

首先我们看一下通常我们是如何使用cglib增强一个类的

public class Main {static class Test{public void test(){System.out.println("test");try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}innerTest();}public void innerTest(){System.out.println("inner test");}}public static void main(String[] args) throws Exception {Enhancer enhancer = new Enhancer();// 设置被代理的类enhancer.setSuperclass(Test.class);// 创建MethodInterceptor对象作为回调方法enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {long cost = System.currentTimeMillis();// 调用实际方法,也就是被代理类原本的方法Object result = methodProxy.invokeSuper(proxy, args);System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));return result;}});// 生成代理类,并创建代理类对象Test test = (Test)enhancer.create();// 调用代理类对象的方法test.test();}
}

输出结果

test
inner test
method innerTest cost time :0
method test cost time :1012

结果完全符合我们的预期,内部调用的函数也被增强了,那我们接下来就一步步的看一下,cglib究竟是如何实现动态代理的。

1. cglib生成的代理类长什么样?

我们先看一下cglib是如何创建一个代理类的,从我们代码中调用的net.sf.cglib.proxy.Enhancer#create()方法开始一层层的往下看

// net.sf.cglib.proxy.Enhancer#create
// 我们创建代理类的入口
public Object create() {classOnly = false;argumentTypes = null;return createHelper();
}// net.sf.cglib.proxy.Enhancer#createHelper
private Object createHelper() {// 预处理,验证是否有明确的回调信息preValidate();// 存储类信息、回调方法信息等,cglib使用这些信息对象作为缓存key;之后可以根据key直接去缓存中查找创建过的代理类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;
}// 父类的create方法,创建代理类并获取代理类对象
// net.sf.cglib.core.AbstractClassGenerator#create
protected Object create(Object key) {try {ClassLoader loader = getClassLoader();/*** 省略从缓存中获取对象的代码*/this.key = key;// 从缓存中获取代理类的Class对象Object obj = data.get(this, getUseCache());if (obj instanceof Class) {// 使用代理类的Class对象进行实例化return firstInstance((Class) obj);}return nextInstance(obj);} catch (RuntimeException e) {// 省略异常处理代码......}
}
// 如果使用缓存,则从缓存中取,若不存在则先生成代理类;如果不使用缓存则直接创建
// net.sf.cglib.core.AbstractClassGenerator.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);}
}// 生成代理类并创建代理对象的核心代码;cglib所有的代理类都从这里创建
// net.sf.cglib.core.AbstractClassGenerator#generate
protected Class generate(ClassLoaderData data) {Class gen;Object save = CURRENT.get();CURRENT.set(this);try {// 获取类加载器ClassLoader classLoader = data.getClassLoader();....synchronized (classLoader) {// 生成代理类的类名String name = generateClassName(data.getUniqueNamePredicate());              data.reserveName(name);this.setClassName(name);}// 生成代理类的字节码;// 若想获取生成的代理类的字节码信息可以在此打断点,将字节数据输出到本地文件中,之后所有获取的代理类字节码信息均采用此方法byte[] b = strategy.generate(this);String className = ClassNameReader.getClassName(new ClassReader(b));ProtectionDomain protectionDomain = getProtectionDomain();synchronized (classLoader) { // 将字节码转换成Class对象if (protectionDomain == null) {gen = ReflectUtils.defineClass(className, b, classLoader);} else {gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);}}return gen;} catch (RuntimeException e) {....// 省略异常处理}
}

从代码中我们可以看到,最终所有代理类都是通过调用net.sf.cglib.core.AbstractClassGenerator#generate创建的,这也是。

我们开启debug,在生成字节码的代码之后打断点,将字节码信息输出到本地文件中,再经过反编译就能拿到生成的代理类的代码,我们看一下Test的代理代码,以下省略了所有的hashCode、equals等从Object继承而来的方法增强的代码以及其他不是很重要的初始化方法的代码。

// 代理类是Test的子类
public class Main$Test$$EnhancerByCGLIB$$a07c0bc4 extends Test implements Factory {private MethodInterceptor CGLIB$CALLBACK_0;private static final Method CGLIB$test$0$Method;private static final MethodProxy CGLIB$test$0$Proxy;private static final Method CGLIB$innerTest$1$Method;private static final MethodProxy CGLIB$innerTest$1$Proxy;static void CGLIB$STATICHOOK1() {// 代理类的Class对象Class var0 = Class.forName("com.netease.com.Main$Test$$EnhancerByCGLIB$$a07c0bc4");Class var1;// 获取test和innerTest的Method对象,同时这里给var1变量赋值,指向了未被代理的Class对象var10000 = ReflectUtils.findMethods(new String[]{"test", "()V", "innerTest", "()V"}, (var1 = Class.forName("com.netease.com.Main$Test")).getDeclaredMethods());CGLIB$test$0$Method = var10000[0];CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");CGLIB$innerTest$1$Method = var10000[1];CGLIB$innerTest$1$Proxy = MethodProxy.create(var1, var0, "()V", "innerTest", "CGLIB$innerTest$1");}// 父类的test方法final void CGLIB$test$0() {super.test();}// 代理后的test方法,实际有效内容为我们编码时的MethodInterceptor对象public final void test() {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$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);} else {super.test();}}// 父类的innerTest方法final void CGLIB$innerTest$1() {super.innerTest();}// 被代理的innerTest方法,实际有效内容为我们编码时创建的MethodInterceptor对象public final void innerTest() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {// 调用编码时的intercept方法var10000.intercept(this, CGLIB$innerTest$1$Method, CGLIB$emptyArgs, CGLIB$innerTest$1$Proxy);} else {super.innerTest();}}// 获取方法代理对象public static MethodProxy CGLIB$findMethodProxy(Signature var0) {String var10000 = var0.toString();switch(var10000.hashCode()) {case -1422510685:if (var10000.equals("test()V")) {return CGLIB$test$0$Proxy;}break;case -379268499:if (var10000.equals("innerTest()V")) {return CGLIB$innerTest$1$Proxy;}break;}return null;}
}

从生成的代理对象文件中我们能看到每一个被代理的方法都会创建一个net.sf.cglib.proxy.MethodProxy对象与之绑定,同时代理方法的实际执行方法为我们编码时创建的net.sf.cglib.proxy.MethodInterceptor接口对象。

其中我们关注的四个方法我在这里重点标注一下,正常签名的方法是代理后的方法,增加CGLIB$前缀的方法则是原始类的方法

方法名 功能
CGLIB$test$0() 父类(原始类)的test方法
test() 代理后的test方法
CGLIB$innerTest$1() 父类(原始类)的innerTest方法
innerTest() 代理后的innerTest方法

2. 代理类是如何调用父类方法的

从生成的代理类的代码中我们可以看到,我们最终调用代理对象的方法时,实际上都是执行的编码时创建的net.sf.cglib.proxy.MethodInterceptor接口对象,这个接口只有一个intercept方法。

/**
* 所有被代理的方法实际调用时都会调用改方法,
* @param obj 代理对象,千万注意这个参数是代理后的对象,从生成的代理类中我们也可以看到,每次传入的this
* @param method 被代理的方法
* @param args 方法参数
* @param proxy 每个被代理的方法都会创建一个MethodProxy对象与之绑定,传入的就是与之绑定的MethodProxy对象,可以用它调用未被代理的方法(官方建议)
* @return 方法返回结果
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

net.sf.cglib.proxy.MethodInterceptor接口类似于jdk动态代理中的java.lang.reflect.InvocationHandler接口,所有代理方法的执行都交予该接口,从intercept方法的参数中我们可以看到调用未被代理类(即代理类的父类)的方法可以使用MethodProxy。

我们在编码的时候使用了methodProxy.invokeSuper()方法,我们接下来看下invokeSuper方法是如何调用父类方法的。

// net.sf.cglib.proxy.MethodProxy#invokeSuper
// 调用父类(未被代理的原始类)方法
// 官方限制了传入的obj必须是生成的代理对象
public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {// 初始化FastClassInfoinit();FastClassInfo fci = fastClassInfo;// 实际调用方法// f2绑定的是代理类,i2绑定的原始方法return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}
}

我们首先看一下init()方法都做了哪些事情,重要的参数来源我都已经在注释上标注了。

// net.sf.cglib.proxy.MethodProxy#init
// 初始化FastClassInfo
private void init()
{// 忽略double-check代码......// 获取类信息,其中createInfo是在MethodProxy对象创建时赋值的, c1是原类,c2是代理类CreateInfo ci = createInfo;FastClassInfo fci = new FastClassInfo();// 创建FastClass代理类, f1针对原类, f2针对代理类fci.f1 = helper(ci, ci.c1);fci.f2 = helper(ci, ci.c2);// sig1和sig2也是创建MethodProxy对象时创建的,sig1是代理方法的签名,sig2是原方法的签名// 这里千万要注意,代理方法的签名才是test(), 原方法的签名已被改成CGLIB$test$0()// 所以i1是代理方法的索引,i2是原方法的索引fci.i1 = fci.f1.getIndex(sig1);fci.i2 = fci.f2.getIndex(sig2);fastClassInfo = fci;createInfo = null;
}// net.sf.cglib.proxy.MethodProxy#create
// 创建MethodProxy对象的唯一入口,构造函数是私有的
// 由此可见createInfo的两个变量c1和c2是创建的时候传入的,回顾我们代理类中创建MethodProxy对象时传入的参数,我们能看到c1是原类,c2是代理类
// sig1是代理方法的签名,sig2是原方法的签名
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {MethodProxy proxy = new MethodProxy();proxy.sig1 = new Signature(name1, desc);proxy.sig2 = new Signature(name2, desc);proxy.createInfo = new CreateInfo(c1, c2);return proxy;
}// net.sf.cglib.proxy.MethodProxy#helper
// 生成FastClass类
private static FastClass helper(CreateInfo ci, Class type) {FastClass.Generator g = new FastClass.Generator();g.setType(type);g.setClassLoader(ci.c2.getClassLoader());g.setNamingPolicy(ci.namingPolicy);g.setStrategy(ci.strategy);g.setAttemptLoad(ci.attemptLoad);// 实际上内部调用了net.sf.cglib.core.AbstractClassGenerator#generate生成了一个代理类return g.create();
}

从源码中我们可以看到,init()方法创建了FastClassInfo对象,同时生成了两个FastClass代理类,f1与原始类绑定,f2与代理类绑定。接着我们看一下fci.f2.invoke(fci.i2, obj, args);这行代码究竟执行了什么。FastClass的invoke方法本是一个abstract方法:abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException;,但是我们生成的两个代理类中,均对这个函数的方法体进行了补全,这里我们先把f2的字节码反编译查看,其中我们使用的是两个方法getIndex()和invoke()方法,分别是根据方法签名获取索引,以及是调用实际方法拿到结果

public class Main$Test$$EnhancerByCGLIB$$a07c0bc4$$FastClassByCGLIB$$c6aa9344 extends FastClass {// 根据方法签名查询索引public int getIndex(Signature var1) {String var10000 = var1.toString();switch(var10000.hashCode()) {case -1659809612:if (var10000.equals("CGLIB$test$0()V")) {return 16;}break;case -1422510685:if (var10000.equals("test()V")) {return 7;}break;case -379268499:if (var10000.equals("innerTest()V")) {return 9;}break;case -216640445:if (var10000.equals("CGLIB$innerTest$1()V")) {return 17;}break;return -1;}// 根据索引找到对应的方法执行,然后返回执行结果public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {// 代理类对象// a07c0bc4是我们生成的代理类的类名,所以这就是为什么当初调用invokeSuper()的时候传入的obj必须是生成的代理对象,传入其他的对象会因为强制转换失败而报错a07c0bc4 var10000 = (a07c0bc4)var2;// 索引int var10001 = var1;try {switch(var10001) {case 7:// test代理方法var10000.test();return null;case 9:// innerTest代理方法var10000.innerTest();return null;case 16:// test原方法var10000.CGLIB$test$0();return null;case 17:// innerTest原方法var10000.CGLIB$innerTest$1();return null;}} catch (Throwable var4) {throw new InvocationTargetException(var4);}throw new IllegalArgumentException("Cannot find matching method/constructor");}
}

我们再来回头看一下我们编码时的调用Object result = methodProxy.invokeSuper(proxy, args);, 此时invokeSuper方法内部使用FastClass对象是与代理类绑定的f2,方法签名索引使用的原始方法,即CGLIB$test$0()V,所以实际上的结果也就是调用了原始方法。但是这个原始方法仍然是在代理对象内部执行的,所以如果存在内部调用的话,调用的仍然是代理对象的内部方法,所以切面依然生效!

3. 调用原方法只有这一种方式吗?

当然不是!!

MethodProxy.invoke()

我们看一下MethodProxy类的方法,就会发现其中有一个方法叫invoke方法。

// net.sf.cglib.proxy.MethodProxy#invoke
// 调用未被代理类对象的方法
// obj绝对不能使用代理对象,否则会因为无限递归导致栈溢出
public Object invoke(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci = fastClassInfo;return fci.f1.invoke(fci.i1, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();} catch (IllegalArgumentException e) {if (fastClassInfo.i1 < 0)throw new IllegalArgumentException("Protected method: " + sig1);throw e;}}

我们看一下这个方法的代码就会发现它跟invokeSuper()大同小异,无非就是使用绑定原始类的FastClass对象f1,方法签名是代理方法签名test()。接下来我们看下f1的源码

public class Main$Test$$FastClassByCGLIB$$e2977a20 extends FastClass {// 根据方法签名获取索引public int getIndex(Signature var1) {String var10000 = var1.toString();switch(var10000.hashCode()) {case -1422510685:if (var10000.equals("test()V")) {return 0;}break;case -379268499:if (var10000.equals("innerTest()V")) {return 1;}break;return -1;}// 调用方法public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {// 传入的对象只要是任何一个Test的子类即可Test var10000 = (Test)var2;int var10001 = var1;try {switch(var10001) {case 0:var10000.test();return null;case 1:var10000.innerTest();return null;}} catch (Throwable var4) {throw new InvocationTargetException(var4);}throw new IllegalArgumentException("Cannot find matching method/constructor");}
}

从f1的源码中我们可以看出,这里没有涉及到任何因为代理而生成的方法,例如使用CGLIB$前缀的一系列方法,invoke方法传入的对象只需要是Test的任何一个子类对象即可,但是绝对不能传入我们生成的代理类,假如我们传入生成的代理类proxy的话,proxy的test()方法实际是被代理过的,最终执行又会使用MethodProxy.invoke()方法从而形成无限递归造成Stack over flow。正确的方式应该是传入一个Test对象或Test子类对象,如下。

// 创建正常test对象
Test originalTest = new Test();new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {long cost = System.currentTimeMillis();// 调用时使用正常test对象Object result = methodProxy.invoke(originalTest, args);System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));return result;}
}

Method.invoke

我们仔细观察下intercept方法,里面传入的参数中有原始方法的Method对象,那我们同样只需要构造一个正常的Test对象就能调用了。

// 创建正常test对象
Test originalTest = new Test();new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {long cost = System.currentTimeMillis();// 调用时使用正常test对象Object result = method.invoke(originalTest);System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));return result;}
}

总结

除了使用MethodProxy.invokeSuper()方法可以不创建任何对象的调用原始类方法之外,还可以通过MethodProxy.invoke()和传入的原始方法Method.invoke()来调用原始方法,但是后两种调用方式就类似于jdk的动态代理了,都需要将原始方法的执行托管给一个没有经过代理的对象,也就同样都存在这jdk动态代理的内部调用切面无法生效的问题,以上两种方法的执行结果均是:

test
inner test
method test cost time :1002

总结

至此我们已经了解了cglib如何实现动态代理的,总结来说就是以下步骤

  1. 通过生成字节码创建原始类的一个子类作为代理类,原来父类中所有方法的实现均托管给net.sf.cglib.proxy.MethodInterceptor对象
  2. 在net.sf.cglib.proxy.MethodInterceptor对象中我们可以通过以下三种方式调用原始类的方法
    1. 使用MethodProxy.invokeSuper()方法,该方法会将原始方法的执行重新托管给代理类,所以即使是内部调用,内部方法的切面增强代码依然生效
    2. 使用MethodProxy.invoke()方法,该方法将原始方法的执行托管给未被代理的对象,若托管给代理类则会造成栈溢出,仍然存在内部调用切面失效问题
    3. 使用Method.invoke()方法,与2相同

接着我们来回答一下开头的几个问题。

  1. 首先cglib的动态代理一定不会出现内部调用失效的问题吗?不一定,这取决于调用原始方法时是采用哪种方式,实际上spring-aop中使用cglib生成代理类时调用的就是MethodProxy.invoke()方法,spring中类即使没有实现接口(没有实现接口spring会选择由cglib进行代理),也仍然是会存在内部调用切面失效问题。为什么spring这么设计,我猜大概是为了跟jdk动态代理的效果保持一致吧,不能同样是spring生成的代理类,一部分存在内部调用问题,一部分不存在吧,不太合理。
// org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
// 使用cglib获取代理类的方法入口
public Object getProxy(@Nullable ClassLoader classLoader) {// 省略一大堆代码......// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 设置回调函数Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);
}// 方法org.springframework.aop.framework.CglibAopProxy#getCallbacks中返回的一系列回调函数,
// 随便挑一两个看就能看到其中调用的MethodProxy.invoke()方法
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {@Override@Nullablepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object target = null;// 获取目标对象,这个对象并没有被代理target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);// 调用invoke方法,使用未被代理对象作为目标对象retVal = methodProxy.invoke(target, argsToUse);}else {// We need to create a method invocation...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}
}
  1. 第二个问题,cglib动态代理,一个原始类只会生成一个代理类(这里把cglib直接生成字节码然后再加载到JVM中的都叫做代理类)吗?不是,首先代理类一旦被创建时需要被缓存的,而生成缓存key对象实际上也是生成了一个代理类对象,还记得我们一开始的createHelper()方法中的这段代码吗?这段代码实际上生成了net.sf.cglib.proxy.Enhancer.EnhancerKey接口的一个代理类对象。
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);

其次,代理类调用原始类方法时如果采用的是MethodProxy的方法,则初次调用时会生成两个FastClass抽象类的代理对象,一个绑定原始类,一个绑定代理类。而每个被代理的方法实际上都会创建一个MethodProxy对象,所以一个代理类有n个方法被调用过,就会生成2*n个FastClass的代理对象。

所以使用cglib进行动态代理,生成的代理类绝不止一个,而jdk的动态代理,则确确实实只会生成一个代理类。使用cglib进行动态代理会生成大量的代理类,Class对象都是存储在JVM的方法区的,Class对象的卸载也比较严格,所以使用cglib进行动态代理会增大方法区的存储压力。

  1. 同时最后一个问题,为什么cglib比jdk动态代理要慢?方法初次调用都会生成两个FastClass的代理类,然后加载到JVM中并实例化,这些开销加上自然要比Jdk动态代理要高。

cglib动态代理与jdk动态代理有相同的地方,也有不同的地方,cglib生成更多的代理类,但是也实现了jdk动态代理无法实现的对类进行代理的功能,二者并无孰优孰劣,也许像spring-aop那样将二者的优势结合在一起的应用才是他们二者发挥最大价值的途径吧。

cglib 动态代理原理详解相关推荐

  1. JDK和cglib动态代理原理详解

    AOP的基础是Java动态代理,了解和使用两种动态代理能让我们更好地理解 AOP,在讲解AOP之前,让我们先来看看Java动态代理的使用方式以及底层实现原理. 转自https://www.jiansh ...

  2. 动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    2019独角兽企业重金招聘Python工程师标准>>> 在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码.当前有很多开源框架可以完成这些功能,如A ...

  3. java的动态代理机制详解

    2019独角兽企业重金招聘Python工程师标准>>> 参考资料 1.java的动态代理机制详解 转载于:https://my.oschina.net/Howard2016/blog ...

  4. CGLib动态代理原理

    CGLib动态代理原理 CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法: 看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调 ...

  5. 【java】CGLIB动态代理原理分析

    1.概述 前一篇文章介绍了CGLIB中常用的API,实际上使用了Enhancer和MethodInterceptor之后会生成代理子类,这篇文章就是分析一下CGLIB动态代理的原理. 2.CGLIB动 ...

  6. 【Spring AOP】静态代理设计模式、Spring 动态代理开发详解、切入点详解(切入点表达式、切入点函数)

    AOP 编程 静态代理设计模式 1. 为什么需要代理设计模式 2. 代理设计模式 名词解释 代理开发的核心要素 静态代理编码 静态代理存在的问题 Spring 动态代理开发 搭建开发环境 Spring ...

  7. JDK和CGLIB动态代理原理

    JDK动态代理原理解析 一.例子: 1.定义基础接口 public interface HttpApi {String get(String url); } 2.实现类 public class Re ...

  8. 深入理解Java Proxy和CGLIB动态代理原理

    点击上方关注,每天进步一点点 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译 ...

  9. Java Proxy和CGLIB动态代理原理

    动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...

  10. CGLib动态代理原理及实现

    原文连接:http://songbo-mail-126-com.iteye.com/blog/968792 ---------------------------------------------- ...

最新文章

  1. 【oracle】补充 cursor 基本例子
  2. shell脚本(查看多台服务器端口)
  3. redis集群扩容和缩容_Redis一站式管理平台,支持集群的创建、管理、监控和报警...
  4. ajax请求返回json实例,Jquery Ajax 学习实例2 向页面发出请求 返回JSon格式数据
  5. 计算机用户win7修改不,Win7电脑时间改不了的解决方法
  6. OpenGL ES Emulator横向比较
  7. Helm 3 完整教程(八):Helm 函数讲解(2)字符串函数
  8. HDU2072 tri树/map/set/字符串hash
  9. 《让人无法说 NO的攻心说话术》摘要
  10. epoll监听文件_epoll
  11. 基于java+jsp的户籍管理系统
  12. 二十一世纪大学英语读写基础教程学习笔记(原文)——4 - The Happiest Man in the World(世界上最幸福的人)
  13. 共享电动滑板车来了,它估值为何高达20亿美金?
  14. 如何恢复因为chrome同步而删除的书签
  15. 线性代数 为什么齐次线性方程有非零解的充要条件是系数行列式不等于零?
  16. 微信11个超级实用的小技巧,值得一试
  17. 数字化转型是新瓶装旧酒吗?
  18. MySQL中操作关系型数据库 SQL
  19. 齐博x1新用户手工注册接口
  20. [转载]Python兵器谱

热门文章

  1. Alexa排名 介绍
  2. 经典混响插件免安装+50拓展 – Audio Ease Altiverb 7 XL 7.2.8 WiN
  3. Unity3D客户端实时同步技术
  4. fedora mysql gui,fedora 14 启用无线网卡 | 勤奋的小青蛙
  5. fedora 14 root登陆修改方法
  6. linux fedora14 u盘运行,通过U盘安装Fedora-14-i686-Live-Desktop
  7. NDK学习笔记:线程JNIEnv,JavaVM,JNI_OnLoad(GetEnv返回NULL?FindClass返回NULL?)
  8. mysql覆盖索引理解
  9. 九行代码自动下载instagram原图
  10. 2048小游戏——网页版(基础篇)