java的classLoader分析与jettty的WebAppClassLoader

IAR链接器配置文件(.icf)实战指南:从内存映射到代码布局 本文深入解析了IAR链接器配置文件(.icf)的核心作用与实战配置方法。作为嵌入式开发的“内存地图”,.icf文件精确控制代码与数据在Flash和RAM中的布局。文章从定义存储空间、划分区域、预留栈堆,到精确放置中断向量表、代码段及初始化控制,提供了完整的配置指南与避坑技巧,帮助开发者优化内存使用,解决链接错误,是掌握嵌入式系统底层内存管理的关键。 阅读详情

classLoader,从名字就可以知道,用于加载class的东西。

我们知道在java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。

先用一张图来整体描述一下java标准中定义的classLoader的层次吧:



在整个定义中,一共有两种类型的加载器:

(1)启动类加载器:Bootstrap ClassLoader,这个类加载器是使用c++语言实现的,它本身就是虚拟机的一部分

(2)其他的类加载器,这些类加载器都是用java语言实现的,独立于虚拟机外部,并且他们都继承自抽象类java.lang.ClassLoader


另外按照功能来分的话可以如下:

(1)启动类加载器,也就是Bootstrap ClassLoader,这个类加载器负责将存放在<JAVA_HOME>/lib目录中的类库加载到虚拟机的内存中,而且它还要识别名字,如果名字不符合,就算放在lib目录中,也不会被加载,这个加载器无法被 java程序所引用。。。

(2)扩展类加载器,Extension ClassLoader,这个加载器负责加载<JAVA_HOME>/lib/ext目录中的类库,也就是扩展类库,java程序中可以直接引用这个加载器。。。。

(3)应用程序加载器,Application ClassLoader,它用与负责加载用户类路径上的类库,java程序中可以直接使用这个加载器,而且如果在应用程序中没有自定义过自己的类加载器,那么一般情况下这个就是程序中的默认加载器。。。。也就是loadClass啥的默认都是它了。。。。


另外从上面的图形也可以看成一种层次关系,除了最顶层的启动类加载器之外,其余的加载器都必须要有自己的父加载器(这个是组合实现的,不是继承)。。

这个也就是java中定义的所谓的双亲委派模型:

如果一个类加载器收到了 类加载的请求,那么它首先不会自己去尝试加载这个类,而是将这个请求委派给自己的父加载器去完成,每一层的加载器都是,因此任何一个加载请求都会自动传送到顶层的启动类加载器中,只有当自己的父加载器反馈无法完成这个加载请求的时候,子加载器才会去尝试自己去加载。。。。


这个是一个非常重要的定义,因为它限定了java在api方面的统一,越基础的类那么就由越基础的加载器来加载。。。。

(补充:同一个class文件,如果被不同的加载器加载,那么他们将会不相等)


这种双亲委派模型是java中默认以及推荐的模型,我们一般自己实现classLoader的时候也需要遵守这种规范,但是在web容器中就需要打破这种双亲委派模型了。。。

主流的java web服务器,如tomcat,jetty,weblogic,啥的都有自己定义自己的类加载器。。。

因为在同一个服务器上我们会部署多个web应用程序,那么就需要这些web应用程序代码之间实现相互的隔离,而且web程序的类库还需要与服务器之间隔离,这样web应用程序的部署才不会影响到web服务器。。。

那么在这里就需要打破双亲委派的模型了。。。而且这里会用到线程上下文类加载器。。。(Thread context classLoader)


上面说了这么多,都是对java的classLoader的方面知识的一些普及,因为其实自己在之前也对这方面的概念都了解很少。。。

那么接下来就可开始来分析jetty中定义的WebAppClassLoader了,它在jetty中用于负责加载web应用程序WEB-INF目录下lib以及classes里面的类库。。说白了就是用于加载web应用程序自己的源代码。。。

先来看看它定义的一些比较重要的属性吧:

    private String _name;   //名字
    private WebAppContext _context;   //其所关联的webcontext
    private ClassLoader _parent;   //父classLoader

最开始的是当前loader名字,不知道为啥要有这个名字,感觉也没啥用....第二个参数就是当前classLoader所属的webcontext,这个属性就属于比较重要的了,,...。。。每一个web应用程序都会有一个属于自己的classlaoder,最后一个参数就是前面提到过的父classLoader,仿佛这里看起来是要用于实现上面说过的双亲委派,其实不然。。。

在我们自己定义的web应用程序中,一会用到其余的class代码,例如String,hashmap啥的,那么这里有一个parent,就可以保证可以在parent里面获取他们的class。。。。


那么接下来来看看它的构造函数吧:

    public WebAppClassLoader(WebAppContext context)
        throws IOException  {
        this(null,context);
    }
    
    /* ------------------------------------------------------------ */
    /** Constructor.
     */
    //这里可以看出,如果在构建的时候没有提供父亲classLoader,那么将会默认将当前的线程classLoader作为当前的父classLoader
    public WebAppClassLoader(ClassLoader parent, WebAppContext context)
        throws IOException {
        super(new URL[]{},parent!=null?parent
                :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()
                        :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()
                                :ClassLoader.getSystemClassLoader())));
        _parent=getParent();  //指向当前的parent
        _context=context;  //保存当前的context
        if (_parent==null)
            throw new IllegalArgumentException("no parent classloader!");
        
        if (context.getExtraClasspath()!=null) {
            addClassPath(context.getExtraClasspath());
        }
    }

其实这里一般情况下都是会调用第一个构造函数,第二个构造函数主要是要满足父类URLClassLoader的构造,它是java系统类库中的。。。而且到这里就能够知道其实这里默认是将当前的线程上下文classLoader指定为当前的parent,而这个线程上下文classLoader如果没有用户指定的话默认又将是前面提到过的appClassLoader,也就是用于加载用户代码的classLoader。。。。

接下来来看看它定义的loadClass方法吧:

    //加载类,因为每一个webContext 都会有一个自己的classLoader,所以也就是先了web应用程序代码之间的相互隔离
    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class c= findLoadedClass(name);  //首先在自己这里加载,我们自己定义的代码,引用的jar里面的代码都是在这里加载的
        ClassNotFoundException ex= null;
        boolean tried_parent= false;
        //如果父类加载器优先,或者这里加载的类型是系统类型,例如"java.","javax.servlet.","javax.xml.",那么由父类加载器来加载
        //不过webapp的代码父类加载器肯定是加载不倒的
        if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) {
            tried_parent= true;
            try {
            	//这里的parent就是appclassLoader
                c= _parent.loadClass(name);  //由父类加载,一些系统代码,例如hashMap,set啥的都在这里加载,还有jetty定义的也在这里加载,例如org.mortbay.servlet.NoJspServlet
                if (Log.isDebugEnabled())
                    Log.debug("loaded " + c);
            }  catch (ClassNotFoundException e) {
                ex= e;
            }
        }

        if (c == null) {
            try  {
                c= this.findClass(name);  //这里是从当前classLoader的路径下面去加载
            } catch (ClassNotFoundException e) {
                ex= e;
            }
        }

        if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )
            c= _parent.loadClass(name);

        if (c == null)
            throw ex;

        if (resolve)
            resolveClass(c);

        if (Log.isDebugEnabled())
            Log.debug("loaded " + c+ " from "+c.getClassLoader());
        
        return c;
    }

这段代码其实还算是比较的简单吧。。。这里就可以知道指定parent的重要作用了。。。因为毕竟在我们自己的代码中也会有调用一些类库中的代码。。。而在parent中,却无法加载到我们自己定义的class文件。。。也就实现代码与jetty 服务器本身的隔离。。。。

又因为每一个web应用程序都有自己的classLoader,也就是先了在同一个服务器下,不同的app之间的代码隔离。。。

最后再来补充两个方法吧:

    //添加class或者jar的路径,如果是文件夹路径的话,那么路径应该以/  结尾
    public void addClassPath(String classPath)
    	throws IOException {
        if (classPath == null)
            return;
            
        StringTokenizer tokenizer= new StringTokenizer(classPath, ",;");  //有可能一次性传入多个classLoader
        while (tokenizer.hasMoreTokens()) {
        	/*
        	 * file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/classes/
			   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/aopalliance-1.0.jar
			   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/commons-collections-3.1.jar
        	 */
            Resource resource= Resource.newResource(tokenizer.nextToken());  //获取当前路径的resource对象
            if (Log.isDebugEnabled())
                Log.debug("Path resource=" + resource);

            // Resolve file path if possible
            File file= resource.getFile();  //获取当前的文件
            if (file != null) {  
                URL url= resource.getURL();  //获取url
                addURL(url);   //这个是父类的方法,将会加载这个路径下的class或者jar资源
            } else {
                // Add resource or expand jar/
                if (!resource.isDirectory() && file == null)
                {
                    InputStream in= resource.getInputStream();
                    File tmp_dir=_context.getTempDirectory();
                    if (tmp_dir==null)
                    {
                        tmp_dir = File.createTempFile("jetty.cl.lib",null);
                        tmp_dir.mkdir();
                        tmp_dir.deleteOnExit();
                    }
                    File lib= new File(tmp_dir, "lib");
                    if (!lib.exists())
                    {
                        lib.mkdir();
                        lib.deleteOnExit();
                    }
                    File jar= File.createTempFile("Jetty-", ".jar", lib);
                    
                    jar.deleteOnExit();
                    if (Log.isDebugEnabled())
                        Log.debug("Extract " + resource + " to " + jar);
                    FileOutputStream out = null;
                    try
                    {
                        out= new FileOutputStream(jar);
                        IO.copy(in, out);
                    }
                    finally
                    {
                        IO.close(out);
                    }
                    
                    URL url= jar.toURL();
                    addURL(url);
                } else {
                    URL url= resource.getURL();
                    addURL(url);  //添加class的搜索路径
                }
            }
        }
    }

    
    

    //添加jar包或压缩文件
    public void addJars(Resource lib)  {
        if (lib.exists() && lib.isDirectory())
        {
            String[] files=lib.list();  //获取当前文件夹下面的所有文件
            //遍历下面的所有文件
            for (int f=0;files!=null && f<files.length;f++)  {
                try {
                    Resource fn=lib.addPath(files[f]);    //获取这个文件的resource
                    String fnlc=fn.getName().toLowerCase();
                    //判断文件的扩展名是否正确
                    if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))  {
                    	String jar=fn.toString();  
                    	jar=StringUtil.replace(jar, ",", "%2C");
                    	jar=StringUtil.replace(jar, ";", "%3B");
                        addClassPath(jar); //调用addClassPath来将jar包加载
                    }
                }  catch (Exception ex) {
                    Log.warn(Log.EXCEPTION,ex);
                }
            }
        }
    }

这两个方法的定义应该从名字就能够知道是干嘛的。。用于加载jar包或者class文件的。。最终是通过父类的addUrl的方法来实现这些源文件的加载。。。


好了,到这里jetty中定义的webappclassLoader就算是比较清楚了。。不过比较遗憾没有去分析它的父类URLClassLoader,从而对java的整个classLoader有更深的了解。。以后有机会的话看看吧。。

这样子就可以开始看jetty中如何创建webContext以及启动这些web应用程序了。。。

ubuntu20.04 终端安装claude 【代码】ubuntu20.04 终端安装claude。 阅读详情

相关推荐

Ribo测序翻译效率计算及可视化

本文介绍了一个计算翻译效率(TE)和分析转录-翻译相关性的生物信息学流程。首先通过Python脚本从标准化表达量矩阵(TPM/FPKM)计算TE值,该脚本自动匹配转录组(RNA)和翻译组(Ribo)样本进行比值运算。然后使用R语言进行相关性分析,通过ggplot2绘制样本间转录-翻译表达量的散点图,添加线性回归线和相关系数标注,支持PNG/PDF格式输出。整个流程实现了从原始数据处理到可视化分析的全过程。

Tiaoxiaobai的博客 552

Jetty源码阅读---Jetty的类加载器WebAppClassLoader

前面介绍了java中类加载的一般模型:双亲委派模型,这个模型适用于大多数类加载的场景,但对于web容器却是不适用的;这是因为servlet规范对web容器的类加载做了一些规定,简单的来说有以下几条: 1.WEB-INF/classes和WEB-INF/lib路径下的类会优先于父容器中的类加载,比如WEB-INF/classes下有个ABC类,CLASSPATH下也有个ABC类,jetty会

荒野孤鹰 2637

用Multisim和AD2复刻经典:手把手教你搭建μA741三角波-方波发生器(附完整避坑指南)

本文详细介绍了如何使用Multisim和AD2搭建基于μA741运算放大器的三角波-方波发生器,包括电路原理、Multisim仿真流程、AD2硬件搭建及调试技巧。通过对比理论计算、仿真和实测数据,深入分析器件非理想特性的影响,并提供16个典型问题的解决方案,帮助读者掌握波形发生器的设计原理和工程调试经验。

weixin_28744423的博客 284

Jetty类加载

ssloader,系统classloader作为它的父亲。在Java中这样的层级关系是正常的,然而servlet规范复杂化了层级关系,因为它要求:  1)包含在WEB-INF/lib或者WEB-INF/classes中的类优先于在父classloader上的类。这和通常的Java 2 classloader的行为是对立的。  2)系统类例如java.lang.String被从webapp优先权

宁静致远 5474

行车记录仪 - 碰撞检测

功能需求 行车记录仪的核心功能是记录,大部分时候,行车记录仪都是在循环录制视频和音频,而设备的存储空间是有限的,因此录制机制一般是循环覆盖录制,如此一些重要的记录(例如:发生碰撞时的记录)就可能会被覆盖,为了避免这种情况的发生,行车记录仪设备就需要感知这些情况并做特殊处理(例如:备份相应时刻的录像数据)。 实现原理 碰撞检测是通过物理芯片G-sensor来实现的,加速度发生变化时G-sensor驱动会将加速度上报给应用层,应用层做相应处理。 G-sensor G-sensor(Gravity se

qazw9600的专栏 5214

Gamit 数据处理,相关的文件配置

一、处理前准备 1、在主文件夹内新建test项目文件,项目内新建brdc、igs和rinex三个文件夹,分别存放广播星历,精密星历几观测值文件,所用的命令分别为sh_get_nav、sh_get_orbits和sh_get_rinex (若文件为.Z,用gunzip命令解压,若仍为d,用命令sh_crx2rnx -f 命令解压为o文件) 2、进入test项目文件夹,链接tables,运行 sh_setup -yr 2015 -apr itrf08_com.apr 3、生成station.info文件。 将t

CodeDoraemon的博客 3375

查找WebServer中各个App的ClassLoader

1. 介绍 web server 中 classLoader 的层次体系 2. 如何查找web server中各个app的ClassLoader(以Jetty和Tomcat为例)

helowken2的博客 762

Unity URP半透明物体渲染避坑指南:双面渲染与深度写入的实战技巧

本文深入探讨了Unity URP中半透明物体自交叠渲染问题的核心根源与解决方案。针对模型自身交叠导致的排序错误,文章提供了双面渲染与深度写入两种实战技巧,详细分析了其实现原理、性能开销及适用场景,帮助开发者根据项目需求选择最优方案,有效提升玻璃、水体等材质的视觉表现。

sunny的博客 545

Web 容器 Jetty 多 ClassLoader 重复加载 Class 导致 java.lang.LinkageError

问题描述: 最近团队在使用 Axis2 开发 WebService 服务时,遇到服务发布成功,但调用一直出现如下错误 Axis2 Can't find Spring's ApplicationContext 问题分析: 查看异常出处:SpringAppContextAwareObjectSupplier.getServiceObject 方法可知 ApplicationContext 由

u013244720的专栏 1530

【LAMMPS学习】四、运行 LAMMPS(2)命令行选项

本部分解释了在安装可执行文件或下载源代码并构建可执行文件后如何运行 LAMMPS。命令文档页面描述了输入脚本的结构以及它们可以包含的命令。

荷塘阅色 5241

AWS SQS跨账户访问失败排查指南

在使用AWS SQS(Simple Queue Service)时,跨账户访问是常见的业务场景。例如,账户A的应用程序向队列发送消息,账户B的消费者从队列拉取消息。尽管AWS官方文档明确支持此类配置,但在实际应用中,由于权限模型的复杂性,开发者和运维人员常会遇到“策略已配置但无法接收消息”的问题。通过本文的逐步排查指南,您不仅可以解决当前问题,还能深入理解AWS跨账户访问的权限模型。接收方账户的用户或角色必须同时附加IAM策略,显式授权操作目标队列。尽管策略看似允许接收方操作,实际测试中却返回空消息或。

探索云原生与智能化驱动下的安全运维新范式。关注DevSecOps、可观测性、AIOps等前沿领域,与您共赴技术前沿。 456

jetty WebappClassLoader 问题

jetty WebappClassLoader 问题 一、 背景: 一个main方法启动一个java app和一个jetty webserver 但是jetty webserver 获取不到java app的数据。【就是jetty web action不能获取 app中对象的返回结果】 原因:两部分程序有两个classLoader sun.misc.Launcher$AppClass...

hongliangpan 703

制作3D地形图的方法-使用GlobalMapper和3ds Max软件

打开GlobalMapper软件,在菜单栏中选择“文件”>“打开数据文件”,然后选择相应的高程数据文件。在菜单栏中选择“渲染”>“渲染设置”,根据需要进行设置,如分辨率和输出格式。选择地形对象,点击菜单栏中的“渲染”>“材质编辑器”,然后根据需要调整材质属性,如颜色、纹理和光照等。在菜单栏中选择“工具”>“高程栅格和网格”>“创建地形网格”。在菜单栏中选择“文件”>“导出”>“导出栅格格式”。打开3ds Max软件,在菜单栏中选择“文件”>“导入”,然后选择之前导出的地形数据文件。步骤3:导出地形网格。

JqlScala的博客 1535

Java的ClassLoader分析与Jettty的WebAppClassLoader

classLoader,从名字就可以知道,用于加载class的东西。 我们知道在java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。 先

kobejayandy的专栏 1113

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

主流web容器(jetty,tomcat,jboss)的classloader机制对比和相关问题分析

背景      前段时间一直在做应用容器的迁移,将公司的应用容器从jboss,tomcat统一迁移到jetty。在整个迁移过程中遇到最多的潜在问题还是在classloader机制上,这里记录一下希望能对大家有所帮助,避免重复走弯路。   啥都不说,先来看下遇到的几个问题,比较纠结的问题。 问题1: (jar sealed问题) Caused by: java.lang.SecurityE...

agapple 402

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

非常规解决Eclipse启动Tomcat时报Error loading WebappClassLoader错误

网上搜了几种解决方案:1、关掉myeclipse,把原来的tomcat换掉,如果是安装版的就卸载,重新放一个tomcat,启动myeclipse,配置tomcat,部署,再启动tomcat,一切OK。2、之前jar包我是通过Build path -> Configure build path-> Add External JARs的方式添加的,程序编译,Tomcat启动都很正常,没有错误。可是一访

苏湘的专栏 1050

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

JAVA ClassLoader

ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。 几个相关概念 ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流等),通过ClassLoader从而将资源载入JVM;每个class都有一个reference,指向自己的ClassL

u010228840的专栏 362

记录一次Jetty出现NoClassDefFoundError的解决过程

首先,问题出现在春结后,测试环境的大部分模块都不能使用,报如下错误。 3353394284 [163714][k4oi4rk2kxn1nfyu1] ERROR com.alibaba.dubbo.rpc.filter.ExceptionFilter  -  [DUBBO] Got unchecked and undeclared exception which called by 10.1.20...

gedingbaod的专栏 1541

Jetty 嵌入使用

1. 简介 Jetty 提供了一个Web服务器和javax.servlet容器,以及对HTTP/2,WebSocket,OSGi,JMX,JNDI,JAAS和许多其他集成的支持。这些组件是开源的,可用于商业用途和发行。 Jetty有一个口号:不要把应用部署到Jetty上,要把Jetty部署到你的应用里。这句话的意思是把应用打成一个war包部署到Jetty上,不如将Jetty作为应用的一个组件,它可以实例化并像POJO一样。换种说法,在嵌入式模块中运行Jetty意味着把HTTP模块放到你的应用里,这种方法比把

shpunishment的博客 1158

Java中用ClassLoader载入各种资源(类、文件、web资源)的方法

ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。 ClassLoader负责载入系统的所有资源(Class,文件,图片,来自网络的字节流等),通过ClassLoader从而将资源载入JVM 中。每个class都有一个引用,指向自己的ClassLoader。

程序媛想事儿 1万+

探讨ClassLoader引发的 java.lang.LinkageError

问题产生的原因 我遇到过的多次LinkageError,都是因为调用对象方法时,方法的某部分(参数或者返回值类型)被不同的ClassLoader所加载,下面用几个例子说明。 例子1:web应用 jetty 部署应用的时候,出现了Java LinkageError: java.lang.reflect.InvocationTargetException at sun.reflect.NativeM...

helowken2的博客 3490

Java类加载器--Tomcat ClassLoader类加载器和WebappClassLoader类加载器

本文Tomcat版本基于apache-tomcat-8.5.12.看一下Tomcat的源码Bootstrap类的initClassLoader方法,代码如下: private void initClassLoaders() { try { // 创建commonLoader // 这里父加载器传递null,但是内部会使用默认的类加载器AppClassL...

DemoTransfer 1650

jetty嵌入式 webApp开发(Simple Demo)

时间可以带走一切    ---路 jetty是一个轻量级的servlet容器,有点类似tomcat,但是用起来比tomcat方便。 jetty下载链接:http://download.eclipse.org/jetty/ 我的开发环境 : 操作系统:UBuntu 14.04 LTS 集成工具:Eclipse(Version: Luna Service Release 1 (

happy_boys_的专栏 2386
上一篇: node.js与java性能测试(http,与redis访问)
下一篇: java的Reference与DirectBuffer的内存回收
fjs_cloud
博客等级 码龄15年 248粉丝 192原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值