Cglib invoke为什么会死循环?
Cglib invoke为什么会死循环?
案例分析
package com.demo;
public class UserService {
public void addUser(){
System.out.println("添加用户");
}
}
简单介绍下UserService 模拟数据层操作,TxHelper作为一个cglib增强的回调.
package com.demo;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TxHelper implements MethodInterceptor {
public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
Object res=proxy.invokeSuper(obj,args);
// 这里调用invoke方法就会导致死循环从而栈溢出
// Object res=proxy.invoke(obj,args);
System.out.println("关闭事务");
return res;
}
/*这个方法根据clazz使用空参构造器获取clazz的cglib子类*/
public Object getInstance(Class clazz){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object getInstance2(Class clazz,Class params[],Object[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create(params,args);
}
}
TxHelper中为了节省代码量,将获取Cglib生成的子类写在TxHelper中,即getInstance(class)和getInstance2(clazz,clazz[],Object[])方法,都是调用的Enhancer.create来获取Cglib子类.
Cglib依赖添加
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-util</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
依赖说明:cglib2.2版本只依赖于asm3.1,asm-util是asm的相关工具包,这里引入是另有目的.

测试方法
package com.demo;
import net.sf.cglib.core.DebuggingClassWriter;
public class UserServiceTests {
public static void main(String[] args) {
//设置cglib.debugLocation属性指定将动态代理的类指定生成在哪里
// 以下方式等价于 -Dcglib.debugLocation=E:\\data\\blog
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\data\\blog");
UserService userService= (UserService) new TxHelper().getInstance(UserService.class);
userService.addUser();
}
}
测试方法说明: System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY)是用于设置 cglib动态代理的子类生成的位置,等价于启动参数 -Dcglib.debugLocation=E:\\data\\blog,这样就可以将代理子类生成到我们指定目录。 上面额外引入的依赖 asm-util则是会将动态生成的子类的字节码展示出来。
public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
测试效果

可以看到methodProxy.invokeSuper方法会调用父类的方法 成功添加父类,至于另外的情况最后再分析.
进入之前设置的cglib.debugLocation指定的目录,查看cglib生成的子类class文件.
在该目录下的自己的包名文件夹com/demo下会有我们的UserService的cglib子类,额外的还有net/sf/cglib/core以及net/sf/cglib/proxy这两个cglib额外生成的层级目录,因为上面引入了 asm-util ,所以伴随着class文件,还会有一些特殊的 asm 文件,asm 文件使用notepad++等工具就可以查看了,这些asm文件记录着每一个类生成过程中字节码.
查看cglib生成的 class文件
可以看到我们的UserService类就已经生成了三个class文件, 其中UserService$$EnhancerByCGLIB$$268385a2可以理解为是 UserService的真实子类,而 UserService$$FastClassByCGLIB$$417ebd8c和UserService$$EnhancerByCGLIB$$268385a2$$FastClassByCGLIB$$c6a21d27 则是计算动态代理类调用方法走父类还是本身的方法,这里后面也会发现CGLIB比反射效率高(我的理解,直接调用会比反射效率高).

查看 **asm ** 文件
补充一点:class version 46.0代表 JDK1.2版本编译的class文件,高版本编译环境可编译低版本编译过来的Class文件,但是低版本的JDK1.7就不能编译JDK1.8的class文件了,这时候尝试编译会报错:Unsupported major version

CGLIB子类一探究竟
上面测试效果、CGLIB子类我们都获取到了,甭管怎么生成的CGLIB子类,我们先来看看,CGLIB子类究竟长啥样?
通常会用 jd-gui 工具查看class文件,但是cglib生成的子类用 jd-gui 查看效果不是很好,不利于阅读,查看方式:直接将class文件拖到 IDEA 中,就可以查看反编译后的代码.
我不太习惯看 反编译后的 class 文件,于是就将 class文件的内容复制到 com/demo目录下,并且修改文件名为 .java结尾,但是class文件内容直接复制到java 文件中,会有好多处红叉报错:一一解决下. 这里改成java文件只是为了方便自己阅读,不能用来调试.
动态代理子类的java文件
第一处报错: final类型变量可能没初始化,导致编译不过;其实cglib是初始化了,但是为啥IDEA不认呢?
先说解决方法: 将final 关键字都去掉

其实cglib关于 final变量都是初始化了的,一个静态代码块调用 CGLIB$STATICHOOK1,在静态方法CGLIB$STATICHOOK1中进行了初始化,但是为啥IDEA不认呢?
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.demo.UserService$$EnhancerByCGLIB$$268385a2");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"addUser", "()V", "getUser", "()V"}, (var1 = Class.forName("com.demo.UserService")).getDeclaredMethods());
CGLIB$addUser$0$Method = var10000[0];
CGLIB$addUser$0$Proxy = MethodProxy.create(var1, var0, "()V", "addUser", "CGLIB$addUser$0");
CGLIB$getUser$1$Method = var10000[1];
CGLIB$getUser$1$Proxy = MethodProxy.create(var1, var0, "()V", "getUser", "CGLIB$getUser$1");
var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$2$Method = var10000[0];
CGLIB$finalize$2$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$2");
CGLIB$equals$3$Method = var10000[1];
CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
CGLIB$toString$4$Method = var10000[2];
CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
CGLIB$hashCode$5$Method = var10000[3];
CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
CGLIB$clone$6$Method = var10000[4];
CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
}
第二处报错:
MethodInterceptor.intercept方法异常没有捕获解决方案: 手动Try catch异常吧.

报错三:
new实例化对象缺少括号,下面多了字节码<init>()解决方案:上面添上括号,
<init>()这行注释掉

另外两个 FastClass 类也是类似的操作,细心地发现:只有UserService$$EnhancerByCGLIB$$743464da是继承了我们的UserService类的并且实现了Factory接口 ,另外两个类都是继承自FastClass.
动态代理子类实例化过程
上一步已经得到了cglib生成的动态代理类,动态代理类肯定要初始化一个实例对象,实例化的流程呢:AbstractClassGenerator.create--->Enhancer.firstInstance--->Enhancer.createUsingReflection
private Object createUsingReflection(Class type) {
setThreadCallbacks(type, callbacks);
try{
if (argumentTypes != null) {
return ReflectUtils.newInstance(type, argumentTypes, arguments);
} else {
return ReflectUtils.newInstance(type);
}
}finally{
// clear thread callbacks to allow them to be gc'd
setThreadCallbacks(type, null);
}
}
Enhancer.createUsingReflection首先setThreadCallbacks(type,callbacks)----->ReflectUtils.newInstance
type就是当前这个动态代理子类的class UserService$$EnhancerByCGLIB$$743464da,callbacks 就是我们自己设置到enhancer中的.

setThreadCallbacks就是调用动态代理的类的静态CGLIB$SET_THREAD_CALLBACKS方法,将callbacks设置进入,查看动态代理类的静态CGLIB$SET_THREAD_CALLBACKS方法:
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
private static ThreadLocal CGLIB$THREAD_CALLBACKS;
其中 CGLIB$THREAD_CALLBACKS本来是final static 变量,不过被我们强行改为了static,这里可以看到 动态代理的类有个静态量ThreadLocal,持有回调数组callback,至于ThreadLocal 的初始化在 static 代码块中完成了.
ReflectUtils.newInstance方法会根据动态代理类可用的构造方法调用 反射来实例化 动态代理子类UserService$$EnhancerByCGLIB$$743464da . 而实例化完成之后,仍然调用CGLIB$THREAD_CALLBACKS将ThreadLocal变量中的callback清除.
UserService$$EnhancerByCGLIB$$743464da的实例化
public UserService$$EnhancerByCGLIB$$743464da() {
CGLIB$BIND_CALLBACKS(this);
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
UserService$$EnhancerByCGLIB$$743464da var1 = (UserService$$EnhancerByCGLIB$$743464da)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
可以看到 动态代理类初始化调用了 CGLIB$BIND_CALLBACKS(this),先判断CGLIB$BOUND标志位,第一次绑定之后就为true了,绑定过程就是 从 ThreadLocal 中提取出来callback,并赋给动态代理对象的CGLIB$CALLBACK_0属性,回调属性都是以CGLIB$CALLBACK_开头,假如回调数组元素为多个,那就有多少个属性,分别是CGLIB$CALLBACK_0、CGLIB$CALLBACK_1.
总结下来:回调数组callback赋值给了动态代理类的每一个属性,有几个回调元素,就有几个回调属性;赋值过程是在实例化动态代理类时候完成的,为了防止线程不安全,用的是ThreadLocal来保存callback
动态代理类调用过程
现在UserService$$EnhancerByCGLIB$$743464da实例已经有了,调用addUser方法,会先进入子类的方法.
public final void addUser() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
try {
var10000.intercept(this, CGLIB$addUser$0$Method, CGLIB$emptyArgs, CGLIB$addUser$0$Proxy);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
} else {
super.addUser();
}
}
尝试获取CGLIB$CALLBACK_0属性,为什么说尝试呢?如果CGLIB$CALLBACK_0为空,还回去从 ThreadLocal中取,当获取到CGLIB$CALLBACK_0不为空,调用CGLIB$CALLBACK_0.intercept方法.
四个入参:
- this,代表当前代理类的对象,没啥好说的;
CGLIB$addUser$0$Method静态代码块中初始化,获取的是父类void UserService.addUser()方法,也就是父类方法的Method.
private final static Method CGLIB$addUser$0$Method;
static{
..........
Class var1=null;
try {
CGLIB$addUser$0$Method = ReflectUtils.findMethods(new String[]{"addUser", "()V"}, (var1 = Class.forName("com.demo.UserService")).getDeclaredMethods())[0];
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
............
}
CGLIB$emptyArgs一个大小为0的Object数组CGLIB$addUser$0$Proxy通过MethodProxy.create(superClass,cglibSonClass,methodDescriptor,superMethodName,sonMethodName)来获取MethodProxy对象.private static final MethodProxy CGLIB$addUser$0$Proxy; static{
Class var0 = null;
try {
var0 = Class.forName("com.demo.UserService$$EnhancerByCGLIB$$743464da");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class var1=null;
try {
CGLIB$addUser$0$Method = ReflectUtils.findMethods(new String[]{"addUser", "()V"}, (var1 = Class.forName("com.demo.UserService")).getDeclaredMethods())[0];
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
CGLIB$addUser$0$Proxy = MethodProxy.create(var1, var0, "()V", "addUser", "CGLIB$addUser$0");
}
MethodProxy.create过程
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;
}
解释说明: c1是父类class,c2是cglib子类class,desc就是方法修饰符,比如()V代表无如参void类型,(Ljava/lang/String;)I代表String入参int返回值的方法,对象类型都是 L全限定名; 这种,基本数据类型int对应 I 这种,数组对应 [
name1是父类中方法的名称,name2是子类中方法的名称 ,比如addUser现在cglib子类想要直接调用父类的方法,不需要增强,那我调用CGLIB$addUser$0方法就等价于 直接调用父类的方法,调用方式如下:
意味着:cglib子类中的addUser方法就是增强的方法,而CGLIB$addUser$0就是未增强的方法,方法名的生成规则后续再记录.
public static void main(String[] args) throws Exception {
//设置cglib.debugLocation属性指定将动态代理的类指定生成在哪里
// 以下方式等价于 -Dcglib.debugLocation=E:\\data\\blog
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\data\\blog");
UserService userService= (UserService) new TxHelper().getInstance(UserService.class);
// userService.addUser();
for (final Method declaredMethod : userService.getClass().getDeclaredMethods()) {
System.out.println(declaredMethod.getName());
}
Method m = userService.getClass().getDeclaredMethod("CGLIB$addUser$0", null);
m.invoke(userService,null);
}
回到MethodProxy.create中,生成了一个MethodProxy对象,sig1、sig2都是Signature类型的,代表方法签名,sig1是父类方法的签名,sig2是子类方法的签名;而 createInfo属性最主要的是持有 c1(父类class)、c2(cglib子类class).
至此我们已经分析完毕MethodProxy.create的逻辑.
MethodInterceptor.intercept调用逻辑
**MethodInterceptor ** 怎么来的、有什么属性我们已经分析完毕,intercept方法就进入了我们自定义的回调类中,离我们分析的目标更近了.
我们自定义回调逻辑中,知道proxy.invokeSuper方法才是正确的,而proxy.invoke会导致栈溢出. 现在我们已经知道这个intercept方法的几个入参: obj 就是当前cglib子类实例,method就是父类UserService.adUser()的Method,args就是方法的入参,proxy就是上面创建的MethodProxy对象。 cglib子类中每一个继承父类的方法都会生成一个MethodProxy对应,额外还有Object类的toString、hashCode、equals、finalize、clone方法
public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
Object res=proxy.invokeSuper(obj,args);
// 这里invoke方法就会导致死循环从而栈溢出
// Object res=proxy.invoke(obj,args);
System.out.println("关闭事务");
return res;
}
动态代理之MethodProxy.invokeSuper
MethodProxy的 invokeSuper方法如下.

说明文档上介绍:调用增强类(cglib子类)的父类的方法,obj对象就是增强的cglib子类.
第一步 init()方法
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
}
}
}
}
双重检查锁机制,一直以为是安全的,但是cglib注释提到 这段代码在JDK1.5之前可能会导致
fastClassInfo实例化多个?所以fastClassInfo对象用了volatile关键字来修饰,咱也没用过volatile关键字,这里就暂留作疑问吧.createInfo对象之前提到了,是在实例化MethodProxy中实例化的,持有两个重要属性父类c1、cglib子类c2,helper方法另外生成了两个FastClass的实例,f1就是UserService这种被UserService$$FastClassByCGLIB$$xxxx,f2就是UserService$$EnhancerByCGLIB$$xxx$$FastClassByCGLIB$$xxx,生成过程采用ASM生成字节码较为复杂就忽略,如果需要查看字节码,引入asm-util并且指定cglib.debugLocation参数即可生成asm文件.这里的
fastClassInfo是单例的,又有点缓存的意思,很多初始化逻辑在第一次实例化过程中完成,后续进入就不需要init.
FAQ
init()方法中volatile修饰fastClassInfo的作用是啥?
CGLIB的FastClass实现类
参考上面获取cglib java类,我们可以看到指定的cglib.debugLocation指定包目录下另外两个class就是这里的FastClass
以UserService的FastClass来看,主要实现了三类方法getIndex、invoke、newInstance,先来看第一类方法getIndex
getIndex
Signature是cglib包中的类,代表一个方法的签名. 根据Signature来获取索引,索引代表要执行哪个方法. 首先判断 方法名 + 方法返回值 的hash值,hash值一致的情况下,还需要再用 equals 方法再次比较。 因为两个不同的字符串是有可能hash值相等的. 这样确保获取到的索引正确,索引用呢是在invoke方法中. 至于这个索引则是在 生成字节码中判断的.

invoke
上面获取的方法索引在这里就可以体现用处了,就是var1参数。 switch中的序号和上面 getIndex一定是一一对应的.
同理,cglib子类的getIndex、invoke方法都是类似的,只不过cglib子类的 FastClassgetIndex以及 invoke的选项会多一倍,因为一个是 继承自父类,也就是增强的方法,还一个是重命名的父类方法,单纯调用父类方法,比如CGLIB$toString$3()

init方法继续
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
}
}
}
FastClassInfo通过ASM生成了两个FastClass子类之后,f1就是 UserService$$FastClassByCGLIB的实例,f2就是UserService$$EnhancerByCGLIB$$FastClass..的实例,sig1代表被增强方法的签名,addUser()V这种,sig2代表别增强方法在cglib子类中的签名,如CGLIB$addUser$0.
invokeSuper方法调用
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();
}
}
fci.i2就是CGLIB$addUser$0在其中的索引,比如我这里是15. obj对象是cglib的子类,invokeSuper就是调用这个cglib增强类的索引为15的方法,而绕回来这个索引又是通过 getIndex 获取CGLIB$addUser$0获取的,等于直接调用了 cglib增强子类的CGLIB$addUser$0方法,就是调用了父类的addUser方法. 这样invokeSuper就实现了动态调用父类的方法,比如UserService有很多种方法,我现在是A方法,那里面的MethodProxy对象又是一个全新的关联了A以及CGLIB$A$...方法,而我们只需要使用proxy.invokeSuper就能自动调用父类方法,这里绕开了反射,相应的在动态代理类、每个方法第一次调用会带来一定的性能损耗,但是后续使用起来与普通调用无差别,会优于后续反射来调用方法.
final void CGLIB$addUser$0() {
super.addUser();
}
invoke方法调用
相信这里我们就能大概明白,为啥invoke会带来方法死循环.
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的区别大概是,f2换成了f1,索引也换成了对应的i1,我们不饶一圈,就是直接调用了动态代理类的addUser方法. 嗯? 我们都增强了addUser方法,你还调用增强的方法,那不是又饶了一圈嘛,难怪会死循环,从而栈溢出.
CGLIB实现类如何不调用增强方法
我常常在想假如我就不想增强某个方法,咋调用呢? 想了个笨的方法,反射找到那个直接调用父类的方法,反射执行行不?
public static void main(String[] args) throws Exception {
//设置cglib.debugLocation属性指定将动态代理的类指定生成在哪里
// 以下方式等价于 -Dcglib.debugLocation=E:\\data\\blog
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\data\\blog");
UserService userService= (UserService) new TxHelper().getInstance(UserService.class);
// userService.addUser();
for (final Method declaredMethod : userService.getClass().getDeclaredMethods()) {
System.out.println(declaredMethod.getName());
}
Method m = userService.getClass().getDeclaredMethod("CGLIB$addUser$0", null);
m.setAccessible(true);
m.invoke(userService,null);
}
查看输出.......

想了想,知识有限,我们查看他所有的方法,还好对于CGLIB有一点点了解,找那个方法名类似CGLIB$XXX$数字的方法,反射调用试试呗, 虽然这种方法肯定存在很多弊端,奈何能耐有限呢?
Cglib invoke为什么会死循环?的更多相关文章
- cglib invoke 和 invokeSuper 可用的组合
在深入字节码理解invokeSuper无限循环的原因中,我们理解的cglib的原理和其中一个合理的调用方式.但是这个调用方式是基于类的,对所有实例生效.实际场景中,我们可能只是希望代理某个具体的实例, ...
- Cglib invoke以及invokeSuper的一点区别
简单记录下,解决的一个问题,Cglib的invoke和invokeSuper的区别: 简而言之,invoke方法调用的对象没有增强过,invokeSuper方法调用的对象已经是增强了的,所以会再走一遍 ...
- java代理的深入浅出(二)-CGLIB
java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...
- JDK动态代理实现源码分析
JDK动态代理实现方式 在Spring框架中经典的AOP就是通过动态代理来实现的,Spring分别采用了JDK的动态代理和Cglib动态代理,本文就来分析一下JDK是如何实现动态代理的. 在分析源码之 ...
- Cglib源码分析 invoke和invokeSuper的差别(转)
原文 https://blog.csdn.net/makecontral/article/details/79593732 Cglib的实例 本文重在源码的分析,Cglib的使用不再复述. //被代理 ...
- java 笔记(3) —— 动态代理,静态代理,cglib代理
0.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口. 代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存 ...
- CGLIB学习笔记
0 概述 CGLIB基于ASM实现.提供比反射更为强大的动态特性.使用CGLIB可以非常方便的实现的动态代理. 0.1 CGLIB包结构 net.sf.cglib.core 底层字节码处理类. ...
- JDK动态代理和cglib代理详解
JDK动态代理 先做一下简单的描述,通过代理之后返回的对象已并非原类所new出来的对象,而是代理对象.JDK的动态代理是基于接口的,也就是说,被代理类必须实现一个或多个接口.主要原因是JDK的代理原理 ...
- Cglib动态代理实现方式
Cglib动态代理实现方式 我们先通过一个demo看一下Cglib是如何实现动态代理的. 首先定义个服务类,有两个方法并且其中一个方法用final来修饰. public class PersonSer ...
随机推荐
- zookeeper从3.4.8升级到3.4.14
升级背景说明: 最近在做系统安全扫描时,扫出来zookeeper存在安全漏洞 Apache Zookeeper 缓冲区溢出漏洞(CVE--) 官方给出的升级建议: 地址:https://zookeep ...
- CentOS7优化打开文件句柄数,修改MariaDB允许最大连接数、允许最大插入数据库的数据大小。
修改服务器配置:vim /etc/systemd/system.conf查找并修改下列两行的值:DefaultLimitNOFILE=1024000DefaultLimitNPROC=1024000 ...
- 【翻译】Flink Table Api & SQL — 性能调优 — 流式聚合
本文翻译自官网:Streaming Aggregation https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table ...
- 工控随笔_22_关于Profibus网络接线的规则
最近在做一个项目调试,用的是西门子的PLC,416-2 DP,下面挂了几个DP子网,在进行现场网络测试的时候,有几个走的DP网络的 绝对值编码器,无论怎么弄DP网络不能联通. 一开始我以为DP网线接的 ...
- ubuntu 16 typora 安装 ,14系统的不管用。。
# sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA300B7755AFCFAE linuxidc@linuxidc:~ ...
- [Py] 简单的 Python 运行环境
python:https://www.python.org/downloads/ pip:https://pip.pypa.io/en/stable/installing/#upgrading-pip ...
- python1-变量和简单的数据类型
变量和简单的数据类型 1 Hello World程序 1.1 执行py文件 linux下编辑一个文件,hello.py print("Hello world") 执行 # pyth ...
- hbase 操作
视频随笔视频地址:hbase教程 1.与传统关系型数据库的区别 hbase 传统分布式 单机列动态增减 建表时候指定只有字符串一种数据类型 数值,字符空值不被存储 存储不支持SQL 查 ...
- 最新 多点Dmalljava校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.多点Dmall等10家互联网公司的校招Offer,因为某些自身原因最终选择了多点Dmall.6.7月主要是做系统复习.项目复 ...
- Oracle Spatial分区应用研究之六:全局空间索引下按县分区与按省分区效率差异原因分析
1.实验结论 全局空间索引下,不同分区粒度之所有效率会有不同,差异并不在于SDO_FILTER操作本身,而在于对于数据字典表的访问次数上: 分区越多.表上的lob column越多,对数据字典表的访问 ...