CGLIB原理简析-前篇

news2025/2/25 13:52:12

CGLIB(Code Generation Library)是实现动态代理的一种方案。动态代理的内容一般都包含三个部分:① 代理类的生成;② 代理类的实例化;③ 代理类的使用。 本文作为CGLIB文章的前篇,将通过与使用者直接相关入手,先介绍代理类使用相关底层逻辑。即,当我们调用代理对象的对应方法时如何实现代理的。关于前两个部分将会在后篇进行介绍。

一、简单使用

开篇第一章,先回顾下CGLIB动态代理的简单使用,代码如下:
① 被代理类

public class Student {
    public String name;

    public Student() {
        this.name = "spl";
    }

    public void outStudentName() {
        System.out.println(this.name);
    }
}

② 方法拦截器

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("代理前置....");
        System.out.println("第一个参数:obj = " + obj.getClass().getSimpleName());
        System.out.println("第二个参数:method = " + method.getName());
//        System.out.println("第三个参数:args = " + args);
        System.out.println("第三个参数:proxy = " + proxy.getSignature());
        Object invokeResult = proxy.invokeSuper(obj, args);
        System.out.println("代理后置....");
        return invokeResult;
    }
}

③ 使用cglib代理

public class Client {
    public static void main(String[] args) {
        System.setProperty ( DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/spl/own/mavenTest" );

        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(myMethodInterceptor);
        Student stu = (Student) enhancer.create();
        stu.outStudentName();
    }
}

执行结果:
在这里插入图片描述
最上面的红字表示我已经开启了保存动态类信息功能,便于后续分析代理功能的实现逻辑。下面输出了多行信息表明动态代理已生效。

二、增强代理类结构

配置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY属性,cglib会将动态生成的类保存在指定目录下。
在这里插入图片描述
可以看到,动态生成代理类文件路径在指定目录下的代理类同路径下,即${DebuggingClassWriter.DEBUG_LOCATION_PROPERTY}/{被代理类包路径}.

net.sf.cglib包下的动态代理类先不介绍,先看Strudent$$XXX动态代理相关类。

  • Student$$EnhancerByCGLIB$86327ae0
    生成并写入文件的增强代理类。
  • Student$$EnhancerByCGLIB$2838b21a$FastClassByCGLIB$594c36f
    前缀和增强代理类相同,后面是FastClass类的标志,因此该文件是调用代理方法后生成的代理类的FastClass子类。
    FastClass是为了避免反射获取类方法而设计的,内部维护索引到各个方法的映射,加速了CGLIB代理方法的执行速度。
  • Student$$FastClassByCGLIB$53ab8f74
    如上规律,该文件时调用代理方法后生成的被代理类的FastClass子类。功能如上。

    FastClass子类及其对象都是在调用对应代理方法时才会被创建、实例化。这部分后续文中会提到。

2.1 代理类类间关系

public class Student$$EnhancerByCGLIB$$2838b21a 
extends Student 
implements Factory {
...
}

代理类直接继承了被代理类,而且实现了Factory接口。所有被Enhancer类返回的增强代理类均会实现Factory接口。Factory接口提供了一系列创建新增强代理对象的newInstance()方法,且支持替换之前指定的Callbacks列表(什么是Callback?简单理解为方法回调对象,如上文示例中MyMethodInterceptor实现的MethodInterceptor接口就继承了Callbacks接口)。

【TODO】cglib能够代理抽象类、接口吗?不能。

2.2 增强代理类属性

增强代理类的属性如下:

  • 2个对象属性(非静态)
    • private boolean CGLIB$BOUND;
      标识当前增强代理对象是否已绑定回调对象(Callback)。增强代理类支持修改回调对象,且具备有限线程上下文中的回调对象,因此当调用任何公开方法时,都会根据此表示判断回调对象是否已绑定在下面这个回调对象属性上。
    • private MethodInterceptor CGLIB$CALLBACK_{index};
      回调对象。允许有多个,这个属性指向了第index个回调对象。
  • 5个静态类成员属性
    • public static Object CGLIB$FACTORY_DATA;
      用途暂不清楚【TODO】
    • private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
      线程级缓存回调对象列表,优先级高于全局
    • private static final Callback[] CGLIB$STATIC_CALLBACKS;
      通过内部公开Set方法设置全局回调对象列表
    • private static Object CGLIB$CALLBACK_FILTER;
      用途暂不清楚【TODO】
    • private static final Object[] CGLIB$emptyArgs;
      当方法参数为空时,会传递该空数组给回调方法(intecept)上。
  • (n+4)组类方法相关句柄
    • n为被代理对象方法个数,每组包含的方法如下:
      private static final Method CGLIB$outStudentName$0$Method;           // 被代理方法句柄
      private static final MethodProxy CGLIB$outStudentName$0$Proxy;		// 代理方法句柄
      
      被代理方法对象以及代理方法对象会作为第二、第四个参数传给回调函数的intercep(…)方法。
    • cglib会为每一个需要被代理的方法创建一组方法句柄。除了被代理类方法之外,还有Object类的equals()、toString()、hashCode()以及clone()也被代理。

2.3 增强代理类静态初始化

JVM加载代理类时会初始化所有的被代理方法对象(Method)和代理方法对象(MethodProxy)。
被代理方法Method对象的获取是通过反射的方式获取,而代理方法的获取是创建MethodProxy对象,MethodProxy创建时会持有被代理类Class对象、代理类Class对象、被代理方法名、代理方法名、以及方法描述(由参数类型即返回值组成)。
在具体调用方法拦截器的intercept方法入参中Method就是这里的被代理方法、MethodProxy就是代理方法对象。MethodProxy在整个CGLIB代理过程中作用至关重要,具体有关MethodProxy细节在后续第三章内容。

2.4 增强代理类构造函数

public Student$$EnhancerByCGLIB$$86327ae0() {
    CGLIB$BIND_CALLBACKS(this);
}

代理类仅提供了一个无参构造方法,该方法内部仅做了一件事情-给代理类对象绑定回调方法。绑定回调方法的过程:

  1. 根据属性CGLIB$BOUND,如果已经绑定,就不再绑定了。否则2;【绑定回调方法会在多处用到,因此这里专门使用一个属性标识当前对象是否已绑定】
  2. 设置属性CGLIB$BOUND为true,标识当前对象已绑定
  3. 优先通过当前静态线程上下文(CGLIB$THREAD_CALLBACKS)中获取回调对象,如果不存在则4,存在则5;
  4. 线程上下文不存在,就从当前对象的CGLIB$STATIC_CALLBACKS静态属性获取,如果不存在返回,存在则5;
  5. 将获取的多个回调对象(可能有多个)设置到**CGLIB$CALLBACK_{index}**属性中。

构造方法还有一件事情,就是调用父类的构造方法。如果被代理对象时有参构造,代理对象也是有参构造,并会显示调用super(…)方法

静态初始化+构造方法基本上是将代理类所必须的属性全部设置完。其中静态初始化的属性均为所有对象确定且一致的,而构造函数依赖于运行时可变静态属性。

2.5 增强代理类公开方法

代理类的公开方法在这里会被分为三个部分,分别是父类引用方法、接口引用方法、内部公开方法。我先说下为什么这么分:既然是代理类,那么该类的类名实际上肯定是运行时动态确定的,因此在实际程序代码中不太可能使用代理类型变量,因此代理类对象肯定是通过多态进行调用
根据代理类的类间关系,代理类对象要么被其父类(即被代理类)引用,要么被其实现的接口(Factory)引用。除此之外,代理类还有几个内部静态公开方法,这个是给内部使用的,上层应用程序中使用不到。

2.5.1 代理类重写父类方法执行过程

前面说到增强代理类代理的方法有(n+4)个,其中n为被代理类的方法,4为Object类中的方法。这些方法在代理类中会被重写,代理对象调用时的逻辑可总结图如下:
在这里插入图片描述
流程还是十分简单清楚的,有回调对象就直接调用其intercept()方法,具体后续交给每一个MethodInteceptor对象处理。反之,如果没有回调对象,那就只能调用父类方法了(即,无代理动作)。
这里说一下调用MethodInteceptor#intercept()方法的入参,为后续做好铺垫。几乎所有重写方法的内部逻辑差异不大,这里以Object#equals方法为例:

Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);

var10000就是MethodInteceptor回调对象,intercept方法的第一个参数就是当前代理类对象,第二个参数被代理方法(Method),第三个参数是方法入参,第四个参数是代理方法(MethodProxy)。如果方法没有任何入参,第三个参数会传入CGLIB$emptyArgs空Object数组。

2.5.2 Factory方法

前面已经提到所有创建的增强代理类都会实现Factory接口,Factory接口提供了两个能力:

  1. 重新创建相同的代理对象
    • Object newInstance(Callback callback);
      无参创建对象并传入一个回调函数
    • Object newInstance(Callback[] callbacks);
      无参创建对象并传入多个回调函数
    • Object newInstance(Class[] types, Object[] args, Callback[] callbacks);
      有参创建多个对象并传入多个回调函数
      实际上这三种方法差异不大,均是调用对应代理类的构造方法,设置回调函数方法依靠CGLIB$THREAD_CALLBACKS线程上下文属性。但需要注意的是,回调对象的个数不能比之前少,多余部分会忽略。
  2. 设置或获取回调对象
    • Callback[] getCallbacks();
      返回所有回调对象列表
    • Callback getCallback(int index);
      获取第index个回调对象,越界返回null
    • void setCallback(int index, Callback callback);
      替换第index个回调对象;注意不能新增回调对象个数
    • void setCallbacks(Callback[] callbacks);
      替换所有的回调对象;注意回调对象个数和之前保持一致。

    这里使用setXXX方法不好,其实内部逻辑实际上是replaceXXX。

2.5.3 内部静态公开方法

增强代理类除了以上公开方法,还有三个内部使用的静态方法:

  1. MethodProxy CGLIB$findMethodProxy(Signature var0)
    根据方法签名获取代理方法。其中方法签名包括方法名+方法描述(参数类型、返回类型)
  2. void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0)
    设置回调对象列表至线程上下文中
  3. void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0)
    设置回调对象列表

三、代理方法

前面已经将代理类和代理对象的内容分析的十分清楚了,代理对象会重写父类(也即,代理类)的方法,而在实际调用中会将代理类对象、被代理方法Method对象、方法参数、代理方法MethodProxy对象传给MethodInterceptor对象。这其中MethodProxy我们还是比较陌生的,本章节将会一步步拆解其内部逻辑。

3.1 方法代理属性

public class MethodProxy {
    private Signature sig1;
    private Signature sig2;
    private CreateInfo createInfo;
    
    private final Object initLock = new Object();
    private volatile FastClassInfo fastClassInfo;
}

MethodProxy共有5个属性:

  • sig1、sig2
    均表示方法签名,其中sig1为被代理方法签名,sig2为代理方法签名。方法签名由方法名、入参类型、返回类型构建为Signature 对象。
  • createInfo
    由被代理类Class对象、代理类Class对象构建的CreateInfo实例(CreateInfo为MethodProxy私有内部类)。该实例创建出来就是为了后续初始化FastClassInfo对象属性的。
  • initLock
    MethodProxy对象初始化FastClassInfo对象通过Double Check Lock实现。这里创建一个空Object对象是为了加锁。
  • fastClassInfo
    该对象会在调用MethodProxy公开方法时根据createInfo进行初始化。FastClassInfo也是MethodProxy私有内部类,该对象存储了代理类的FastClass对象、被代理类的FastClass对象以及各自当前方法的index值。
    FastClass对象时Cglib在查找类方法时的效率优化机制,相比于JDK动态代理通过反射的方式,Cglib通过FastClass和方法index能够快速找到对应的方法。在第一章节,我们可以注意到在cglib生成类包下除了增强代理类,还有另外两个类就分别是被代理类和代理类的FastClass类文件。有关于FastClass后续会有文章详细讲解。

3.2 方法代理实例化

MethodProxy的构造函数为私有,仅提供一个公开静态方法用于内部创建方法代理实例。

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

MethodProxy 实例化方法中就是创建代理类及被代理类对应方法的签名和CreateInfo实例。
当前除了创建对象的初始化逻辑之外,在调用MethodProxy对象的任何公开方法时,都会调用其init()方法去初始化FastClassInfo对象,便于之后的方法索引获取。

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;
                createInfo = null;
            }
        }
    }
}

可以看到这里通过双重检查锁的方式来创建FastClassInfo对象。FastClassInfo对象的创建过程会创建并存储被代理类、代理类的FastClass对象(f1、f2),也会存储当前代理方法在被代理类、代理类FastClass对象的索引(i1、i2)便于后续快速查找方法。
FastClass对象的创建逻辑在helper方法中,此处暂时先不展开介绍,在后续文章介绍FastClass自然就理解了。值得提醒的是,代理类、非代理类的FastClass类只有在此处才会被创建,意即只有在调用MethodProxy对象公开方法才会创建对应的FastClass类。这就回答了一个问题,为什么FastClassInfo对象不是在create方法中直接创建好,而是延迟使用DCL创建呢?因为并非是所有代理类都会被调用,如果大量的FastClass创建会减慢编译速度,增加java程序的编译时间。

看过我之前单例模式文章,在讲解DCL机制的时候提到过,以上这种使用方式可能存在问题-在高并发下,有可能会使用不成熟的FastClassInfo对象,产生空指针。
解决办法:在第一重判断条件上,增加与逻辑(或直接替换):createInfo != null;

3.2 方法代理公开方法

MethodProxy共提供了5个公开方法(其中1个是静态方法):

  1. MethodProxy find(Class type, Signature sig)
    静态方法,用于获取代理类匹配的某个签名方法。其中,type参数必须是代理类Class对象。
    内部逻辑上实际调用的是代理类内部公开的CGLIB$findMethodProxy方法,可以和之前回顾下。
  2. Signature getSignature()
    返回代理方法的签名(方法名+入参类型+返回值类型)。
  3. String getSuperName()
    返回实际被代理方法的方法名。
  4. int getSuperIndex()
    返回实际被代理方法在其FastClass对象的index值
  5. Object invoke(Object obj, Object[] args)
    通过被代理类的FastClass对象调用被代理对象obj的被代理方法。注意,这里的obj不能为代理对象,代理对象调用被代理方法(代理对象重写)就又会被拦截,就会导致死循环直到栈溢出结束。
  6. Object invokeSuper(Object obj, Object[] args)
    通过代理类的FastClass对象调用代理对象的代理方法。注意,这里的obj不能为被代理对象,被代理对象没有代理方法,也会出现强转代理类型失败。

invoke是调用被代理对象的被代理方法,而invokeSuper则是调用增强代理对象的代理方法。
疑问:invoke似乎用处很小,一般都是使用invokeSuper。
下面以invokeSuper为例,梳理下具体调用过程:

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        init();			// 例行初始化FastClassInfo(创建被代理类、代理类FastClass)
        FastClassInfo fci = fastClassInfo;
        /**
        * 调用代理类的FastClass对象的invoke方法
        * 第一个参数为代理方法的索引ID,第二个参数为代理对象,第三个参数方法参数
        * 具体执行:就是根据索引ID找到方法,然后根据obj、args调用方法
        **/
        return fci.f2.invoke(fci.i2, obj, args);	
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}

在第二个章节开头,其中Student$$EnhancerByCGLIB$2838b21a$FastClassByCGLIB$594c36f就是为代理类生成的FastClass子类文件。FastClass就是为了加速检索类方法,这里不再赘述。其invoke方法逻辑为:

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        86327ae0 var10000 = (86327ae0)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return new Boolean(var10000.equals(var3[0]));
            case 1:
                return var10000.toString();
            case 2:
                return new Integer(var10000.hashCode());
            case 3:
                return var10000.clone();
           ...
            case 17:
                return var10000.CGLIB$clone$4();
            case 18:
                var10000.CGLIB$outStudentName$0();
                return null;
            case 19:
                return 86327ae0.CGLIB$findMethodProxy((Signature)var3[0]);
            case 20:
                86327ae0.CGLIB$STATICHOOK1();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

当被代理方法很多时,这个方法代码会很长,但是理解起来其实十分简单,就是根据第一个参数index值,来执行不同的方法。代理类所有的方法都对应了一个索引,而在MethodProxy#invokeSuper方法的索引值一定是代理方法(以CGLIB开头)对应的index。代理方法在代理类中的权限为默认权限,且其内部逻辑仅是调用父类的对应方法,没有调用回调对象的拦截方法。下面给出equals()方法的代理方法示例:

final boolean CGLIB$equals$1(Object var1) {
   return super.equals(var1);
}

四、总结

回到最初的问题,当我们调用代理对象的对应方法时如何实现代理的?
我们先回顾下JDK动态代理。JDK动态代理也是会生成并实例化一个代理类,该代理类继承的父类为Proxy类,且实现了接口下的所有方法。同样的,因为是运行时生成的代理类,代理对象一定是通过接口引用的多态对象存在。当代理对象调用任何接口方法时,其内部逻辑都会调用InvocationHandler对象(仅一个)的invoke方法进行调用。因此,使用者可以自定义InvocationHandler对象,在invoke方法中实现对指定对象的代理。
但CGLIB却有所不同。CGLIB也会生成并实例化一个代理类,该代理类直接继承被代理类,且会重写所有被代理类的方法。代理类的的重写方法内部逻辑是如果有回调对象,就将方法的执行逻辑托给回调对象,该回调对象就是使用者指定的MethodInterceptor对象,使用者可以在intercept方法中实现方法逻辑的增减修改。
两者有所不同,实际上JDK动态代理类完全就是一个新类,从代理关系上来看,代理对象代理了InvocationHandler对象,InvocationHandler对象代理了需要被代理的对象(当然,这一步依赖于用户的具体实现逻辑)。而CGLIB增强类实际上是被代理类的子类,利用重写方法的机制实现方法逻辑的增强,并提供回调对象供使用者自定义增强逻辑。因此,CGLIB生成的类应该算是动态类型增强,好过理解为代理。

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

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

相关文章

Java AWT Swing(图形化界面编程)(一)

目录 1.简介 2.Java中的图像化界面----Awt与Swing 一、AWT编程 1.简介 2.AWT的继承体系 3.container容器 3.1container继承体系 3.2.常见API 3.3容器演示一 3.4容器演示二 3.5容器演示三 1.简介: 通常情况下,java语言一般是用来开发后台程序的&#xff0…

mysql数据库第十二课------mysql语句的拔高2------飞高高

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…

图论——最短路算法

引入&#xff1a; 如上图&#xff0c;已知图G。 问节点1到节点3的最短距离。 可心算而出为d[1,2]d[2,3]112,比d[1,3]要小。 求最短路径算法&#xff1a; 1.Floyd(弗洛伊德) 是一种基于三角形不等式的多源最短路径算法。边权可以为负数 表现为a[i,j]a[j,k]<a[i,k]。 …

【好好练习ACM-DAY1】

网站主要参照洛谷 牛客 力扣 cf等 题单列表 - 洛谷 题单链接 今天的刷题内容如上 在进入专题学习之前 先看补充知识 数组能开多大 C/C数组的大小最大能有多大&#xff1f;_c数组大小_JoannaJuanCV的博客-CSDN博客 全局&#xff1a;int 2000*2000 6个0可 局部&#xf…

Ubuntu删除大量磁盘文件时,内存cache降free升而创造的漂亮曲线

文章目录 前言图形本尊产生的原因总结 前言 最近在做压力测试&#xff0c;对磁盘、CPU、内存等指标比较敏感&#xff0c;因为产生了大量的日志文件需要定期删除&#xff0c;于是写了个定时清理磁盘的脚本&#xff0c;今天早上发现内存曲线居然产生了一个漂亮的图形&#xff0c…

递归、搜索与回溯算法

一.递归 &#xff08;1&#xff09;汉诺塔问题 当n2时&#xff0c;要将A中最下面盘子上方的盘子放到B上&#xff0c;最下面盘子放到C上&#xff0c;再将B上的盘子通过A放到C即可&#xff1b; 当n3时&#xff0c;要将A中最下面盘子上方的盘子放到B上&#xff0c;最下面盘子放到…

父进程等待子进程退出 / 僵尸进程孤儿进程

Q&#xff1a;父进程为什么要等待子进程退出&#xff1f; A&#xff1a;回顾创建子进程的目的&#xff0c;就是让子进程去处理一些事情&#xff0c;那么“事情干完了没有”这件事&#xff0c;父进程需要知道并收集子进程的退出状态。子进程的退出状态如果不被收集&#xff0c;…

系统架构设计专业技能 · 系统安全分析与设计(四)【加解密、数字信封、信息摘要、数字签名、数字书证、网络安全、信息安全】

系列文章目录 系统架构设计专业技能 网络规划与设计&#xff08;三&#xff09;【系统架构设计师】 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师…

SQL 语句解析过程详解

SQL 语句解析过程详解&#xff1a; 1&#xff0e;输入SQL语句 2&#xff0e;词法分析------flex 使用词法分析器&#xff08;由Flex生成&#xff09;将 SQL 语句分解为一个个单词&#xff0c;这些单词被称为“标记“。标记包括关键字、标识符、运算符、分隔符等。 2.1 flex 原…

CCLINK转MODBUS-TCP网关cclink通讯接线图 终端电阻

大家好&#xff0c;今天我们要聊的是生产管理系统中的CCLINK和MODBUS-TCP协议&#xff0c;它们的不同使得数据互通比较困难&#xff0c;但捷米JM-CCLK-TCP网关的出现改变了这一切。 1捷米JM-CCLK-TCP是一款自主研发的CCLINK从站功能的通讯网关&#xff0c;它的主要功能是将各种…

JavaWeb 中对 HTTP 协议的学习

HTTP1 Web概述1.1 Web和JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器 1.3 Web核心 2 HTTP2.1 简介2.2 请求数据格式2.2.1 格式介绍2.2.2 实例演示 2.3 响应数据格式2.3.1 格式介绍2.3.2 响应状态码2.3.…

linux中的ifconfig和ip addr

在linux操作系统中ifconfig和ip addr都是显示网卡配置信息的命令&#xff0c;好多人有疑惑它们有什么区别呢 区别1&#xff1a;对于linux发行的版本不一样 ip addr是对新发行版本的linux使用会比较多&#xff1b;而ifconfig是老版本遇到使用的会比较多。 区别2&#xff1a;显…

mysql8和mysql5的安装过程都有!!!超多图超详细保姆级教程最新教程新手小白轻松上手,带你了解清楚你安装过程的每一个术语

目录 前言mysql5和mysql8的区别1.官网下载2.mysql8的安装2.1安装程序打开前2.2Choosing a Setup Type选择安装模式2.3Select Products选择组件2.3.1Select Products的组件解释2.3.2Select Products的组件选择2.3.3电脑操作系统位数查看2.3.4Select Products的组件的内容配置2.3…

锐捷设备密码管理、密码恢复、恢复出厂设置

目录 配置登录用户名密码以及Enable密码 只需要密码登录 需要用户名和密码登录&#xff08;无AAA&#xff09; 需要用户名和密码登录&#xff08;有AAA&#xff09; 密码恢复 Web密码忘记 Telnet/SSH密码忘记 Console密码忘记 所有密码都忘记&#xff0c;通过Console进…

微信小程序在使用vant组件库时构建npm报错

在跟着vant官方进行使用步骤一步步操作时&#xff0c;由于要构建NPM&#xff0c;但NPM包在App配置文件的外部 所以在做下图这一步时&#xff1a; 接着再进行npm构建时会报错 message:发生错误 Error: F:\前端学习\前端框架\小程序\project\demo\miniprogram解决方法 &#xf…

Offset Explorer

Offset Explorer 简介下载安装 简介 Offset Explorer&#xff08;以前称为Kafka Tool&#xff09;是一个用于管理和使Apache Kafka 集群的GUI应用程序。它提供了一个直观的UI&#xff0c;允许人们快速查看Kafka集群中的对象以及存储在集群主题中的消息。它包含面向开发人员和管…

1、深入理解Mysql索引底层数据结构与算法(前置课程)

1.表中数据看似挨在一起&#xff0c;本质写在磁盘里&#xff0c;随着时间和其他程序占用可能分散分布 k为值 v为磁盘空间地址 2.为什么mysql选择B树 假如b树存放2k万数据 一个节点16KB&#xff0c;里面带有data的话存的太少&#xff0c;导致树的高度过高&#xff0c;所以让…

配置:Terminal和oh-my-posh

目录 命令行安装oh-my-posh查看安装情况配置PowerShell启用oh-my-posh、设置主题配色安装字体Terminal中的配置 命令行安装oh-my-posh Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString(https://ohmy…

Java四大查找(二)插值查找

Java四大查找 一. 插值查找1.1 题目1.2 思路分析&#xff08;图解加文字&#xff09;1.3 代码演示 一. 插值查找 1.1 题目 给定一个有序数组&#xff0c;在有序数组中找到该数字。如果有&#xff0c;则返回该数字的下标&#xff0c;如果没有&#xff0c;则返回-1. 1.2 思路分析…

模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

问题如下 当时我以为引脚配错了&#xff0c;原理图明明是B引脚&#xff0c;为何程序是C呢 查了一下资料&#xff0c;顿悟了 https://blog.csdn.net/m0_62243928/article/details/125779308 在使用模拟IIC的时候&#xff0c;观看别人的程序的时候发现了程序之间的一些不一…