cglib动态代理实现原理详细分析

2023-11-04

在之前Java代理模式中大致的分析了下代理模式的类型及对每种代理类型简单的举例了下。在上篇JDK动态代理实现原理详细分析中,对其JDK代理的流程做了一个详细的分析。而本文,将介绍另一种动态代理模式:cglib动态代理。阅读完本文,你将对cglib代理模式的运行的流程有一个清晰的认识。

本文的目录如下:

目录

一:cglib动态代理的样例展示

二:cglib生成的代理类的分析

三:cglib运行流程详细的分析(结合源码)

四:小结


一:cglib动态代理的样例展示

public class MyCglib implements MethodInterceptor {

    public Object getInstance(Object object){
        //创建增强类
        Enhancer enhancer = new Enhancer();
        //设置生成的代理类的父类为传入的对象类
        enhancer.setSuperclass(object.getClass());
        //设置方法的拦截回调为当前对象(方法拦截的时候会调用intercept方法)
        enhancer.setCallback(this);
        //返回创建的动态代理对象(Enhancer有静态方法create可以直接调用)
        return enhancer.create();
    }


    /**
     * @param subProxy      生成的代理对象  该类是目标类的子类
     * @param method        原目标方法
     * @param objects       方法的参数
     * @param methodProxy   生成的代理方法
     */
    public Object intercept(Object subProxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里执行的逻辑将在下文详细分析
        return methodProxy.invokeSuper(subProxy,objects);
    }

}

目标类:

public class ZhangSan {
    public String say(){
        return "说中文";
    }
}

测试类:

public class CglibTest {
    public static void main(String[] args) throws IOException {
        ZhangSan instance = (ZhangSan) new MyCglib().getInstance(new ZhangSan());
        instance.say();
    }
    static{
        //用于生成cglib字节码文件,用于下文分析,生成文件的保存路径为:D:\Tools\Study
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Tools\\Study");
    }
}

在测试类中,有一个cglib生成的代理对象instance,代理对象调用了say方法,而最终执行的却是目标类的say方法。要想弄清这个问题,我们看看生成的代理对象instance到底是什么结构。

二:cglib生成的代理类的分析

 通过System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Tools\\Study"),我们可以在对应的目录下生成代理类的class文件。class文件反编译之后的结果为:

public class ZhangSan$$EnhancerByCGLIB$$c4d2de63 extends ZhangSan 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$say$0$Method;      //重写的目标方法
  private static final MethodProxy CGLIB$say$0$Proxy;  //生成的代理方法
  
  private static final Object[] CGLIB$emptyArgs;       //方法参数
  
  //父类中的方法都会在这里有一个重写的方法以及代理生成的方法
  private static final Method CGLIB$equals$1$Method;
  private static final MethodProxy CGLIB$equals$1$Proxy;
  
  private static final Method CGLIB$toString$2$Method;
  private static final MethodProxy CGLIB$toString$2$Proxy;
  
  private static final Method CGLIB$hashCode$3$Method;
  private static final MethodProxy CGLIB$hashCode$3$Proxy;
  
  private static final Method CGLIB$clone$4$Method;
  private static final MethodProxy CGLIB$clone$4$Proxy;

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
	
    Class localClass1 = Class.forName("com.sg.spring05.cglib.ZhangSan$$EnhancerByCGLIB$$c4d2de63");
    Class localClass2;
	
    Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    
    CGLIB$equals$1$Method = tmp83_80[0];
    CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    
    Method[] tmp103_83 = tmp83_80;
    CGLIB$toString$2$Method = tmp103_83[1];
    CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    
    Method[] tmp123_103 = tmp103_83;
    CGLIB$hashCode$3$Method = tmp123_103[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
    
    Method[] tmp143_123 = tmp123_103;
    CGLIB$clone$4$Method = tmp143_123[3];
    CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    tmp143_123;
	
    //具体的设置重写的方法以及代理的方法
    Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "say", "()Ljava/lang/String;" }, (localClass2 = Class.forName("com.sg.spring05.cglib.ZhangSan")).getDeclaredMethods());
    CGLIB$say$0$Method = tmp191_188[0];
    CGLIB$say$0$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "say", "CGLIB$say$0");
    tmp191_188;
  }

  //代理生成的方法  直接调用的父类(目标类的方法)
  final String CGLIB$say$0(){
    return super.say();
  }

  //方法重写 测试样例中就是调用的这里的say方法
  public final String say()
  {
	//判断目标类是否有设置回调:enhancer.setCallback(this);
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null){
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
	//设置了方法的回调则调用拦截器方法intercept
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    return super.say();
  }

  .......省略了equals,toString,hashCode,clone方法.........
  

  public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
  {
    String tmp4_1 = paramSignature.toString();
    switch (tmp4_1.hashCode())
    {
    case -1257330122:
      if (tmp4_1.equals("say()Ljava/lang/String;"))
        return CGLIB$say$0$Proxy;
    case -508378822:
    case 1826985398:
    case 1913648695:
    case 1984935277:
    }
  }

  public ZhangSan$$EnhancerByCGLIB$$c4d2de63()
  {
    CGLIB$BIND_CALLBACKS(this);
  }

  public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
  }

  public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
  }

  ..........
  public Object newInstance(Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new c4d2de63();
  }

  public Object newInstance(Callback paramCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new c4d2de63();
  }

  public Object newInstance(Class[] paramArrayOfClass, Object[] paramArrayOfObject, Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    Class[] tmp9_8 = paramArrayOfClass;
    switch (tmp9_8.length)
    {
    case 0:
      tmp9_8;
      break;
    default:
      new c4d2de63();
      throw new IllegalArgumentException("Constructor not found");
    }
    CGLIB$SET_THREAD_CALLBACKS(null);
  }

  public Callback getCallback(int paramInt)
  {
    CGLIB$BIND_CALLBACKS(this);
    switch (paramInt)
    {
    case 0:
      break;
    }
    return null;
  }

  public void setCallback(int paramInt, Callback paramCallback)
  {
    switch (paramInt)
    {
    case 0:
      this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
      break;
    }
  }

  public Callback[] getCallbacks()
  {
    CGLIB$BIND_CALLBACKS(this);
    return new Callback[] { this.CGLIB$CALLBACK_0 };
  }

  public void setCallbacks(Callback[] paramArrayOfCallback)
  {
    this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
  }

  static
  {
    CGLIB$STATICHOOK1();
  }
}

在生成的代理类(ZhangSan$$EnhancerByCGLIB$$c4d2de63)中我们可以看到它继承与目标类(ZhangSan)。在生成的代理类中,对于父类中每一个能够继承重写的方法,动态代理类都会生成两个相应的方法。一个是直接重写父类的方法,一个是生成的对应的动态代理的方法。在调用时,会直接先调用重写的方法。

三:cglib运行流程详细的分析(结合源码)

分析cglib动态代理的运行流程,我们从测试类中的调用方法开始入手:

ZhangSan instance = (ZhangSan) new MyCglib().getInstance(new ZhangSan());
instance.say();

即instance.say()为入口切入。这里的instance为生成的代理类对象,调用say方法的时候,实际上是调用的为代理类重写的父类的方法。即:

  public final String say()
  {
    //判断目标类是否有设置回调:enhancer.setCallback(this);
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null){
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    //设置了方法的回调则调用拦截器方法intercept
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    return super.say();
  }

首先判断是否目标类对象是否设置了回调的方法,即设置了enhancer.setCallback(this);如果不为空,则执行:

(String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);

直接执行生成代理类的intercept方法,参数为:this(当前代理对象),CGLIB$say$0$Method(目标类中的方法),CGLIB$emptyArgs(方法参数,这里为空),CGLIB$say$0$Proxy(代理类生成的代理方法)。即这里会调用到样例中MyCglib.intercept方法。代码如下:

 public Object intercept(Object subProxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(subProxy,objects);
    }

我们接下来看看在invokeSuper中做了一些什么。

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            //初始化生成一个FastClassInfo
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //执行相应的FastClass的方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

生成一个FastClassInfo和执行其对应的方法。那么FastClassInfo又是什么?其源码如下:

private static class FastClassInfo {
        FastClass f1;   //目标类
        FastClass f2;   //代理类
        int i1;     //目标类要执行方法的下标
        int i2;     //代理类要执行方法的下标

        private FastClassInfo() {
        }
}

其中f1为对应的目标类,f2为生成的代理类,i1为目标类(f1)中实际要执行的方法,i2为代理类(f2)中实际要执行的方法。这样的好处是,类与方法直接有一个下标的映射关系,通过对应的下标,可以直接调用方法而不是通过反射的方法。而反射则会更加的耗时。对应详细的FastClass机制,这里不再赘述。明白了这边,我们再回到代码中:

 MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, arg);

首先生成一个FastClassInfo对象fci,保存了目标类,代理类,目标方法下标,代理类执行的下标。而fci.f2.invoke(fci.i2, obj, arg);调用的的为代理类的对应的目标方法(f2).即又调用回了目标类的方法,即:

final String CGLIB$say$0(){
    return super.say();
  }

而这里,才是真正额调用调用到了父类(目标类)中对应的方法。至此,整个的调用流程完毕。

四:小结

cglib动态代理小结:

(1)首先生成代理对象。【创建增强类enhancer,设置代理类的父类,设置回调拦截方法,返回创建的代理对象】

(2)调用代理类中的方法。【这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用intercept方法】

(3)调用intercept,方法中会对调用代理方法中的invokeSuper方法。而在invokeSuper中维护了一个FastClassInfo类,其包含四个属性字段,分别为FastClass f1(目标类);FastClass f2 (代理类); int i1(目标类要执行方法的下标); int i2(代理类要执行方法的下标); invokeSuper中会调用的为代理类中的对应方法(代理类继承于父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法)

(4)调用代理类中的代理方法,代理方法中通过super.method来实际真正的调用要执行的方法。

 

更多深入解析文章,请关注我的公众号:南瓜小灯

让我们共同学习吧!

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cglib动态代理实现原理详细分析 的相关文章

  • Java复习-25-单例设计模式

    单例设计模式 目的 使用场景 在实际开发下 会存在一种情况 某一种类在程序的整个生命周期中 只需要实例化一次就足够了 例如 系统数据类 由于操作系统只有一个 因此在程序初始化时该类只需要实例化一次 之后的系统数据更改都是在这一个实例化对象中
  • 设计模式三: 代理模式(Proxy) -- JDK的实现方式

    简介 代理模式属于行为型模式的一种 控制对其他对象的访问 起到中介作用 代理模式核心角色 真实角色 代理角色 按实现方式不同分为静态代理和动态代理两种 意图 控制对其它对象的访问 类图 实现 JDK自带了Proxy的实现 下面我们先使用JD
  • C++设计模式(二)观察者模式

    1 观察者模式知识点 1 定义 定义对象间的一种一对多的依赖关系 当一个对象的状态发生改变的时候 所有依赖它的对象都得到通知并自动更新 2 动机 将一个系统分割成一系列相互协作的类有一个常见的副作用 需要维护相关对象间的一致性 我们不希望为
  • 设计模式——原型模式

    原型模式顾名思义 就是指以某个实例为原型 copy出一个新的实例 该实例属性与原型相同或者是类似 很多时候 我们需要创建大量的相同或者相似的对象 如果一个个用new 构造函数的形式去创建的话比较繁琐 就像孙悟空要想变出成千上万个猴子猴孙总不
  • 小谈设计模式(1)—总序

    小谈设计模式 1 总序 专栏地址 开始操作 设计模式总论 设计模式是什么 组成要素 模式名称 问题描述 解决方案 效果描述 设计模式有什么作用 提供可重用的解决方案 提高代码的可读性和可维护性 促进代码的可扩展性 提高代码的灵活性和可重用性
  • 设计模式学习之装饰器模式

    装饰器 Decorator 模式跟适配器 Adapter 模式一样 属于构建型设计模式 在学习适配器模式的时候说过 适配器模式的重点在复用能力 装饰器模式的重点在扩展能力 换言之 装饰器模式是先复用后扩展 这也导致了很多人跟适配器模式混淆
  • 设计模式(十)装饰器模式

    装饰器模式是一种非常有用的结构型模式 它允许我们在不改变类的结果的情况下 为类添加新的功能 我们来举例说明一下 首先添加一组形状 它们都实现了形状接口 public interface Shape String getShape class
  • 设计模式学习笔记-工厂模式

    设计模式学习笔记 工厂模式 作用 实现了创建者和调用者的分离 详细分类 简单工厂模式 用来生产同一等级结构中的任意产品 对于增加新的产品 必须要扩展已有的代码 工厂方法模式 用来生产同一等级结构中的固定产品 支持增加任意产品 抽象工厂模式
  • 单例模式的八种写法比较

    单例模式是最常用到的设计模式之一 熟悉设计模式的朋友对单例模式都不会陌生 一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式 但是除了这两种方式 本文还会介绍其他几种实现单例的方式 让我们来一起看看吧 简介 单例模式是一种常
  • [设计模式]模板方法模式(Template Method)

    1 意图 定义一个操作中的算法的骨架 而将一些步骤延迟到子类中 TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 2 动机 其实就是如意图所描述的 算法的骨架是一样的 就是有些特殊步骤不一样 就可以
  • Java设计模式之装饰者设计模式Decorator Pattern

    目录 一 基本概念 二 结构 1 图示 三 案例演示 被装饰对象的基类 一个接口 有cost 和description 两个抽象方法 具体被装饰的对象 实现上面这个接口 装饰者抽象类 基类 实现drink接口 具体的装饰者类 糖 具体装饰者
  • 设计模式之访问者模式

    访问者模式 把被操作的对象作为元素 可变可拓展的操作作为访问者 可以说访问者中有很多操作 然后访问者访问元素 对该元素进行操作 不同的访问者有不同的操作 案例 定义访问者接口 public interface UniversalVisito
  • JavaScript设计模式-02-单例模式

    Javascript 设计模式 02 单例模式 简介 单例就是保证一个类只有一个实例 实现的方法一般是先判断实例是否存在 如果存在直接返回 如果不存在就创建了再返回 确保了一个类只有一个实例对象 在JavaScript里 单例作为一个命名空
  • 二十四种设计模式之策略模式

    一 什么是策略模式 简单来说 策略模式是将每一个算法封装到拥有共同接口的不同类中 使得算法可以在不影响客户端的情况下发生变化 也可以理解为可供程序运行时选择的 不同的类 不同的解决方案 策略模式的特点 高内聚低耦合 可扩展 遵循ocp原则
  • 设计模式详解---策略模式

    1 策略模式简介 策略模式 Strategy Pattern 是一种行为型设计模式 用于在运行时根据不同的情境选择不同的算法或策略 该模式将算法封装成独立的类 使得它们可以相互替换 而且可以独立于客户端使用它们的方式 1 1 主要角色 上下
  • Java设计模式:模板方法模式

    作者主页 欢迎来到我的技术博客 个人介绍 大家好 本人热衷于 Java后端开发 欢迎来交流学习哦 如果文章对您有帮助 记得 关注 点赞 收藏 评论 您的支持将是我创作的动力 让我们一起加油进步吧 文章目录 一 模板方法模式的定义 二 模板方
  • 设计模式(3)--对象结构(5)--外观

    1 意图 为子系统中的一组接口提供一个一致的界面 Facade模式定义了一个高层接口 这个接口使得 这一子系统更加容易使用 2 两种角色 子系统 Subsystem 外观 Facade 3 优点 3 1 对客户屏蔽了子系统组件 减少了客户处
  • C++设计模式 #3策略模式(Strategy Method)

    动机 在软件构建过程中 某些对象使用的的算法可能多种多样 经常改变 如果将这些算法都写在类中 会使得类变得异常复杂 而且有时候支持不频繁使用的算法也是性能负担 如何在运行时根据需求透明地更改对象的算法 将算法和对象本身解耦 从而避免上述问题
  • 2023 年精选:每个 DevOps 团队都应该了解的 5 种微服务设计模式

    微服务彻底改变了应用程序开发世界 将大型整体系统分解为更小 更易于管理的组件 这种架构风格的特点是独立 松散耦合的服务 带来了从可扩展性 模块化到更高的灵活性等众多优势 DevOps 团队如何最好地利用这种方法来实现最高效率 答案在于理解并
  • 【设计模式之美】 SOLID 原则之五:依赖反转原则:将代码执行流程交给框架

    文章目录 一 控制反转 IOC 二 依赖注入 DI 三 依赖注入框架 DI Framework 四 依赖反转原则 DIP 一 控制反转 IOC 通过一个例子来看一下 什么是控制反转 public class UserServiceTest

随机推荐

  • 服务器卡死,重启报错: INFO: task blocked for more than 120 seconds

    问题 服务器负载很高 但是CPU利用率不高 服务器经常夯住 网站打不开 SSH连接非常不稳定 输入命令夯住 重启服务器报错 INFO task blocked for more than 120 seconds 问题原因 默认情况下 Lin
  • Linux下安装配置tomcat

    Linux下安装配置tomcat 1 安装 Tomcat版本 6 0 29 Linux版本 Radhat Enterprise 5 5 Jdk版本 1 6 0 20 解压缩tomcat tar zxvf apache tomcat 6 0
  • python+django网上美食菜品订餐系统的设计与实现vue

    随着科学技术的飞速发展 社会的方方面面 各行各业都在努力与现代的先进技术接轨 通过科技手段来提高自身的优势 好吃网线上订餐系统当然也不能排除在外 从美食类型 美食信息的统计和分析 在过程中会产生大量的 各种各样的数据 本文以好吃网线上订餐系
  • JS - 基本语法

    JavaScript是一种脚本语言 主要功能是 动态修改html页面内容 包括创建 删除html页面元素 修改html页面元素的内容 外观 位置 大小等 数据类型和变量 任何语言都离不开数据类型和变量 虽然JavaScript语言是弱类型的
  • D-S证据理论

    一 前言 20世纪60年代美国哈佛大学数学家A P Dempster利用上 下限概率来解决多值映射问题方面的研究工作 自1967年起连续发表了一系列论文 标志着证据理论的诞生 Dempster的学生G Shafer对证据理论做了进一步发展
  • js中for循环与定时器

    js中for循环和定时器的问题 有四个解决方法 这里面涉及到了同步与异步的问题 也可以理解为 解决方法1 闭包 解决方法2 拆分结构 解决方法3 let let和var区别 解决方法4 第三个参数 for var i 0 i lt 6 i
  • 超酷的13个CSS有趣学习网站

    13个CSS有趣学习网站 今天来给大家推荐13个辅助你学习巩固知识的网站 让你边玩边学边记 因为这些网站大多都是国外的大佬们做的 所以网页大多都是英文 为了更好地使用 给你们推荐两个翻译的方式 使用Chrome浏览器自带的翻译功能 可以中英
  • Linux异步IO实现方案总结

    一 glibc aio 1 名称 由于是glibc提供的aio函数库 所以称为glibc aio glibc是GNU发布的libc库 即c运行库 另外网上还有其他叫法posix aio 都是指glibc提供的这套aio实现方案 2 主要接口
  • java中other的用法_java语言 - 如何使用"variable might not have been initialized(变量可能未初始化)"避免System.exit_others_...

    奇怪的是编译器不知道System exit 1 永远不会返回 因此 你所要做的就是给它提供一些不会让你从catch块到try catch之后的东西 例如 try foo Long parseLong args 0 bar Long pars
  • java获取前四个季度结束日期_JAVA使用LocalDate获取当前日期所在季度的开始日期和结束日期...

    需要使用jdk1 8及以上 获取当前日期所在季度的开始日期和结束日期 季度一年四季 第一季度 1月 3月 第二季度 4月 6月 第三季度 7月 9月 第四季度 10月 12月 param isFirst true表示查询本季度开始日期 fa
  • IOU计算Python代码实现

    文章目录 一 目标检测中的IOU代码实现 二 代码 总结 一 目标检测中的IOU代码实现 目标检测中会用IOU大小的值来衡量检测结果与准确结果之间的差距 IOU的计算公式 IOU A B A B 式中A B为检测结果和准确结果 ground
  • opencv4(五) VideoCapture获取摄像头图像

    环境 ubuntu18 04 opencv4 4 0 摄像头 usb摄像头 挺老的摄像头 还是usb2 0的 csi摄像头也支持这种方法 插入摄像头后 ls dev可以看到 dev video0 video后面的数字就是后面需要的设备id
  • IDEA 之解决快捷键突然失效问题

    刚开机打开IDEA所有快捷键是可用的 但是发现打开很多软件后 IDEA中有些快捷键会失效 原因 IDEA的快捷键和其中一个软件的快捷键冲突 解决方案一 找到和IDEA快捷键冲突的软件 在软件中取消和IDEA冲突的快捷键 但是很多情况下根本找
  • springboot:员工管理系统-修改与删除

    修改员工数据 编辑和删除都需要根据员工的id来进行 步骤 我们需要一个按钮跳转到编辑页面 从而进行修改功能 a class btn btn sm btn primary 编辑 a 对应的controller 去员工的修改页面 GetMapp
  • 继承的笔记

    继承 对象代表什么 就得封装对应的数据 并提供数据对应的行为 对于两种不同的类 但是具有很多共同的属性的时候我们就想着用继承 我们可以将共同的属性放置在一个类中 然后 只需要新建两个类 继承共有的类 然后单独写自己的属性特点 继承类 Jav
  • C++ 符号常量

    一 const限定符 使用const关键字来创建符号常量 常量被创建后其值就固定了 编译器将不允许修改该常量的值 const int a 20 注意 应在声明时对const进行初始化 如果在声明常量时没有提供值 则该常量的值将是不确定的 且
  • 第二部分__建模应用篇__第十一章__时间序列分析

    这一部分是时间序列 计量经济学的大头 下面两个库很重要 statsmodel http www statsmodels org stable index html arch https github com bashtage arch AR
  • JQuery版本使用建议

    目前jQuery有三个大版本 1 x x 兼容ie6 7 8 使用最为广泛 官网只做BUG维护 功能不再新增 因此一般项目来说 使用1 X版本就可以了 最终版本 1 12 4 2016年5月20日 2 x x 不兼容ie6 7 8 很少有人
  • Python3.8 Numpy包 pip指令安装失败、提示超时的解决办法

    每次下东西必折腾半天 这次又遇到了新问题 Python3 8下载Numpy包以及各种包都出错 1 问题描述 在cmd命令提示符窗口调用 pip install package 下载十多分钟后进度条卡住 然后提示超时 尝试添加参数 defau
  • cglib动态代理实现原理详细分析

    在之前Java代理模式中大致的分析了下代理模式的类型及对每种代理类型简单的举例了下 在上篇JDK动态代理实现原理详细分析中 对其JDK代理的流程做了一个详细的分析 而本文 将介绍另一种动态代理模式 cglib动态代理 阅读完本文 你将对cg