废弃fastjson!大型项目迁移Gson保姆级实战

前言

本篇文章是我这一个多月来帮助组内废弃fastjson框架的总结,我们将大部分Java仓库从fastjson迁移至了Gson。

这么做的主要的原因是公司受够了fastjson频繁的安全漏洞问题,每一次出现漏洞都要推一次全公司的fastjson强制版本升级,很令公司头疼。

文章的前半部分,我会简单分析各种json解析框架的优劣,并给出企业级项目迁移json框架的几种解决方案。

在文章的后半部分,我会结合这一个月的经验,总结下Gson的使用问题,以及fastjson迁移到Gson踩过的深坑。

文章目录:

  • 为何要放弃fastjson?

  • fastjson替代方案

    • 三种json框架的特点

    • 性能对比

    • 最终选择方案

  • 替换依赖时的注意事项

    • 谨慎,谨慎,再谨慎

    • 做好开发团队和测试团队的沟通

    • 做好回归/接口测试

    • 考虑迁移前后的性能差异

  • 使用Gson替换fastjson

    • Json反序列化

    • 范型处理

    • List/Map写入

    • 驼峰与下划线转换

  • 迁移常见问题踩坑

    • Date序列化方式不同

    • SpringBoot异常

    • Swagger异常

    • @Mapping JsonObject作为入参异常

注意:是否使用fastjson是近年来一个争议性很大的话题,本文无意讨论框架选型的对错,只关注迁移这件事中遇到的问题进行反思和思考。大家如果有想发表的看法,可以在评论区 理 性 讨论。

本文阅读大概需要:5分钟

码字不易,欢迎关注我的个人公众号:后端技术漫谈(二维码见文章底部)

为何要放弃fastjson?

究其原因,是fastjson漏洞频发,导致了公司内部需要频繁的督促各业务线升级fastjson版本,来防止安全问题。

fastjson在2020年频繁暴露安全漏洞,此漏洞可以绕过autoType开关来实现反序列化远程代码执行并获取服务器访问权限。

从2019年7月份发布的v1.2.59一直到2020年6月份发布的 v1.2.71 ,每个版本的升级中都有关于AutoType的升级,涉及13个正式版本。

fastjson中与AutoType相关的版本历史:

1.2.59发布,增强AutoType打开时的安全性 fastjson
1.2.60发布,增加了AutoType黑名单,修复拒绝服务安全问题 fastjson
1.2.61发布,增加AutoType安全黑名单 fastjson
1.2.62发布,增加AutoType黑名单、增强日期反序列化和JSONPath fastjson
1.2.66发布,Bug修复安全加固,并且做安全加固,补充了AutoType黑名单 fastjson
1.2.67发布,Bug修复安全加固,补充了AutoType黑名单 fastjson
1.2.68发布,支持GEOJSON,补充了AutoType黑名单
1.2.69发布,修复新发现高危AutoType开关绕过安全漏洞,补充了AutoType黑名单
1.2.70发布,提升兼容性,补充了AutoType黑名单
1.2.71发布,补充安全黑名单,无新增利用,预防性补充

相比之下,其他的json框架,如Gson和Jackson,漏洞数量少很多,高危漏洞也比较少,这是公司想要替换框架的主要原因。

fastjson替代方案

本文主要讨论Gson替换fastjson框架的实战问题,所以在这里不展开详细讨论各种json框架的优劣,只给出结论。

经过评估,主要有Jackson和Gson两种json框架放入考虑范围内,与fastjson进行对比。

三种json框架的特点

FastJson

速度快

fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越。

使用广泛

fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。

测试完备

fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个。每次发布都会进行回归测试,保证质量稳定。

使用简单

fastjson的API十分简洁。

Jackson

容易使用 - jackson API提供了一个高层次外观,以简化常用的用例。

无需创建映射 - API提供了默认的映射大部分对象序列化。

性能高 - 快速,低内存占用,适合大型对象图表或系统。

干净的JSON - jackson创建一个干净和紧凑的JSON结果,这是让人很容易阅读。

不依赖 - 库不需要任何其他的库,除了JDK。

Gson

提供一种机制,使得将Java对象转换为JSON或相反如使用toString()以及构造器(工厂方法)一样简单。

允许预先存在的不可变的对象转换为JSON或与之相反。

允许自定义对象的表现形式

支持任意复杂的对象

输出轻量易读的JSON

性能对比

同事撰写的性能对比源码:

https://github.com/zysrxx/json-comparison

本文不详细讨论性能的差异,毕竟这其中涉及了很多各个框架的实现思路和优化,所以只给出结论:

1.序列化单对象性能Fastjson > Jackson > Gson,其中Fastjson和Jackson性能差距很小,Gson性能较差

2.序列化大对象性能Jackson> Fastjson > Gson ,序列化大Json对象时Jackson> Gson > Fastjson,Jackson序列化大数据时性能优势明显

3.反序列化单对象性能 Fastjson > Jackson > Gson , 性能差距较小

4.反序列化大对象性能 Fastjson > Jackson > Gson , 性能差距较很小

最终选择方案

  • Jackson适用于高性能场景,Gson适用于高安全性场景

  • 对于新项目仓库,不再使用fastjson。对于存量系统,考虑到Json更换成本,由以下几种方案可选:

    • 项目未使用autoType功能,建议直接切换为非fastjson,如果切换成本较大,可以考虑继续使用fastjson,关闭safemode。

    • 业务使用了autoType功能,建议推进废弃fastjson。

替换依赖注意事项

企业项目或者说大型项目的特点:

  • 代码结构复杂,团队多人维护。

  • 承担重要线上业务,一旦出现严重bug会导致重大事故。

  • 如果是老项目,可能缺少文档,不能随意修改,牵一发而动全身。

  • 项目有很多开发分支,不断在迭代上线。

所以对于大型项目,想要做到将底层的fastjson迁移到gson是一件复杂且痛苦的事情,其实对于其他依赖的替换,也都一样。

我总结了如下几个在替换项目依赖过程中要特别重视的问题。

谨慎,谨慎,再谨慎

再怎么谨慎都不为过,如果你要更改的项目是非常重要的业务,那么一旦犯下错误,代价是非常大的。并且,对于业务方和产品团队来说,没有新的功能上线,但是系统却炸了,是一件“无法忍受”的事情。尽管你可能觉得很委屈,因为只有你或者你的团队知道,虽然业务看上去没变化,但是代码底层已经发生了翻天覆地的变化。

所以,谨慎点!

做好开发团队和测试团队的沟通

在依赖替换的过程中,需要做好项目的规划,比如分模块替换,严格细分排期。

把前期规划做好,开发和测试才能有条不紊的进行工作。

开发之间,需要提前沟通好开发注意事项,比如依赖版本问题,防止由多个开发同时修改代码,最后发现使用的版本不同,接口用法都不同这种很尴尬,并且要花额外时间处理的事情。

而对于测试,更要事先沟通好。一般来说,测试不会太在意这种对于业务没有变化的技术项目,因为既不是优化速度,也不是新功能。但其实迁移涉及到了底层,很容易就出现BUG。要让测试团队了解更换项目依赖,是需要大量的测试时间投入的,成本不亚于新功能,让他们尽量重视起来。

做好回归/接口测试

上面说到测试团队需要投入大量工时,这些工时主要都用在项目功能的整体回归上,也就是回归测试。

当然,不只是业务回归测试,如果有条件的话,要做接口回归测试。

如果公司有接口管理平台,那么可以极大提高这种项目测试的效率。

打个比方,在一个模块修改完成后,在测试环境(或者沙箱环境),部署一个线上版本,部署一个修改后的版本,直接将接口返回数据进行对比。一般来说是Json对比,网上也有很多的Json对比工具:

https://www.sojson.com/

考虑迁移前后的性能差异

正如上面描述的Gson和Fastjson性能对比,替换框架需要注意框架之间的性能差异,尤其是对于流量业务,也就是高并发项目,响应时间如果发生很大的变化会引起上下游的注意,导致一些额外的后果。

使用Gson替换Fastjson

这里总结了两种json框架常用的方法,贴出详细的代码示例,帮助大家快速的上手Gson,无缝切换!

Json反序列化

String jsonCase = "[{\"id\":10001,\"date\":1609316794600,\"name\":\"小明\"},{\"id\":10002,\"date\":1609316794600,\"name\":\"小李\"}]";// fastjson
JSONArray jsonArray = JSON.parseArray(jsonCase);
System.out.println(jsonArray);
System.out.println(jsonArray.getJSONObject(0).getString("name"));
System.out.println(jsonArray.getJSONObject(1).getString("name"));
// 输出:
// [{"date":1609316794600,"name":"小明","id":10001},{"date":1609316794600,"name":"小李","id":10002}]
// 小明
// 小李// Gson
JsonArray jsonArrayGson = gson.fromJson(jsonCase, JsonArray.class);
System.out.println(jsonArrayGson);
System.out.println(jsonArrayGson.get(0).getAsJsonObject().get("name").getAsString());
System.out.println(jsonArrayGson.get(1).getAsJsonObject().get("name").getAsString());
// 输出:
// [{"id":10001,"date":1609316794600,"name":"小明"},{"id":10002,"date":1609316794600,"name":"小李"}]
// 小明
// 小李

看得出,两者区别主要在get各种类型上,Gson调用方法有所改变,但是变化不大。

那么,来看下空对象反序列化会不会出现异常:

String jsonObjectEmptyCase = "{}";// fastjson
JSONObject jsonObjectEmpty = JSON.parseObject(jsonObjectEmptyCase);
System.out.println(jsonObjectEmpty);
System.out.println(jsonObjectEmpty.size());
// 输出:
// {}
// 0// Gson
JsonObject jsonObjectGsonEmpty = gson.fromJson(jsonObjectEmptyCase, JsonObject.class);
System.out.println(jsonObjectGsonEmpty);
System.out.println(jsonObjectGsonEmpty.size());
// 输出:
// {}
// 0

没有异常,开心。

看看空数组呢,毕竟[]感觉比{}更加容易出错。

String jsonArrayEmptyCase = "[]";// fastjson
JSONArray jsonArrayEmpty = JSON.parseArray(jsonArrayEmptyCase);
System.out.println(jsonArrayEmpty);
System.out.println(jsonArrayEmpty.size());
// 输出:
// []
// 0// Gson
JsonArray jsonArrayGsonEmpty = gson.fromJson(jsonArrayEmptyCase, JsonArray.class);
System.out.println(jsonArrayGsonEmpty);
System.out.println(jsonArrayGsonEmpty.size());
// 输出:
// []
// 0

两个框架也都没有问题,完美解析。

范型处理

解析泛型是一个非常常用的功能,我们项目中大部分fastjson代码就是在解析json和Java Bean。

// 实体类
User user = new User();
user.setId(1L);
user.setUserName("马云");// fastjson
List<User> userListResultFastjson = JSONArray.parseArray(JSON.toJSONString(userList), User.class);
List<User> userListResultFastjson2 = JSON.parseObject(JSON.toJSONString(userList), new TypeReference<List<User>>(){});
System.out.println(userListResultFastjson);
System.out.println("userListResultFastjson2" + userListResultFastjson2);
// 输出:
// userListResultFastjson[User [Hash = 483422889, id=1, userName=马云], null]
// userListResultFastjson2[User [Hash = 488970385, id=1, userName=马云], null]// Gson
List<User> userListResultTrue = gson.fromJson(gson.toJson(userList), new TypeToken<List<User>>(){}.getType());
System.out.println("userListResultGson" + userListResultGson);
// 输出:
// userListResultGson[User [Hash = 1435804085, id=1, userName=马云], null]

可以看出,Gson也能支持泛型。

List/Map写入

这一点fastjson和Gson有区别,Gson不支持直接将List写入value,而fastjson支持。

所以Gson只能将List解析后,写入value中,详见如下代码:

// 实体类
User user = new User();
user.setId(1L);
user.setUserName("马云");// fastjson
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("user", user);
jsonObject1.put("userList", userList);
System.out.println(jsonObject1);
// 输出:
// {"userList":[{"id":1,"userName":"马云"},null],"user":{"id":1,"userName":"马云"}}// Gson
JsonObject jsonObject = new JsonObject();
jsonObject.add("user", gson.toJsonTree(user));
System.out.println(jsonObject);
// 输出:
// {"user":{"id":1,"userName":"马云"},"userList":[{"id":1,"userName":"马云"},null]}

如此一来,Gson看起来就没有fastjson方便,因为放入List是以gson.toJsonTree(user)的形式放入的。这样就不能先入对象,在后面修改该对象了。(有些同学比较习惯先放入对象,再修改对象,这样的代码就得改动)

驼峰与下划线转换

驼峰转换下划线依靠的是修改Gson的序列化模式,修改为LOWER_CASE_WITH_UNDERSCORES

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gsonUnderScore = gsonBuilder.create();
System.out.println(gsonUnderScore.toJson(user));
// 输出:
// {"id":1,"user_name":"马云"}

常见问题排雷

下面整理了我们在公司项目迁移Gson过程中,踩过的坑,这些坑现在写起来感觉没什么技术含量。但是这才是我写这篇文章的初衷,帮助大家把这些很难发现的坑避开。

这些问题有的是在测试进行回归测试的时候发现的,有的是在自测的时候发现的,有的是在上线后发现的,比如Swagger挂了这种不会去测到的问题。

Date序列化方式不同

不知道大家想过一个问题没有,如果你的项目里有缓存系统,使用fastjson写入的缓存,在你切换Gson后,需要用Gson解析出来。所以就一定要保证两个框架解析逻辑是相同的,但是,显然这个愿望是美好的。

在测试过程中,发现了Date类型,在两个框架里解析是不同的方式。

  • fastjson:Date直接解析为Unix

  • Gson:直接序列化为标准格式Date

导致了Gson在反序列化这个json的时候,直接报错,无法转换为Date。

解决方案:

新建一个专门用于解析Date类型的类:

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;import java.io.IOException;
import java.util.Date;public class MyDateTypeAdapter extends TypeAdapter<Date> {@Overridepublic void write(JsonWriter out, Date value) throws IOException {if (value == null) {out.nullValue();} else {out.value(value.getTime());}}@Overridepublic Date read(JsonReader in) throws IOException {if (in != null) {return new Date(in.nextLong());} else {return null;}}
}

接着,在创建Gson时,把他放入作为Date的专用处理类:

Gson gson = new GsonBuilder().registerTypeAdapter(Date.class,new MyDateTypeAdapter()).create();

这样就可以让Gson将Date处理为Unix。

当然,这只是为了兼容老的缓存,如果你觉得你的仓库没有这方面的顾虑,可以忽略这个问题。

SpringBoot异常

切换到Gson后,使用SpringBoot搭建的Web项目的接口直接请求不了了。报错类似:

org.springframework.http.converter.HttpMessageNotWritableException

因为SpringBoot默认的Mapper是Jackson解析,我们切换为了Gson作为返回对象后,Jackson解析不了了。

解决方案:

application.properties里面添加:

#Preferred JSON mapper to use for HTTP message conversion
spring.mvc.converters.preferred-json-mapper=gson

Swagger异常

这个问题和上面的SpringBoot异常类似,是因为在SpringBoot中引入了Gson,导致 swagger 无法解析 json。

采用类似下文的解决方案(添加Gson适配器):

http://yuyublog.top/2018/09/03/springboot%E5%BC%95%E5%85%A5swagger/

  1. GsonSwaggerConfig.java

@Configuration
public class GsonSwaggerConfig {//设置swagger支持gson@Beanpublic IGsonHttpMessageConverter IGsonHttpMessageConverter() {return new IGsonHttpMessageConverter();}
}
  1. IGsonHttpMessageConverter.java

public class IGsonHttpMessageConverter extends GsonHttpMessageConverter {public IGsonHttpMessageConverter() {//自定义Gson适配器super.setGson(new GsonBuilder().registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()).serializeNulls()//空值也参与序列化.create());}
}
  1. SpringfoxJsonToGsonAdapter.java

public class SpringfoxJsonToGsonAdapter implements JsonSerializer<Json> {@Overridepublic JsonElement serialize(Json json, Type type, JsonSerializationContext jsonSerializationContext) {return new JsonParser().parse(json.value());}
}

@Mapping JsonObject作为入参异常

有时候,我们会在入参使用类似:

public ResponseResult<String> submitAudit(@RequestBody JsonObject jsonObject) {}

如果使用这种代码,其实就是使用Gson来解析json字符串。但是这种写法的风险是很高的,平常请大家尽量避免使用JsonObject直接接受参数。

在Gson中,JsonObject若是有数字字段,会统一序列化为double,也就是会把count = 0这种序列化成count = 0.0

为何会有这种情况?简单的来说就是Gson在将json解析为Object类型时,会默认将数字类型使用double转换。

如果Json对应的是Object类型,最终会解析为Map<String, Object>类型;其中Object类型跟Json中具体的值有关,比如双引号的""值翻译为STRING。我们可以看下数值类型(NUMBER)全部转换为了Double类型,所以就有了我们之前的问题,整型数据被翻译为了Double类型,比如30变为了30.0。

可以看下Gson的ObjectTypeAdaptor类,它继承了Gson的TypeAdaptor抽象类:

具体的源码分析和原理阐述,大家可以看这篇拓展阅读:

https://www.jianshu.com/p/eafce9689e7d

解决方案:

第一个方案:把入参用实体类接收,不要使用JsonObject

第二个方案:与上面的解决Date类型问题类似,自己定义一个Adaptor,来接受数字,并且处理。这种想法我觉得可行但是难度较大,可能会影响到别的类型的解析,需要在设计适配器的时候格外注意。

总结

这篇文章主要是为了那些需要将项目迁移到Gson框架的同学们准备的。

一般来说,个人小项目,是不需要费这么大精力去做迁移,所以这篇文章可能目标人群比较狭窄。

但文章中也提到了不少通用问题的解决思路,比如怎么评估迁移框架的必要性。其中需要考虑到框架兼容性,两者性能差异,迁移耗费的工时等很多问题。

希望文章对你有所帮助。

参考

《如何从Fastjson迁移到Gson》

https://juejin.im/post/6844904089281626120

《FastJson迁移至Jackson》此文作者自己封装了工具类来完成迁移

https://mxcall.github.io/posts/%E5%B7%A5%E4%BD%9C/%E7%A8%8B%E5%BA%8F%E5%91%98/javaSE/FastJson%E8%BF%81%E7%A7%BB%E8%87%B3Jackson/

《你真的会用Gson吗?Gson使用指南》

https://www.jianshu.com/p/e740196225a4

json性能对比

https://github.com/zysrxx/json-comparison/tree/master/src/main/java/json/comparison

fastjson官方文档

https://github.com/alibaba/fastjson/wiki

易百教程

https://www.yiibai.com/jackson

往期精彩文章:

绝了,几款主流的 JSON 库性能对比!

Java中常用的4个Json库,哪个性能更牛逼?

这个 bug 让我更加理解 Spring 单例了

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

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

相关文章

学到了!MySQL 8 新增的「隐藏索引」真不错

MySQL 8.0 虽然发布很久了&#xff0c;但可能大家都停留在 5.7.x&#xff0c;甚至更老&#xff0c;其实 MySQL 8.0 新增了许多重磅新特性&#xff0c;比如栈长今天要介绍的 "隐藏索引" 或者 "不可见索引"。隐藏索引是什么鬼&#xff1f; 隐藏索引 字面意思…

iOS开发-自定义UIAlterView(iOS 7)

App中不可能少了弹框&#xff0c;弹框是交互的必要形式&#xff0c;使用起来也非常简单&#xff0c;不过最近需要自定义一个弹框&#xff0c;虽然iOS本身的弹框已经能满足大部分的需求&#xff0c;但是不可避免还是需要做一些自定义的工作。iOS7之前是可以自定义AlterView的&am…

Spring 事务失效的 8 大场景,面试官直呼666...

前几天发了一篇文章里面有一个关于事务失效的问题&#xff1a;用 Spring 的 Transactional 注解控制事务有哪些不生效的场景&#xff1f;其中有个热心粉丝留言分享了下&#xff0c;我觉得总结得有点经验&#xff0c;给置顶了&#xff1a;但是我觉得还是总结得不够全&#xff0c…

认真聊一下MySQL索引的底层实现!

前言当我们发现SQL执行很慢的时候&#xff0c;自然而然想到的就是加索引&#xff0c;当然他也是高频的面试问题&#xff0c;所以今天我们一起来学习一下MySQL索引的底层实现&#xff1a;B树。树简介、树种类B-树、B树简介B树插入B树查找B树删除B树经典面试题树的简介树的简介树…

最新大厂面试真题集锦

年后又是一波求职季&#xff0c;正是“金三银四”这个求职黄金期&#xff0c;很多人扎堆在这个时间段跳槽&#xff0c;找工作&#xff0c;程序员也不例外。春节刚过&#xff0c;各公司企业都开始启动了新一年的招聘计划&#xff0c;招聘岗位倍增&#xff0c;求职人数远超于岗位…

使用OpenCV在Python中进行人脸和眼睛检测

Modules Used: 使用的模块&#xff1a; python-opencv(cv2)python-opencv(cv2) python-opencv(cv2) Opencv(Open source computer vision) is a python library that will help us to solve computer vision problems. Opencv(开源计算机视觉)是一个Python库&#xff0c;可帮…

Java打造一款SSH客户端,已开源!

最近由于项目需求&#xff0c;项目中需要实现一个WebSSH连接终端的功能&#xff0c;由于自己第一次做这类型功能&#xff0c;所以首先上了GitHub找了找有没有现成的轮子可以拿来直接用&#xff0c;当时看到了很多这方面的项目&#xff0c;例如&#xff1a;GateOne、webssh、she…

我去,这几个Linux指令太装B了|动图展示

1. sl先看一下呼啸而过的火车&#xff1b;安装指令如下&#xff1b;sduo apt-get install sl执行结果如下&#xff1a;2. htop图形化Linux系统性能监测工具&#xff0c;屌不屌:安装指令如下:sduo apt-get install htop执行结果如下&#xff1b;3. gcp以前用cp复制文件总是看不懂…

书店POS机--细化迭代2--测试

2019独角兽企业重金招聘Python工程师标准>>> (1) 开始一次新的销售&#xff0c;点击书店POS系统的销售&#xff1a; (2) 进入销售模块之后的界面如下&#xff1a; (3)逐条录入商品条目(根据商品编号)&#xff0c;并修改数量。确认无误之后点击“确认”按钮&#x…

Google Guava,牛逼的脚手架

01、前世今生你好呀&#xff0c;我是 Guava。1995 年的时候&#xff0c;我的“公明”哥哥——Java 出生了。经过 20 年的发展&#xff0c;他已经成为世界上最流行的编程语言了&#xff0c;请允许我有失公允的把“之一”给去了。虽然他时常遭受着各种各样的吐槽&#xff0c;但他…

阿里巴巴Druid,轻松实现MySQL数据库加密!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;为什么要加密&#xff1f;现在的开发习惯&#xff0c;无论是公司的项目还是个人的项目&#xff0c;都会选择将源码上传到 Gi…

计算机图形学图形旋转_计算机图形学中的平板显示

计算机图形学图形旋转平板显示器 (Flat Panel Display) It is generally known as FPD, the flat-panel display is such a display technology which overtakes Cathode Ray Tube as a new standard of computer desktop displays. Unlike monitors through CRT, flat-panel d…

一文掌握Redisson分布式锁原理|干货推荐

ReentrantLock 重入锁在说 Redisson 之前我们先来说一下 JDK 可重入锁: ReentrantLockReentrantLock 保证了 JVM 共享资源同一时刻只允许单个线程进行操作实现思路ReentrantLock 内部公平锁与非公平锁继承了 AQS[AbstractQueuedSynchronizer]1、AQS 内部通过 volatil 修饰的 in…

7种分布式事务的解决方案,一次讲给你听

本文约5300字&#xff0c;阅读时长「5分钟」什么是分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器「分别位于不同的分布式系统的不同节点之上」。一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操…

css @media 响应式布局

2019独角兽企业重金招聘Python工程师标准>>> &#xfeff;1、在 html 标签中 <link rel"stylesheet" type"text/css" href"style1.css" media"screen and (min-width: 600px) and (max-width: 800px)"> 2、在样式表中…

Apache JK Tomcat 集群问题

2019独角兽企业重金招聘Python工程师标准>>> 这几天被集群并发问题快折腾死了&#xff0c;望哪位高人看下到底是哪里出现了问题。 Apache Server是正常的&#xff0c;各服务器的Tomcat 也是正常的&#xff0c;但当Apache的连接数达到 300左右的时候&#xff0c;JK就…

Redis实现分布式锁的7种方案,及正确使用姿势!

种方案前言日常开发中&#xff0c;秒杀下单、抢红包等等业务场景&#xff0c;都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开&#xff0c;跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方&#xff0c;欢迎大家指出哈&#xff0c;一起…

Android软件开发之盘点所有Dialog对话框大合集(一)

转&#xff1a;http://xys289187120.blog.51cto.com/3361352/657562/ 雨松MOMO带大家盘点Android 中的对话框 今天我用自己写的一个Demo 和大家详细介绍一个Android中的对话框的使用技巧。 1.确定取消对话框 对话框中有2个按钮 通过调用 setPositiveButton 方法 和 setNegat…

PHP将数组存入数据库中的四种方式

PHP将数组存入数据库中的四种方式 最近突然遇到了一个问题&#xff0c;如何用PHP将数组存入到数据库中&#xff0c;经过自己的多方查找和研究&#xff0c;总结了以下四种方法&#xff1a;1.implode()和explode()方式2.print_r()和自定义函数方式3.serialize()和unserialize()方…

Android开发:利用Activity的Dialog风格完成弹出框设计

转&#xff1a;http://www.linuxidc.com/Linux/2011-08/41933.htm 在我们使用Dialog时&#xff0c;如果需要用到很多自己设计的控件&#xff0c;虽然可以让弹出框显示出我们需要的界面&#xff0c;但却无法找到地方完成控制代码的编写&#xff0c;如何解决这个问题呢,我们可以将…