cglib动态代理实现原理_动态代理的两种方式

cglib动态代理实现原理_动态代理的两种方式CGLib动态代理原理CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法;看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑,结构还是一目了然的;1.CGLib的基本使用使用一下CGLib,在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer;使用的CGLib版本是2.2.2,我是随便找的,不

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

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{ 
   

    @Override
    public 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$eat 0 这 两 个 方 法 ;     − 其 中 前 者 e a t 则 是 我 们 使 用 代 理 类 时 候 调 用 的 方 法 ,     − 后 者 C G L I B 0这两个方法;   - 其中前者eat 则是我们使用代理类时候调用的方法,   - 后者CGLIB 0  eat使  CGLIBeat 0 是 在 方 法 拦 截 器 里 面 调 用 的 ,   换 句 话 来 说 当 我 们 代 码 调 用 代 理 对 象 的 e a t 方 法 , 然 后 会 到 方 法 拦 截 器 中 调 用 i n t e r c e p t 方 法 , 该 方 法 内 则 通 过 p r o x y . i n v o k e S u p e r 调 用 C G L I B 0是在方法拦截器里面调用的,  换句话来说当我们代码调用代理对象的eat方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIB 0 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是否为空,也就是我们传入的方法拦截器是否为空,如果不为空就最终到下面的_L4
        if(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的fastclass
        FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass
        int i1; //方法g在f1中的索引
        int i2; //方法CGLIB$g$0在f2中的索引
    }

MethodProxy 中invokeSuper方法的代码如下:

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

当调用invokeSuper方法时,实际上是调用代理类的CGLIB$g 0 方 法 , C G L I B 0方法,CGLIB 0CGLIBg$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),其实就是调用CGLIB e a t eat eat这个方法

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

5.总结

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

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

在这里插入图片描述

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/179117.html原文链接:https://javaforall.net

(0)
上一篇 2022年8月30日 下午3:16
下一篇 2022年8月30日 下午3:16


相关推荐

  • 慧荣SM2246EN开卡Toggle 8贴东芝闪存SSD失败解决方法[通俗易懂]

    慧荣SM2246EN开卡Toggle 8贴东芝闪存SSD失败解决方法[通俗易懂]如果你的固态硬盘是慧荣SM2246EN主控的,且闪存为8贴9D2H9E2H9DDJ9EDJ9EFK9DFK9DFL9EFL9CDJ等8Die4CE的,那么遇到开卡失败的话,可以尝试下面的方法来解决:打开慧荣SM2246EN量产工具文件夹中的FlashDB文件夹,找到里面的Flash.SET(闪存参数配置)文件,用记事本打开此文件后,把对应的闪存参数第46项数值(十六进制)适当降低1-6(十六进制)即可解决。比如下面以9DDJ为例:减少后,再次去开卡,应该就能过了,如果你也遇到

    2022年6月8日
    80
  • pygame安装(超级详细)

    pygame安装(超级详细)安装时是需要设置python环境变量的,下载python的时候底下有个小框框(没有默认选中)AddPython3.7toPATH需要选择的,如果没有选择的话,需要自己设置,我一般比较懒,卸载了python重新下载的,让下载器自动设置。然后是python版本问题有人疑问这是64位还是32位,看那个[64bit]是64位,至于后面的on32是在windows上使用的意思,在Linux上…

    2022年5月24日
    204
  • DWR问题

    DWR问题这两天琢磨了 jQuery 的 ajax 使用方法 好像运用到 php 中很方便 不太熟悉怎么运用到 ssh 框架中 回过头来 发现一直用的 DWR 也用不起来了 不知道问题出在了哪里 花了好多时间 功夫不负有心人发现了症结所在 dwr 被我动过了下面是一个和 Spring 配合使用的 DWR 例子 其中 beanName 不能改为其它名字 如果不在 spring 中使用的话 prarm 就为 cla

    2026年3月17日
    1
  • Quagga 移植

    Quagga 移植文章目录一 Quagga 编译的依赖包二先交叉编译三个依赖包三编译 Quagga 一 Quagga 编译的依赖包 libcareslibn 其中 libreadline 是需要 libncurses 支持的 如果没有 ncurses 在编译 quagga 时会出现如下错误 libreadline so undefinedref PC

    2026年3月26日
    2
  • 论文精读——CenterNet :Objects as Points[通俗易懂]

    论文精读——CenterNet :Objects as Points[通俗易懂]论文题目:ObjectsasPoints论文地址:https://arxiv.org/pdf/1904.07850.pdf发布时间:2019.4.16机构:UTAustin,UCBerkeley代码:https://github.com/xingyizhou/CenterNetAbstract目标检测识别往往在图像上将目标以轴对称的框形式框出。大…

    2022年7月26日
    5
  • C语言基础:哪儿来的32766

    这篇文章通过一个简单的示例程序来介绍一下如何结合使用IDE来学习C语言。

    2022年4月4日
    53

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号