CGLib动态代理原理

CGLib动态代理原理

CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法;

看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑,结构还是一目了然的;
在这里插入图片描述

1.CGLib的基本使用

使用一下CGLib,在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer;

使用的CGLib版本是2.2.2,我是随便找的,不同的版本有点小差异,建议用3.x版本的…我用的maven项目进行测试的,首先要导入cglib的依赖

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>

目标类(一个公开方法,另外一个用final修饰):

package com.wyq.day527;public class Dog{final public void run(String name) {System.out.println("狗"+name+"----run");}public void eat() {System.out.println("狗----eat");}
}

方法拦截器:

package com.wyq.day527;import java.lang.reflect.Method;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class MyMethodInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("这里是对目标类进行增强!!!");//注意这里的方法调用,不是用反射哦!!!Object object = proxy.invokeSuper(obj, args);return object;}  
}

测试类:

package com.wyq.day527;import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;public class CgLibProxy {public static void main(String[] args) {//在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数Enhancer enhancer = new Enhancer();//设置目标类的字节码文件enhancer.setSuperclass(Dog.class);//设置回调函数enhancer.setCallback(new MyMethodInterceptor());//这里的creat方法就是正式创建代理类Dog proxyDog = (Dog)enhancer.create();//调用代理类的eat方法proxyDog.eat();       }
}

测试结果:

在这里插入图片描述
使用起来还是很容易的,但是其中有很多小细节我们要注意,下面我们就慢慢的看;

2.生成动态代理类

首先到我们指定的目录下面看一下生成的字节码文件,有三个,一个是代理类的FastClass,一个是代理类,一个是目标类的FastClass,我们看看代理类(Dog

EnhancerByCGLIBEnhancerByCGLIB

a063bd58.class),名字略长~后面会仔细介绍什么是FastClass,这里简单说一下,就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低,也可以叫做FastClass机制
在这里插入图片描述  然后我们可以结合生成的动态代理类来简单看看原理,一个反编译工具

我们就打开xxx.java文件,稍微进行整理一下,我们可以看到对于eat方法,在这个代理类中对应会有eat 和CGLIB$eat0这两个方法;−其中前者eat则是我们使用代理类时候调用的方法,−后者CGLIB0这两个方法;   - 其中前者eat 则是我们使用代理类时候调用的方法,   - 后者CGLIB0  eat使  CGLIBeat0是在方法拦截器里面调用的,换句话来说当我们代码调用代理对象的eat方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIB0是在方法拦截器里面调用的,  换句话来说当我们代码调用代理对象的eat方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIB0 eatinterceptproxy.invokeSuperCGLIBeat$0这个方法,不要因为方法名字太长了就觉得难,其实原理很简单。。。(顺便一提,不知道大家有没有发现代理类中只有eat方法,没有run方法,因为run方法被final修饰了,不可被重写,所以代理类中就没有run方法,这里要符合java规范!!!)

package com.wyq.day527;import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;//可以看到这个代理类是继承我们的目标类Dog,并且顺便实现了一个Factory接口,这个接口就是一些设置回调函数和返回实例化对象的方法
public class Dog$$EnhancerByCGLIB$$fbca2ec6 extends Dog implements Factory{//这里有很多的属性,仔细看一下就是一个方法对应两个,一个是Method类型,一个是MethodProxy类型private boolean CGLIB$BOUND;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback CGLIB$STATIC_CALLBACKS[];private MethodInterceptor CGLIB$CALLBACK_0;private static final Method CGLIB$eat$0$Method;private static final MethodProxy CGLIB$eat$0$Proxy;private static final Object CGLIB$emptyArgs[];private static final Method CGLIB$finalize$1$Method;private static final MethodProxy CGLIB$finalize$1$Proxy;private static final Method CGLIB$equals$2$Method;private static final MethodProxy CGLIB$equals$2$Proxy;private static final Method CGLIB$toString$3$Method;private static final MethodProxy CGLIB$toString$3$Proxy;private static final Method CGLIB$hashCode$4$Method;private static final MethodProxy CGLIB$hashCode$4$Proxy;private static final Method CGLIB$clone$5$Method;private static final MethodProxy CGLIB$clone$5$Proxy;//静态代码块,调用下面静态方法,这个静态方法大概做的就是获取目标方法中每个方法的MethodProxy对象static {CGLIB$STATICHOOK1();}//无参构造器public Dog$$EnhancerByCGLIB$$fbca2ec6(){CGLIB$BIND_CALLBACKS(this);}//此方法在上面的静态代码块中被调用static void CGLIB$STATICHOOK1(){//注意下面这两个Method数组,用于保存反射获取的Method对象,避免每次都用反射去获取Method对象Method[] amethod;Method[] amethod1;CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];//获取目标类的字节码文件Class class1 = Class.forName("com.wyq.day527.Dog$$EnhancerByCGLIB$$fbca2ec6");//代理类的字节码文件Class class2;//ReflectUtils是一个包装各种反射操作的工具类,通过这个工具类来获取各个方法的Method对象,然后保存到上述的Method数组中amethod = ReflectUtils.findMethods(new String[] {"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());Method[] _tmp = amethod;//为目标类的每一个方法都建立索引,可以想象成记录下来目标类中所有方法的地址,需要用调用目标类方法的时候根据地址就能直接找到该方法//这就是此处CGLIB$xxxxxx$$Proxy的作用。。。CGLIB$finalize$1$Method = amethod[0];CGLIB$finalize$1$Proxy = MethodProxy.create(class2, class1, "()V", "finalize", "CGLIB$finalize$1");CGLIB$equals$2$Method = amethod[1];CGLIB$equals$2$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");CGLIB$toString$3$Method = amethod[2];CGLIB$toString$3$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");CGLIB$hashCode$4$Method = amethod[3];CGLIB$hashCode$4$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$4");CGLIB$clone$5$Method = amethod[4];CGLIB$clone$5$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");amethod1 = ReflectUtils.findMethods(new String[] {"eat", "()V"}, (class2 = Class.forName("com.wyq.day527.Dog")).getDeclaredMethods());Method[] _tmp1 = amethod1;CGLIB$eat$0$Method = amethod1[0];CGLIB$eat$0$Proxy = MethodProxy.create(class2, class1, "()V", "eat", "CGLIB$eat$0");}//这个方法就是调用目标类的的eat方法final void CGLIB$eat$0(){super.eat();}//这个方法是我们是我们要调用的,在前面的例子中调用代理对象的eat方法就会到这个方法中public final void eat(){//CGLIB$CALLBACK_0 = (MethodInterceptor)callback;CGLIB$CALLBACK_0;//这里就是判断CGLIB$CALLBACK_0是否为空,也就是我们传入的方法拦截器是否为空,如果不为空就最终到下面的_L4if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 37;goto _L3 _L4
_L3:break MISSING_BLOCK_LABEL_21;
_L4:break MISSING_BLOCK_LABEL_37;this;CGLIB$eat$0$Method;CGLIB$emptyArgs;CGLIB$eat$0$Proxy;//这里就是调用方法拦截器的intecept()方法intercept();return;super.eat();return;}//这里省略finalize,equals,toString,hashCode,clone,因为和上面的eat的两个方法差不多//..........//...........//..........public static MethodProxy CGLIB$findMethodProxy(Signature signature){String s = signature.toString();s;s.hashCode();JVM INSTR lookupswitch 6: default 140//                   -1574182249: 68//                   -1310345955: 80//                   -508378822: 92//                   1826985398: 104//                   1913648695: 116//                   1984935277: 128;goto _L1 _L2 _L3 _L4 _L5 _L6 _L7
_L2:"finalize()V";equals();JVM INSTR ifeq 141;goto _L8 _L9
_L9:break MISSING_BLOCK_LABEL_141;
_L8:return CGLIB$finalize$1$Proxy;
_L3:"eat()V";equals();JVM INSTR ifeq 141;goto _L10 _L11
_L11:break MISSING_BLOCK_LABEL_141;
_L10:return CGLIB$eat$0$Proxy;
_L4:"clone()Ljava/lang/Object;";equals();JVM INSTR ifeq 141;goto _L12 _L13
_L13:break MISSING_BLOCK_LABEL_141;
_L12:return CGLIB$clone$5$Proxy;
_L5:"equals(Ljava/lang/Object;)Z";equals();JVM INSTR ifeq 141;goto _L14 _L15
_L15:break MISSING_BLOCK_LABEL_141;
_L14:return CGLIB$equals$2$Proxy;
_L6:"toString()Ljava/lang/String;";equals();JVM INSTR ifeq 141;goto _L16 _L17
_L17:break MISSING_BLOCK_LABEL_141;
_L16:return CGLIB$toString$3$Proxy;
_L7:"hashCode()I";equals();JVM INSTR ifeq 141;goto _L18 _L19
_L19:break MISSING_BLOCK_LABEL_141;
_L18:return CGLIB$hashCode$4$Proxy;
_L1:JVM INSTR pop ;return null;}public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[]){CGLIB$THREAD_CALLBACKS.set(acallback);}public static void CGLIB$SET_STATIC_CALLBACKS(Callback acallback[]){CGLIB$STATIC_CALLBACKS = acallback;}private static final void CGLIB$BIND_CALLBACKS(Object obj){Dog$$EnhancerByCGLIB$$fbca2ec6 dog$$enhancerbycglib$$fbca2ec6 = (Dog$$EnhancerByCGLIB$$fbca2ec6)obj;if(dog$$enhancerbycglib$$fbca2ec6.CGLIB$BOUND) goto _L2; else goto _L1
_L1:Object obj1;dog$$enhancerbycglib$$fbca2ec6.CGLIB$BOUND = true;obj1 = CGLIB$THREAD_CALLBACKS.get();obj1;if(obj1 != null) goto _L4; else goto _L3
_L3:JVM INSTR pop ;CGLIB$STATIC_CALLBACKS;if(CGLIB$STATIC_CALLBACKS != null) goto _L4; else goto _L5
_L5:JVM INSTR pop ;goto _L2
_L4:(Callback[]);dog$$enhancerbycglib$$fbca2ec6;JVM INSTR swap ;0;JVM INSTR aaload ;(MethodInterceptor);CGLIB$CALLBACK_0;
_L2:}public Object newInstance(Callback acallback[]){CGLIB$SET_THREAD_CALLBACKS(acallback);CGLIB$SET_THREAD_CALLBACKS(null);return new Dog$$EnhancerByCGLIB$$fbca2ec6();}public Object newInstance(Callback callback){CGLIB$SET_THREAD_CALLBACKS(new Callback[] {callback});CGLIB$SET_THREAD_CALLBACKS(null);return new Dog$$EnhancerByCGLIB$$fbca2ec6();}public Object newInstance(Class aclass[], Object aobj[], Callback acallback[]){CGLIB$SET_THREAD_CALLBACKS(acallback);JVM INSTR new #2   <Class Dog$$EnhancerByCGLIB$$fbca2ec6>;JVM INSTR dup ;aclass;aclass.length;JVM INSTR tableswitch 0 0: default 35//                   0 28;goto _L1 _L2
_L2:JVM INSTR pop ;Dog$$EnhancerByCGLIB$$fbca2ec6();goto _L3
_L1:JVM INSTR pop ;throw new IllegalArgumentException("Constructor not found");
_L3:CGLIB$SET_THREAD_CALLBACKS(null);return;}public Callback getCallback(int i){CGLIB$BIND_CALLBACKS(this);this;i;JVM INSTR tableswitch 0 0: default 30//                   0 24;goto _L1 _L2
_L2:CGLIB$CALLBACK_0;goto _L3
_L1:JVM INSTR pop ;null;
_L3:return;}public void setCallback(int i, Callback callback){switch(i){case 0: // '\0'CGLIB$CALLBACK_0 = (MethodInterceptor)callback;break;}}public Callback[] getCallbacks(){CGLIB$BIND_CALLBACKS(this);this;return (new Callback[] {CGLIB$CALLBACK_0});}public void setCallbacks(Callback acallback[]){this;acallback;JVM INSTR dup2 ;0;JVM INSTR aaload ;(MethodInterceptor);CGLIB$CALLBACK_0;}}

根据上面的代码我们可以知道代理类中主要有几部分组成:

  1. 重写的父类方法,
  2. CGLIB$eat$0这种奇怪的方法,
  3. Interceptor()方法,
  4. newInstance和get/setCallback方法

3.FastClass机制分析

为什么要用这种机制呢?直接用反射多好啊,但是我们知道反射虽然很好用,但是和直接new对象相比,效率有点慢,于是就有了这种机制, Jdk动态代理的拦截对象是通过反射的机制来调用被拦截方法的,反射的效率比较低,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,下面用一个小例子来说明一下,这样比较直观:

public class test10 {public static void main(String[] args){Test tt = new Test();Test2 fc = new Test2();int index = fc.getIndex("f()V");fc.invoke(index, tt, null);}
}class Test{public void f(){System.out.println("f method");}public void g(){System.out.println("g method");}
}
class Test2{public Object invoke(int index, Object o, Object[] ol){Test t = (Test) o;switch(index){case 1:t.f();return null;case 2:t.g();return null;}return null;}public int getIndex(String signature){switch(signature.hashCode()){case 3078479:return 1;case 3108270:return 2;}return -1;}
}

上例中,Test2是Test的Fastclass,在Test2中有两个方法getIndex和invoke。在getIndex方法中对Test的每个方法建立索引,并根据入参(方法名+方法的描述符)来返回相应的索引。Invoke根据指定的索引,以ol为入参调用对象O的方法。这样就避免了反射调用,提高了效率。代理类(Target

EnhancerByCGLIBEnhancerByCGLIB

788444a0)中与生成Fastclass相关的代码如下:

Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
localClass2 = Class.forName("net.sf.cglib.test.Target");
CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");

MethodProxy中会对localClass1和localClass2进行分析并生成FastClass,然后再使用getIndex来获取方法g 和 CGLIB$g$0的索引,具体的生成过程将在后续进行介绍,这里介绍一个关键的内部类:

private static class FastClassInfo{FastClass f1; // net.sf.cglib.test.Target的fastclassFastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclassint i1; //方法g在f1中的索引int i2; //方法CGLIB$g$0在f2中的索引}

MethodProxy 中invokeSuper方法的代码如下:

 FastClassInfo fci = fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);

当调用invokeSuper方法时,实际上是调用代理类的CGLIB$g0方法,CGLIB0方法,CGLIB0CGLIBg$0直接调用了目标类的g方法。所以,在第一节示例代码中我们使用invokeSuper方法来调用被拦截的目标类方法。

4.简单原理

上面我们看了CGLib动态代理的用法、实际生成的代理类以及FastClass机制,下面我们就以最前面的那个例子中调用eat()方法来看看主要的调用步骤;

第一步:是经过一系列操作实例化出了Enhance对象,并设置了所需要的参数然后enhancer.create()成功创建出来了代理对象,这个就不多说了…

第二步:调用代理对象的eat()方法,会进入到方法拦截器的intercept()方法,在这个方法中会调用proxy.invokeSuper(obj, args);方法

第三步:invokeSuper中,通过FastClass机制调用目标类的方法

方法拦截器中只有一个invoke方法,这个方法有四个参数,obj表示代理对象,method表示目标类中的方法,args表示方法参数,proxy表示代理方法的MethodProxy对象
在这里插入图片描述
在这个方法内部会调用proxy.invokeSuper(obj, args)方法,我们进入.invokeSuper方法内部看看:

在这里插入图片描述

简单看看init()方法:

在这里插入图片描述
 FastClassInfo内部如下图,由此可以看出prxy.invokeSuper()方法中fci.f2.invoke(fci.i2, obj, args),其实就是调用CGLIBeateateat这个方法

在这里插入图片描述
 invoke方法是个抽象方法,我们反编译一下代理类的FastClass(也就是生成的那三个字节码文件名称最长的那个)就可以看到,由于代码比较长,就不复制了…
在这里插入图片描述

5.总结

CGLib动态代理是将继承用到了极致
  这里随便画一个简单的图看看整个过程,当我们去调用方法一的时候,在代理类中会先判断是否实现了方法拦截的接口,没实现的话直接调用目标类的方法一;如果实现了那就会被方法拦截器拦截,在方法拦截器中会对目标类中所有的方法建立索引,其实大概就是将每个方法的引用保存在数组中,我们就可以根据数组的下标直接调用方法,而不是用反射;索引建立完成之后,方法拦截器内部就会调用invoke方法(这个方法在生成的FastClass中实现),在invoke方法内就是调用CGLIB方 法 一 方法一方法一这种方法,也就是调用对应的目标类的方法一;

一般我们要添加自己的逻辑就是在方法拦截器那里。。。。

在这里插入图片描述

学习参考记录:
https://www.cnblogs.com/wyq1995/p/10945034.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql读取excel数据_Python 读取 Excel 数据,并插入到MySQL

说实话&#xff0c;个人不建议用Python来读取Excel进行入库操作&#xff0c;有条件的话&#xff0c;可以尝试用 ETL 工具&#xff0c;快速导入到MySQL中&#xff0c;或者也可使用 SQL 的导入工具进行。写本文的目的在于&#xff1a;记录一下之前做过一次这个同类型的东西。也为…

无向图的深度优先遍历非递归_图算法总结

&#xfeff;[TOC]图算法1、图的表示1.1、邻接矩阵&#xff08;有向图、无向图、带权图、代码实现&#xff09;1、无向图的邻接矩阵 2、有向图的邻接矩阵 3、带权值的图 有了上述的理解&#xff0c;我们可以设计数据结构&#xff0c;并实现了。C实现如下&#xff1a;#include1.…

华为ipd项目管理流程_IPD:一套卓越的产品开发经营体系

IPD全流程指的不仅仅是“产品开发流程”&#xff0c;而是如何实现产品“从机会到商业变现”的全流程。华为通过20年的全流程实践&#xff0c;才做到持续性推出高质量产品和解决方案。本期IPD全流程班&#xff0c;帮助企业研发管理者学习这套结构化流程&#xff0c;其中划下重点…

Springboot中@ComponentScan 注解

三个点&#xff1a; 1、工程中Application类的位置。默认情况下就不需要配置ComponentScan这个注解了。 因为Application类&#xff0c;在启动的时候&#xff0c;默认是加载和Application类所在同一个目录下的所有类&#xff0c;包括所有子目录下的类。所以一般情况下&#xff…

在react里写原生js_小程序原生开发与第三方框架选择

最近正在更新《微信小程序入门与实践》一书的第二版。书中有一章节谈到了”多样化的小程序开发“&#xff0c;摘取并加以整理分享给各位开发者。我一向不推荐也不提倡公众号阅读学习编程&#xff0c;文章更多的是列出小程序如今多样化的框架选择&#xff0c;并简单剖析它们之间…

springboot中service层注入的是实现类,但Controller层接收的是接口

我们习惯这样编写代码&#xff1a; 在service层写接口&#xff0c;然后用实现类去实现接口&#xff0c;并且将实现类注入到容器中 Service public class AccountServiceImpl implements AccountService {}在controller层却是用接口操作service的bean的方法&#xff0c; Auto…

mysql data transfer_MySQL主从同步加速 Transfer-- FAQ

Q: Transfer是什么A: 是一个解决MySQL原生主从同步延迟的方案。 Transfer本身是一个在MySQL源码上打的patch&#xff0c;可以用于当Slave&#xff0c;也可以用于当第三方工具&#xff0c;将Master的数据同步发给Slave。 利用多线程实现主从无延迟。Q: Transfer目前的发布形式&a…

springboot整合mybatisplus中@Mapper与@MapperScan的使用

一、Mapper与MapperScan不可同时使用 二、Mapper用于注解单个mapper接口 三、Mapper Scan用于批量注解Mapper接口 四、Mapper不起作用时&#xff0c;因为缺少下图中的依赖包 以上为自己整合框架中遇到的问题与解决方案&#xff0c;如果以上解决方案不能解决您的问题&#x…

html列表按时间排序代码_按字母顺序排序的列表

有时候你需要自制一份课件PPT或者手写笔记的关键词索引&#xff0c;用来开卷考试查阅PPT或者复习查阅笔记时能快速定位查阅内容位置&#xff0c;这时你可能想要一个按字母顺序排序的列表来帮助构建需要的索引。LaTeX自身的index本身并不支持这样的功能&#xff0c;修改起来也颇…

@MapperScan和@ComponentScan使用问题

报错如下 原因&#xff1a;当MapperScan和ComponentScan一起使用时&#xff0c;项目启动时扫描包会发生冲突&#xff0c;找不到swagger配置类的包和mapper接口的包 解决办法&#xff1a;MapperScan和ComponentScan可以一起使用。 改为MapperScan&#xff08;basePackages {}…

C语言中CY位什么时候才能为1_你真的了解C语言中的整型吗?

整型数据类型1. 整型数据类型在上一节当中&#xff0c;我们遇到了整型(integer)int&#xff0c;用来表示一个整数的数据类型。下面呢我们来讨论一下C语言里面的整数数据类型。看到这里你可能想问&#xff0c;一个整数而已&#xff0c;为什么会需要定义这么多的类型出来呢?要知…

Spring的两种动态代理:Jdk和Cglib 的区别和实现

Spring的两种动态代理&#xff1a;Jdk和Cglib 的区别和实现 一、原理区别&#xff1a; java动态代理是利用反射机制生成一个实现代理接口的匿名类&#xff0c;在调用具体方法前调用InvokeHandler来处理。 而cglib动态代理是利用asm开源包&#xff0c;对代理对象类的class文件…

red6.4 mysql_rhel6.4 yum本地yum源配置

rhel6.4 yum本地yum源配置 在安装rpm包时&#xff0c;总是会碰到关联的包&#xff0c;在安装过程中带来很多麻烦&#xff0c;配置yum源&#xff0c;自关联包安装&#xff0c;简单省事。配置步骤&#xff1a;1&#xff1a;mount -o loop 挂载镜像盘2&#xff1a;编辑rhel…

python怎么判断输入的是不是数字_如何在PYTHON里判断字符串是否为数字

1 打开JUPYTER NOTEBOOK&#xff0c;新建一个空白的PY文档。2 str1 "888" print(str1.isdecimal()) print(str1.isdigit()) print(str1.isnumeric()) 这是正常的整数&#xff0c;三种方法都是可以判断。3 str2 "二" print(str2.isdecimal()) print(str2.…

在springBoot中使用Mapper类问题_@Mapper_@MapperScan_xml文件跟mapper分开_xml文件跟mapper在同一个目录下

文章目录背景xml文件跟mapper分开application.properties方式一&#xff1a; 主程序中MapperScan方式二&#xff1a; 每个接口添加Mapper注意点xml文件跟mapper在同一个目录下SpringBoot中关于Mapper 和 Repository 的一些疑问1. Mapper2. Repository3. MapperScan(“com.xxx.x…

上海交大python答案_上海交通大学python期末考试样题加解析

上海交通大学python期末考试样题加解析 一、选择题: 将唯一正确的选项写在题前括号中.每题2 分. 【 】(1) 本课程的目标定位是什么? [A] 学习Python 语言 [B] 学习计算机的工作原理 [C] 学习各种算法 [D] 学习用计算机解决问题 【 】(2) 下列哪个标识符是合法的? [A] var-nam…

hbase1.1.1 连接集群_Hadoop2.7.1+Hbase1.1.2集群环境搭建(10) hadoop hbase kerberos

1.hadoop安全机制历史在Hadoop1.0.0或者CDH3 版本之前&#xff0c; hadoop并不存在安全认证一说。默认集群内所有的节点都是可靠的&#xff0c;值得信赖的。用户与HDFS或者M/R进行交互时并不需要进行验证。导致存在恶意用户伪装成真正的用户或者服务器入侵到hadoop集群上&#…

SSM框架中mapper和mapping.xml文件在同一个包下需要的配置

前言 当我们在开发过程中&#xff0c;由于maven项目本身的限制&#xff0c;我们不能直接把我们的mapper.xml文件和对应mapper.java接口文件放到一起&#xff0c;也就是不能直接放在java包中&#xff0c;如图&#xff1a; 因为maven项目在构建打包时&#xff0c;会从src/main…

mysql配置文件简易代码_MySQL配置文件my.cnf 例子最详细翻译

转的MySQL配置文件my.cnf 例子最详细翻译,可以保存做笔记用。#BEGIN CONFIG INFO#DESCR: 4GB RAM, 只使用InnoDB, ACID, 少量的连接, 队列负载大#TYPE: SYSTEM#END CONFIG INFO## 此mysql配置文件例子针对4G内存。# 主要使用INNODB#处理复杂队列并且连接数量较少的mysql服务器#…

Spring+Mybatis使用MapperScannerConfigurer简化配置__MapperScannerConfigurer的作用

SpringMybatis使用MapperScannerConfigurer简化配置 MyBatis的一大亮点就是可以不用DAO层的实现类&#xff0c;而我们知道service层调dao层 如果没有实现类&#xff0c;Spring如何为Service注入DAO的实例呢&#xff1f; 比如 现在有一个dao层的bookmapper和配置mybatis的xm…