浅析闭包在缓存逻辑中的一次应用

Unity Lua热更新开发:VSCode+EmmyLua远程调试环境搭建与实战 在游戏开发领域,热更新技术已成为中大型项目的标配方案,它允许开发者在应用上线后绕过应用商店审核,快速修复Bug和更新内容。其核心原理是通过动态加载脚本语言(如Lua)实现逻辑的实时更新,这显著提升了项目的迭代效率和线上问题响应能力。从技术价值看,一套稳定的热更新调试环境能极大改善开发体验,避免依赖低效的print语句排查问题。在实际应用场景中,Unity引擎常集成Lua作为热更新方案,但跨语言调试成为痛点。本文聚焦于通过VSCode与EmmyLua插件搭建远程调试环境,解决Unity中Lua代码的断点调试、 阅读详情


摘要

场景、背景
   缓存逻辑代码大量重复,需要设法精简,以提高编码效率,降低维护成本。
问题本质
  一段流程控制的代码大部分重复,小部分不同的情况下如何精简代码。典型情况如缓存逻辑,数据库查询逻辑等。
解决方案
  使用闭包,通过模板模式配合回调,予以解决。优点:代码精简优雅、使开发人员只需关注变化的业务逻辑而无需关注缓存存取实现的细节。

关键字
闭包,缓存,底层改造,框架优化

浅析闭包在缓存逻辑中的一次应用


摘要


什么是闭包?

闭包有什么用?

在Java中应用闭包

小结

参考文献


什么是闭包?

我们先来看一个直观的例子:

(由于闭包在函数式编程中很普遍,先以js代码来说明,js即属于典型的函数式编程)

-------代码片段 1-----------------------------------------------------------
function a(){
    var i=10;
    function b(){
        alert(++i);
    }
    return b;
}

var c = a();
c();


 

这段代码有两个特点:

1、函数b嵌套在函数a内部,同时b直接访问了其函数体外的临时变量i。

2、函数a返回函数b,供外部回调。

 

这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个对话框显示i的值(第一次为11)。这段代码其实就创建了一个闭包,即函数a内部定义的函数b与函数a内部的部分被b引用了的临时变量被打包在了一起,供函数a外的函数c回调时使用。

 

什么是闭包?

官方的解释有些生涩,我山寨地归纳了一下,闭包就是定义了一个用于回调的方法,其方法体内能够直接访问到该方法体外、定义该方法时所在作用域内的所有变量的形式。所谓“闭”,是指外部回调该方法时,那些方法体外的变量会起作用,但外部直接访问不了(即上例中通过函数c回调函数b,临时变量i出了函数a的作用域仍然有效,却不能直接被c引用);所谓“包”,是指这个被回调的方法与这些变量捆绑到了一起,像打包一样(即上例中的函数b和变量i)。

闭包有什么用?

在js中,我觉得闭包的主要作用是模拟private字段。上例中,变量i相当于一个全局变量,但又只能通过函数b才能访问它,函数b相当于它的get/set方法。

 

在Java中,我觉得闭包的运用和模板模式、回调以及代码精简这三者有很大的关系。

代码精简,即减少重复代码是最终目的;模板模式是实现精简的方式之一;回调是模板模式中必经的一个环节。(《thinking in Java》中把“闭包和回调”作为一节的标题,放在一起讲,我现在想通了才明白作者的用意)

 

思考最常见的一种场景,连接数据库执行一条sql语句,并对返回的结果集进行适当的处理(例如将其转化为List<DO>)。这几乎是我们dal层中每一个方法都在做的事。之所以现在的代码如此简洁,基本上只要一句

-------代码片段 2-----------------------------------------------------------

return getSqlMapClientTemplate().queryForList("SQL STATEMENT", param);


是因为执行前connection的连接和释放、执行后把数据集映射到DO等重复操作,都被封装在了类似SqlHelper的对象中(即上例中getSqlMapClientTemplate()返回的对象)。用户只需关心变化的sql语句和代入sql中的变量。

 

上例中,操作流程不变,仅待操作数据变化的情况下,可以将操作流程封装成带参方法。若情况再复杂一些,不仅数据有变化,操作也有一部分各不相同,该怎么处理呢?例如,一段典型的缓存存取的代码是这样的:

-------代码片段 3-----------------------------------------------------------
 
publicstaticSearchNewsDTO getTodayNewsByCategoryIdOrdering(String categoryIds, int start, int count, int postHour) {
       GeneralCacheAdministrator cacheAdmin = CacheAdminFactory.getGeneralCacheAdmin(true,false);
       String name = "getNewsByCategory" + categoryIds + start + count + postHour;
       String cacheKey = CacheKeyAdmin.getKey(CacheConstant.CACHE_GROUP_NEWS_LIST, name);
       try {
           Configuration conf = newConfiguration(NewsConstant.NEWS_APP_PROPERTIES);
           String refresh_cron = conf.getString(NewsConstant.CACHE_REFRESH_POLICY_DEFAULT_CRON,"1 1 * * *");
 
           return (SearchNewsDTO) cacheAdmin.getFromCache(cacheKey, -1, refresh_cron);
        }catch (NeedsRefreshException e) {
           try {
               SearchNewsDTO dto = NewsManager.getNewsByCategory(categoryIds, start, count, postHour);
               //如果找不到,就不能放入cache
               if (dto.getNewsCount() <= 0) {
                   cacheAdmin.cancelUpdate(cacheKey);
                }else {
                    cacheAdmin.putInCache(cacheKey, dto,newString[] { CacheConstant.CACHE_GROUP_NEWS_LIST });
                }
               return dto;
            }catch (Exception ex) {
                cacheAdmin.cancelUpdate(cacheKey);
               return (SearchNewsDTO) e.getCacheContent();
            }
        }
}


 

 

每个依赖cache的方法看起来都是如此复杂,其实每个方法体内,灰底代码之外的部分都是一成不变的。

观察一下不难发现,变化的代码只关注:

1、cacheKey

2、cachePolicy

3、cacheContent

 

为了让代码更简洁,可以通过模板模式,将重复代码封装到CacheHelper.getCache()方法中,该方法只接受一个Cacheable类型的参数,我们事先给出Cacheable的定义如下

 

-------代码片段 4-----------------------------------------------------------

 

  
publicinterfaceCacheable {
 
       String getCacheKey();// key
 
       String getCachePolicy();// policy,格式为cron
 
       Object getCachedObject();// cache实体
 
     
}



 

 

如果不采用闭包,每种原有方法都需要先编写一个实现了Cacheable接口的类,并且将getCachedObject()方法体内需要用到的数据通过构造方法或其它方式注入其中,看起来也比较繁琐:

 

-------代码片段 5-----------------------------------------------------------

//实现接口的代码,为了传入数据不可避免地变得冗长:
publicclassCacheableNewsimplementsCacheable {
 
   privateint[]categoryIds;
   privateint  start;
   privateint  count;
   privateint  postHour;
 
   public CacheableNews(int[] categoryIds, int start, int count, int postHour){       
       this.categoryIds = categoryIds;
       this.start = start;
       this.count = count;
       this.postHour = postHour;
    }
 
   publicString getCacheKey() {
       returnCacheKeyAdmin.getKey(CacheConstant.CACHE_GROUP_NEWS_LIST,name);
    }
 
   publicString getCachePolicy() {
       returnNewsConstant.CACHE_REFRESH_POLICY_DEFAULT_CRON;
    }
 
publicObject getCachedObject() {
//灰底部分的大段代码就是为了这一处使用
       NewsManager.getNewsByCategory(categoryIds,start,count,postHour);   
}
}
 
//目标方法倒是简洁了很多:
publicstaticSearchNewsDTO getTodayNewsByCategoryIdOrdering(String categoryIds, int start, int count, int postHour) {
 
CacheableNewsnews=newCacheableNews(categoryIds, start, count, postHour);
returnCacheHelper.getCache(news);
 
}


这样处理,虽然职责明确了,逻辑清晰了,但改造后的代码量是否仍让人觉得有些得不偿失呢?


在Java中应用闭包

通过闭包,目标方法将变得优雅简洁,同时开发人员无需关注缓存API的细节,也不再直接依赖它们。代码如下:

-------代码片段 6-----------------------------------------------------------

publicstaticSearchNewsDTOgetTodayNewsByCategoryIdOrdering(finalString categoryIds, finalint start,
                                                                finalint count, finalint postHour) {
 
       return (SearchNewsDTO)CacheHelper.getCache(newCacheableNews() {
 
           publicStringgetCacheKey() {
               returnmakeCacheKey("getNewsByCategory", categoryIds, start, count, postHour);
            }
 
           publicStringgetCachePolicy() {
               returnNewsConstant.CACHE_REFRESH_POLICY_DEFAULT_CRON;
            }
 
           publicObjectgetCachedObject() {
               returnNewsManager.getNewsByCategory(categoryIds, start, count, postHour);
            }
 
        });
 
    }


 
---------------------------------------------------------------------------

 

灰底部分实现了一个匿名内部类,其特点是能够像闭包一样获取上下文中的变量(前提它们是final的),同时也可以直接访问其宿主类中的private方法(相当于内部类和宿主类之间是友元的关系)

 

 

为什么必须是final?

我的理解是,闭包内的方法和数据会在未来某个时刻,即被回调时才用到,若不将局部变量标记成final,可能回调尚未触发,引用已被改变,于是回调触发时刻得到的将不是预期结果。

 

同时感谢架构师阿干对该问题的耐心解答:反编译匿名内部类的结果显示,编译器暗箱操作的结果正如代码片段5所示,在内部类的构造函数中,将所用到的外部变量注入,因此构造那一刻已经对所有变量执行了一次浅拷贝,若编译器不强制要求开发人员将这些变量标记为final,一旦变量在内部类构造后、回调前发生变化,将导致结果与实际不符(结果将基于旧的而非更新后的变量值),可以说是Java在模拟闭包过程中,编译器的局限所致。C#的实现即无此问题,构造闭包时会真正捕获该变量的引用,变量值可随意更改,并会内外双向影响。


参考:http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class


“ (When C# captures a variable in an anonymous function, it really captures the variable - the closure can update the variable in a way which is seen by the main body of the method, and vice versa.)”



小结

1.       使用闭包的好处是对回调方法优雅的封装,使开发人员能集中精力关注变化的部分。

2.        Java中闭包的典型使用场景为一些应用了模板模式的地方,如Java中新起一个线程,只需关注线程中需要执行的内容,而无需关心向不同操作系统请求线程的API细节;又如Spring的持久层模板,允许对结果集进行迭代,而无需关注异常管理、资源分配或清理等细节;再如上例中的缓存操作,只需关注被缓存对象和缓存策略,而无需关心缓存存取实现等细节。

3.        Java要求闭包内引用自上下文的临时变量必须是final的,以保证回调触发时刻传入的参数与预期的一致。

 

参考文献

1.       《深入理解Javascript闭包(closure)》http://www.v-ec.com/dh20156/article.asp?id=87

2.       《Closure (computer science)》http://en.wikipedia.org/wiki/Closure_(computer_science)#Java

3.       《冒号课堂第四课 重温范式(4)》http://blog.zhenghui.org/2009/09/19/colon-class-4_4/

4.       《跨越边界:闭包》http://www.ibm.com/developerworks/cn/Java/j-cb01097.html

5.       《高阶函数、委托与匿名方法》http://www.infoq.com/cn/articles/higher-order-function

Cocos引擎核心组件深度解析:从性能优化到工程实践 在游戏开发中,渲染合批与Draw Call优化是提升性能的关键技术。其原理在于通过合并使用相同纹理的渲染指令,减少CPU与GPU之间的通信开销,从而显著提升渲染效率。这项技术的核心价值在于能够有效降低图形API调用次数,对于移动端和Web平台的性能优化尤为重要。在实际应用场景中,无论是2D游戏的精灵渲染,还是复杂UI界面的绘制,合理的合批策略都能带来流畅度的质变。本文聚焦于Cocos引擎,深入剖析其节点系统、物理引擎集成以及动画状态机等核心组件,并结合纹理图集与Asset Bundle等热词,系统阐述如何通 阅读详情

相关推荐

如何选择StandardScaler、MinMaxScaler和RobustScaler做标准化的对比解释和示例代码

选择合适的标准化方法取决于数据的特点和需求。如果数据分布接近正态分布且无异常值,可以使用。如果需要将数据映射到特定范围内并保持相对关系,可以使用。如果数据中存在异常值或离群点,可以使用进行鲁棒标准化。在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,StandardScaler表现更好(避免不同量纲对方差、协方差计算的影响);在不涉及距离度量、协方差、数据不符合正态分布、异常值较少的时候,可使用MinMaxScaler。

qq_36001281的博客 4940

JS知识点汇总(四)--闭包&浏览器缓存

若不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的。foo()函数的执行结果返回给bar,而此时变量a仍在使用,还没被销毁,然后执行bar()函数。这个说法来源于MDN-闭包。还有种说法:闭包是指有权访问另外一个函数作用域中的变量的函数。

qq_42445025的博客 319

COCOS 3.8 接入高德定位SDK

/微信登录所必须的code。//是否获得code。

八喜互动 1993

利用闭包与高阶函数实现缓存函数的创建&&缓存函数在项目中的性能优化

允许函数保留其包含(封闭)范围中变量的访问权,即使在该范围外执行函数闭包是一种在函数嵌套的情况下,内部函数可以访问外部函数作用域中变量的现象。基本上,闭包就是将函数内部和函数外部连接起来的一座桥梁。闭包允许你从一个函数中返回另一个函数,并且在后者执行时仍然能够访问前者的变量。通俗地说,闭包是由函数和与其相关的引用环境组合而成的实体。它可以让函数访问在它被创建时所处的词法作用域中的变量,即使函数在这个词法作用域外执行,也能够访问这些变量。内部函数可以访问外部函数中的变量和参数。

Aan___的博客 1303

AI虚拟人物 数字人直播,不用出镜,不用露脸的直播方式(附教程 软件)

我们先来看一组数据,这是我通过蝉妈妈查到的某位虚拟主播的一个带货数据,粉丝还不到 3000 人,每次直播 1 到 4 个小时,30天的总销量达到了1200单,15天,总销售额 2 万,可能相对来说客单价还是比较低,最关键的是才直播了 6 场,如果说他能够一个月直播 20 天,每天花 4个小时去直播,当个副业来做,就可以达到最起码月入 3 万的一个收入水平,我个人觉得是非常的不错的。大家把商品图片放在背景,只要场景设置的不错,场景和产品的关联度高,直播的时候挂上购买链接也会有一定的成交。

. 7万+

Linux运维人,该醒醒了,2019年要变天了

外界都说运维只是处于被动低效率手工救火的状态,企业对其重视程度不高,提起运维很多人能联想到的字眼就有“苦逼”、“辛苦”、“加班”、“背锅”。在大数据、人工智能、容器技术的强力加持下,运维这个传统的技术工种已经从“机械劳动”这样的刻板印象中蜕变出来,成为了任何一家技术公司所必须依赖和大力投入的核心技术能力。 现在,运维人员的发展前景也开始让很多程序员眼羡,作为Linux运维人员的你:这一年,你的L...

qq_43083284的博客 1万+

是时候用闭包实现一个缓存函数了

什么是缓存函数 所谓缓存函数就是将函数运算过的结果缓存起来,这种做法是典型的用内存去换取性能的手段,常用于缓存数据计算结果和缓存对象。缓存只是一个临时的数据存储,它保存数据,以便将来对该数据的请求能够更快地得到处理。 记忆函数的使用场景 在客户前端页面中,有些数据(比如一些后续不常改变的数据),可以在第一次请求的时候全部拿过来保存在 js 对象中,以后需要的时候就不用每次都去请求服务器了。对于那些大量使用类似下拉框的页面,这种方法可以极大地减少对服务器的访问。可以提供便利,减少查询次数和所消耗的时间。 函

孤佣 415

小米汽车车载多摄像头视频链路深度解析 | MAX96792/MAX96724/MAX96781 实战分享

通过合理的输入输出规划 + 虚拟通道管理 + 加串行策略,可以在成本和性能之间取得最佳平衡,这就是多摄像头系统设计的核心精髓。

weixin_43199439的博客 1547

闭包缓存

一. 缓存:将常用的数据进行存储,以提升性能硬件缓存 浏览器缓存 CDN(content delivery network) //内容分发网络,使用缓存技术 内存性数据库(mongodb数据库) 二. 如何用闭包实现缓存:1.写一个闭包,在闭包中创建一个对象用来做缓存的存储对象 2.在闭包中创建一个数组,用来存储缓存中的键 3.返回一个函数,这个函数需要两个参数,一个是key,一个

DLGDark的博客 1090

基于fme和python-docx第三方库批量更新不动产调查表

最近应朋友需要批量更新在不动产成果中提交的各种word报表中的指定数据,根据初步的了解,我做了一个基于fme软件和python-docx第三方库进行一个批量的更新方案。

weixin_43229258的博客 1240

详细解释SigLIP的原理,为什么有些多模态模型喜欢用他?怎么应用于机器人控制呢

详细解释SigLIP的原理,为什么有些多模态模型喜欢用他?怎么应用于机器人控制呢。

具身机器人和通用AI 1230

3.使用闭包实现数据缓存

闭包:   闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在PHP、Scala、Scheme、Common Lisp、Small...

weixin_30815469的博客 329

Unity引擎开发:复杂动作系统_动作捕捉数据的应用与整合

通过以上步骤,我们可以在Unity中有效地应用和整合动作捕捉数据,以实现更加真实和流畅的动画效果。从导入文件格式的选择,到预处理和优化,再到实时应用和调试验证,每一步都至关重要。希望本文能帮助你在Unity项目中更好地利用动作捕捉数据,提升动画质量和性能。

chenlz2007的博客 958

python基础 — 闭包和装饰器

百度百科:维基百科:注意:闭包并不只是一个python中的概念,在函数式编程语言中广泛应用。哈哈看完有没有感觉云里雾里的的,看不懂对了,下面我们来拿Python中的闭包实例来感受下到底什么事闭包,在回过头来理解闭包的概念。(1)Python中闭包的特点(2)创建一个简单的闭包(3)闭包的简单应用问题:使用闭包计算一个数的 n 次幂。相信到这里,我们已经可以判断是否是闭包,自定义闭包并简单的使用闭包了,下面我们来浅析闭包的一些特性原理。(4)闭包的一些特性原理pass拓展被装饰函数功能的一种函数,可以在不改变

个人博客 1718

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

vue3中的computed源码浅析

computed源码浅析 前面通过对reactive的阅读了解了一下响应式系统的概貌。现在开始对computed进行一定的了解,学习和理解这其中的思路。 在vue3中computed以composition api的形式暴露出来,因此可以直接通过computed方法作为入口去阅读理解其中的思路。 // computed.ts // 函数重载 export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T

qq_39217871的博客 871

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

arg是什么函数_浅析函数式编程与前端

毕业三年,市面上听说过的互联网公司几乎都面试了一遍,函数式编程似乎是一个面试官很感兴趣的话题,几乎有一半的面试官都会问到对函数式编程的理解,尤其是这次回成都的几场面试。每每遇到这个问题,我总是简单的说几句自己的看法,然后快速转移话题企图掩盖自己对函数式编程的无知。本周的“思考沉淀”就回忆和总结一下对函数式编程的理解。什么是函数式编程我们常见的编程范式有两种:命令式和声明式,比如我们熟悉的面向对象思...

weixin_39574928的博客 462

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

浅析JavaScript设计模式——单例模式

单例模式 保证一个类仅有一个实例,并提供一个访问它的全局访问点 举一个通俗的例子,在页面中点击登录按钮,弹出了一个登录浮窗,这个登录浮窗是唯一的,无论我们单击多少次,浮窗只会创建一次其实我们可能无意中都会使用过单例模式,我们的做法往往都是使用一个变量来标志当前是否已经为某个类创建了对象, 如果true,那么下一次再想获得这个类的实例时,直接返回之前创建过的对象单例模式的核心是确保只有一个实例

尐轩web前端网络日志 3515

Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制。因此,V8 将内存(堆)分为新生代和老生代两部分。 一、前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存。垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带来的内存泄露问题。 但使用了垃圾回收即意味着程序员将无法掌控内存。ECMAScript没有...

weixin_30236595的博客 206

java加载node模块,浅析node.js的模块加载机制

在node.js中,模块使用CommonJS规范,一个文件是一个模块node.js中的模块可分为三类内部模块 - node.js提供的模块如 fs,http,path等自定模块 - 我们自己写的模块第三方模块 - 通过npm安装的模块node.js提供了大量的模块供我们使用,比如 想解析一个文件的路径,可以使用path模块下的相应方法实现:const path = require('path');...

weixin_36317800的博客 199

React Compiler 1.0浅析:从配置到优化

React Compiler 1.0发布半年后,主流框架已全面支持这一自动优化工具。它通过静态分析在编译阶段自动添加缓存,替代手动useMemo/useCallback,减轻开发者心智负担。新项目可直接使用,老项目需分四步迁移:先通过ESLint检测违规代码,再以annotation模式试点,确认稳定后全量开启,最后逐步清理旧缓存代码。

u012585691的博客 357

浅析Vue响应式原理(三)

Vue响应式原理之defineReactive defineReactive 不论如何,最终响应式数据都要通过defineReactive来实现,实际要借助ES5新增的Object.defineProperty。 defineReactive接受五个参数。obj是要添加响应式数据的对象;key是属性名,val是属性名对应的取值;customSetter是用户自定义的setter;会在响应式数据的s...

我的乌托 363

UE4热更新实战:基于UnLua的蓝图逻辑迁移与热更架构设计

在游戏开发中,热更新技术是解决线上BUG修复和内容迭代的关键。其核心原理是通过动态加载外部脚本或资源,在不重新编译和发布客户端的情况下更新游戏逻辑。这项技术对于需要快速响应玩家反馈和进行赛季更迭的网络游戏与手游具有极高的工程价值。UnLua作为UE4社区中成熟的Lua绑定框架,通过编译期生成绑定代码,在运行时将Lua虚拟机与UE4的C++对象体系高效连接,实现了接近原生性能的脚本化逻辑执行。它特别适用于需要将固化在蓝图或C++中的游戏逻辑(如技能系统、任务逻辑)动态化的应用场景。本文以实战为导向,深入解析如

weixin_33895516的博客 599

iOS商品详情、ffmpeg播放器、指示器集锦、自定义圆弧菜单、实用工具等源码

iOS精选源码 电商商品详情 Swift.两种方式实现tableViewCell拖拽功能 ffmpeg+openGL播放器 微信聊天表情雨、表情下落、表情动画 iOS指示器集锦 弹窗增加 pickerView 可交互、无限个数、支持回弹、自定义的圆弧菜单 LXFProtocolTool-swift协议方式实现功能的实用工具库【支持 Rx 】 MoyaMapper 快速解析模型工具,...

sunnyaigd830的博客 299
上一篇: 跨域post请求实现方案小结
下一篇: RPC发展脉络整理
doraeimo
博客等级 码龄20年 11粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值