使用easyYapi生成文档
easyYapi生成文档
- 背景
- 1.安装配置
- 1.1 介绍
- 1.2 安装
- 1.3 配置
- 1.3.1 Export Postman
- 1.3.2 Export Yapi
- 1.3.3 Export Markdown
- 1.3.4 Export Api
- 1.3.6 常见问题补充
- 2. java注释规范
- 2.1 接口注释规范
- 2.2 出入参注释规范
- 3. 特定化支持
- 3.1 必填校验
- 3.2 忽略导出
- 3.3 返回不一致
- 3.4 设置header
- 3.5 设置tag
- 3.6 设置open
- 3.7 序列化相关
- 4. 自定义配置
- 5. 问题
背景
因为公司业务需要接口自动化测试,所以需要针对所有java项目的后端接口进行完整的文档梳理并同步到yapi接口管理平台,在使用swagger实操过程中,发现了一款比较好用的yapi生成工具,特别好用,不仅支持无侵入的方式生成文档,还支持定制化处理。下面一起来就通过笔者的这篇博文来了解EasyYapi这款插件的使用吧。
1.安装配置
1.1 介绍
基于java注释生成api接口文档的idea插件。
-
代码零侵入
-
通过注释生成api接口文档
-
实时生成并同步相关接口平台
-
灵活的配置规则适应项目特性
官方手册 easyYapi
1.2 安装
支持以下IDE
- 2017.3及以上版本。
从IDEA仓库中安装
Preferences(Settings)-->Plugins>Browse repositories-->find"EasyYapi"-->Install Plugin

手动下载安装:
- 下载插件 Jetbrains or Github ->
Preferences(Settings)>Plugins>Install plugin from disk...
下载地址: https://plugins.jetbrains.com/
重启IDE
1.3 配置
插件安装完成,可以直接通过在某个类或者某个文件夹下 右键->easyYapi->exportYapi

1.3.1 Export Postman
导出为postman支持的接口信息。
-
直接导出: 导出为json格式的api接口信息 可以导入Postman中
-
配置postman的token导出: 通过在
Preferences—>Other Settings—>EasyApi中配置postman对应的token 会直接同步到postman上
配置token

postman token获取方式
https://martian-spaceship-950587.postman.co/integrations/service/pm_pro_api

效果

1.3.2 Export Yapi
导出并同步到yapi服务端。
- 直接导出
首次使用: 需要配置yapi服务端地址 ip+port 和对应yapi分类中的token
1). 配置服务地址

2) . 配置yapi对应分类的token

3). token来源获取

1.3.3 Export Markdown
导出为md文件、直接导出为md文件
1.3.4 Export Api
导出各种类型的请求信息,export api可以针对某个接口进行导出。
##### 1.3.5 call api
Call Api:生成调试工具 可以进行请求调试调用。

1.3.6 常见问题补充
- yapi、postman配置错误或者变更: 可通过
Preferences—>Other Settings—>EasyApi修改。

- 导出yapi时 , 每个module需要配置相应的token, 即对应一个yapi中的项目
2. java注释规范
2.1 接口注释规范
[] 表示可选操作
/*** 分类名称* [分类备注/描述]** [@module 归属项目]*/
@RestController
@RequestMapping(value = "/pathOfCtrl")
public class MockCtrl {/*** api名称* [api描述]* [@param param1 参数1的名称或描述] 对于get请求有作用@RequestParam或者@PathVariable有效* [@param param2 可以用`@link`来表示当前参数的取值是某个枚举{@link some.enum.or.constant.class}]* [@param param3 当目标枚举字段与当前字段名不一致,额外指定{@link some.enum.or.constant.class#property1}]* [@return 响应描述]*/@RequestMapping(value = "/pathOfApi1/${orderCode}")public Result methodName1(long param1,@RequestParam String param2,@RequestParam(required = false, defaultValue = "defaultValueOfParam3") String param3){...}/*** 默认使用`application/x-www-form-urlencoded`,* 对于`@RequestBody`将使用`application/json`** 可以用注解`@Deprecated`来表示api废弃* 也可以用注释`@deprecated`* [@deprecated 改用{@link #methodName3(String)} 只能引用同一个方法]* [@deprecated 改用{@link #yapi地址}或者{@see #yapi地址}]*/@Deprecated@RequestMapping(value = "/pathOfApi2")public Result methodName2(@RequestBody MockDtoOrVo jsonModel){...}}
- GET请求的入参@RequestParam或者@PathVariable 中在注释上必须使用@param、出参使用@return。才能生成正常入参文档。
2.2 出入参注释规范
public class MockDtoOrVo {/*** 字段注释*/private Long field1;/*** 使用@see来说明当前字段的取值是某个枚举* @see some.enum.or.constant.enum*/private int field3;/*** 当目标枚举字段与当前字段名不一致,额外指定* @see some.enum.or.constant.enum#property1*/private int field4;/*** 可以用注解`@Deprecated`来表示字段被废弃* 也可以用注释`@deprecated`* @deprecated It's a secret*/@Deprecatedprivate int field5;/*** 如果使用javax.validation的话* 可以使用@NotBlank/@NotNull表示字段必须*/@NotBlank@NotNullprivate String field6;//序列化名称 @JSONField(name="aaa")@JsonAlias("aaa")@JsonProperty("aaa")private String field7;...
}
- 字段是常量或者枚举值、可以使用@see 引用枚举,注意枚举、常量对应的类也需要写对应的注释
3. 特定化支持
yapi有对应的通用配置能大致满足我们通用接口的生成,但是针对项目中一些特殊接口,yapi也提供了灵活的运用配置规则 通过自定义配配置来适应项目特性以减少代码侵入。
- 默认支持的通用配置:
Preferences—>Other Settings—>EasyApi-->Recommend

3.1 必填校验
默认配置
field.required: 用于标记字段是否为必须。 默认支持javax.validation annotations。
param.required: 用于标记API参数是否为必须。 默认支持javax.validation annotations。
#get请求入参
param.required=@javax.validation.constraints.NotBlank
param.required=@javax.validation.constraints.NotNull
param.required=@javax.validation.constraints.NotEmpty#post请求入参
field.required=@javax.validation.constraints.NotBlank
field.required=@javax.validation.constraints.NotNull
field.required=@javax.validation.constraints.NotEmpty
//get请求 入参
@GetMapping("/update/get")
public Map<String, Object> getHttp(Integer fileId) {}//get请求 参数为path
@GetMapping("/update/get/{orderCode}")
public Map<String, Object> getHttpPath(@PathVariable(name = "orderCode") Integer orderCode) {//@NotBlank @NotEmpty
//get请求 参数为path
@GetMapping("/update/get")
public Map<String, Object> getHttp(@NotNull Integer fileId) {}//post请求入参属性生成必填
public class AreaUpdateDTO {/*** 区域对象数组*/@NotNull//@NotBlank//@NotEmptyprivate List<AreaDTO> areaList;}
get请求中@RequestParam、@PathVariable修饰的请求参数生成的文档都是必填
自定义配置
//open接口有这种方法使用 @Validated 如果继续使用@NotNull等注解会破坏现有接口
public ResultBody<ContractAddResultDTO4Open> chCarCorverContractAdd(@Validated @RequestBody CarCoverPromotionContractAddDto dto){
- 自定义注解
/*** 字段必填注解标识* @author xieqx*/
@Target({ElementType.FIELD, ElementType.PARAMETER,ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface CrmFieldRequired {
}
- 添加自定义配置规则
#设置字段必填 且支持使用自定义注解
field.required=@com.yiche.crm.base.annotations.CrmFieldRequiredparam.required=@com.yiche.crm.base.annotations.CrmFieldRequired
- 使用
//get请求
@GetMapping("/update/get")
public Map<String, Object> getHttp(@CrmRequired Integer fileId) {}//post请求入参属性生成必填
public class AreaUpdateDTO {/*** 区域对象数组*/@CrmRequiredprivate List<AreaDTO> areaList;
}
3.2 忽略导出
ignore: 整个类或者接口方法不导出。
/**
* Mock Apis
* @ignore 忽略当前类
*/
@RestController
@RequestMapping(value = "mock")
public class MockCtrl {/*** Mock String* @ignore 忽略当前api*/@GetMapping("/string")public String mockString() {return Result.success("mock string");}}
field.ignore:字段忽略不导出。
默认支持如下
#字段级别导出
field.ignore=@com.fasterxml.jackson.annotation.JsonIgnore#value
field.ignore=!@com.google.gson.annotations.Expose#serialize
- 自定义注解
/*** 字段忽略注解* @author xieqx*/
@Target({ElementType.FIELD, ElementType.PARAMETER,ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface CrmFieIdIgnore {
}
自定义配置
field.ignore=@com.yiche.crm.base.annotations.CrmFieIdIgnore
param.ignore=@com.yiche.crm.base.annotations.CrmFieIdIgnore
3.3 返回不一致
**method.return: ** 设置方法的返回值。
常用于以下情况:
- 方法响应统一封装
- 方法返回Object
- 方法返回类型中的泛型类型未明确
<Object>/<?>/<*> - 方法返回类型与实际响应无关, 例如通过操作HttpServletResponse来返回响应
目前特殊接口
crm系统中大部分的请求出参情况如下:
//1.返回泛型未明确
public ResultBody getSpecialApprovalAttach();//2.出参与响应不一致
@CommonResult
public String getHttpPath()
@CommonResult
public PageInfo<Student> getHttpPath()
@CommonResult
public List<Student> getHttpPath() //3.下载导出
@CommonResult
public void download(HttpserveletResponse resp)
自定义配置规则
#该配置的意思是 无论返回值是怎样的只需在注释中使用@real_return 后引入真实的对象类型即可
#it.doc("real_return") 中real_return自定义字符串即可(最好项目统一)
method.return[#real_return] =groovy: "com.yiche.crm.common.rest.ResultBody<" + helper.resolveLink(it.doc("real_return")) +">"#对于只返回单个字段
#method.return.main[groovy:it.returnType().isExtend("com.yiche.crm.common.rest.ResultBody")]=data
- 使用
//1.返回泛型未明确
/*** @real_return {@link String}*/
public ResultBody getSpecialApprovalAttach(){return ResultBody.ok("hello");
}//2.出参与响应不一致
/*** @real_return {@link String}*/
@CommonResult
public String getHttpPath() {return "hello"
}
/*** @real_return {@link PageInfo<Student>}*/
@CommonResult
public PageInfo<Student> getHttpPath()/*** @real_return {@link List<Student>}*/
@CommonResult
public List<Student> getHttpPath() //3.下载导出
/*** @real_return {@link void}* 或者* @real_return {@link com.alibaba.excel.EasyExcel}*/
@CommonResult
public void download(HttpserveletResponse resp)
3.4 设置header
method.additional.header: API需要额外的header 。
- 配置
#所有接口都需要设置如下header
#method.additional.header={name: "Authorization",value: "",desc: "认证Token",required:true, example:""}api.tag=@open_header# 特定的接口需要添加header
# 对于注释使用@open_header的接口 需要特定的header(主要针对crm系统open接口)
method.additional.header[groovy:it.hasDoc("open_header")]={name: "token",value: "",desc: "认证Token",required:true}
method.additional.header[groovy:it.hasDoc("open_header")]={name: "source",value: "",desc: "来源,接口调用方",required:true}# 对于注释使用@open_yxs_header的接口 需要特定的header(主要针对crm系统open-yxs接口)
method.additional.header[groovy:it.hasDoc("open_yxs_header")]={name: "x-access-ework-token",value: "",desc: "鉴权token",required:true}
- 使用
/**** @open_header 添加open_header配置的header* @open_yxs_header 添加open_yxs_header配置的header*/
@GetMapping("/update/download")
@CommonResult
public ResultBody download(HttpServletResponse response){}
3.5 设置tag
api.tag: 标记接口,脚本中可以使用it.getDoc(“tagNam”)获取到。
- 配置
#语法 [#标记的名字] --- 注释中写@tagLabel 在yapi的tag中显示tagName
api.tag[#tagLabel]=tagNameapi.tag=@open_header
- 使用
/**** @open_header 添加open_header配置的header* @deprecated 注释中使用*/
@Deprecated //java注解
@GetMapping("/update/download")
@CommonResult
public ResultBody download(HttpServletResponse response){}
3.6 设置open
api.tag: 标记接口是否公开,从yapi导出api的时候可以选择只导出开放接口的api。
- 配置
#配置方式
api.open=#open
api.open=#myTag
- 使用
/**** @open* 或者* @myTag*/
@GetMapping("/update/download")
public Map<String, Object> areaUpdateNotice(@RequestBody AreaUpdateDTO dto) {
3.7 序列化相关
字段名称与返回的类型不一致的问题
@JSONField(name="aaa")
@JsonAlias("aaa")
@JsonProperty("aaa")
private Integer status;
easyYapi默认支持@JsonProperty(“aaa”)的转换,其他转换需要配置
#支持@JSONField注解
field.name=@com.alibaba.fastjson.annotation.JSONField#name#支持@JsonAlias注解
field.name=@com.fasterxml.jackson.annotation.JsonAlias#value
4. 自定义配置
设置字段必填 且支持使用自定义注解
field.required=@com.yiche.crm.base.annotations.YapiRequired#是否开放接口 不设置默认 非开放
api.open=#open
api.open=#xiu#设置标签
#api.tag[#xiu]=my_tag
api.tag=@open_header
api.tag=@open_yxs_header# 对于注释使用@的接口 需要特定的header(主要针对crm系统open接口)
method.additional.header[groovy:it.hasDoc("open_header")]={name: "token",value: "",desc: "认证Token",required:true}
method.additional.header[groovy:it.hasDoc("open_header")]={name: "source",value: "",desc: "来源接口调用方",required:true}# 对于注释使用@open的接口 需要特定的header(主要针对crm系统open_yxs接口)
method.additional.header[groovy:it.hasDoc("open_yxs_header")]={name: "x-access-ework-token",value: "",desc: "易小鲨认证Token",required:true}#支持@JSONField注解
field.name=@com.alibaba.fastjson.annotation.JSONField#name#支持@JsonAlias注解
field.name=@com.fasterxml.jackson.annotation.JsonAlias#value
5. 问题
-
Map、JsonObject问题处理
-
必填校验,如果多个接口使用同一个入参,必填字段不一样使用必填注解也是有问题的
-
返回单个字段 无法设置注释
-
Postman 调试添加用例
-
yapi整合测试、mock、生成测试报告
相关文章:
使用easyYapi生成文档
easyYapi生成文档 背景1.安装配置1.1 介绍1.2 安装1.3 配置1.3.1 Export Postman1.3.2 Export Yapi1.3.3 Export Markdown1.3.4 Export Api1.3.6 常见问题补充 2. java注释规范2.1 接口注释规范2.2 出入参注释规范 3. 特定化支持3.1 必填校验3.2 忽略导出3.3 返回不一致3.4 设置…...
蓝桥杯练习题总结(三)线性dp题(摆花、数字三角形加强版)
目录 一、摆花 思路一: 确定状态: 初始化: 思路二: 确定状态: 初始化: 循环遍历: 状态转移方程: 二、数字三角形加强版 一、摆花 题目描述 小明的花店新开张,为了吸…...
Elasticsearch(15) multi_match的使用
elasticsearch version: 7.10.1 multi_match是Elasticsearch中的一种查询类型,允许在一个或多个字段上执行全文本搜索,并合并各个字段的结果得分。这种查询有助于实现跨多个字段的统一搜索体验。 语法 {"query": {"multi_m…...
nodejs的线程模型和libuv库的基本使用
文章目录 nodejs中集成addon本地代码的回调问题单线程事件驱动模型libuvlibuv基本框架addon中使用libuv代码nodejs中集成addon本地代码的回调问题 在C++的代码中,回调函数是一个基本的代码调用方式。而在我自己的开发实践中,需要在addon这样一个nodejs的本地化模块中实现一个…...
Uni-app/Vue/Js本地模糊查询,匹配所有字段includes和some方法结合使用e
天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.第一步 需要一个数组数据 {"week": "全部","hOutName": null,"weekendPrice": null,"channel": "门市价","hOutId": 98,"cTime": "…...
深度学习pytorch——激活函数损失函数(持续更新)
论生物神经元与神经网络中的神经元联系——为什么使用激活函数? 我们将生物体中的神经元与神经网络中的神经元共同分析。从下图可以看出神经网络中的神经元与生物体中的神经元有很多相似之处,由于只有刺激达到一定的程度人体才可以感受到刺激,…...
《苹果 iOS 应用开发与分发的关键问题解析》
一、背景 解决同事问的问题,来来回回被问好几次相同的问题,然后确认,我觉得不如写个文档 二、非研发人员安装iOS应用方式 TestFlightIPA 文件 对比 TestFlightIPA 文件安装方式TestFlight 是苹果提供的一个 beta 测试平台,开发者…...
爱上数据结构:顺序表和链表
一、线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条…...
python知识点总结(十)
python知识点总结十 1、装饰器的理解、并实现一个计时器记录执行性能,并且将执行结果写入日志文件中2、队列和栈的区别,并且用python实现3、设计实现遍历目录与子目录4、CPU处理进程最慢的情况通常发生在以下几种情况下:5、CPU处理线程最慢的…...
【Python】探索 Python 编程世界:常量、变量及数据类型解析
欢迎来CILMY23的博客 本篇主题为 探索 Python 编程世界:常量、变量及数据类型解析 个人主页:CILMY23-CSDN博客 Python系列专栏:http://t.csdnimg.cn/HqYo8 上一篇博客: http://t.csdnimg.cn/SEdbp C语言专栏: htt…...
vue页面实现左右div宽度,上下div高度分割线手动拖动高度或者宽度自动变化,两个div宽度或者高度拉伸调节,实现左右可拖动改变宽度的div内容显示区
实现左右或者上下div两部分拖动,宽度或者高度自动变化,实现流畅平滑的变化,还可以是实现拖动到一定宽度就不让拖动了,如果你不需要最小宽度,就直接去掉样式就行 这是页面。分左中右三部分,中间我是用来作为拖动的按钮…...
知攻善防应急靶场-Linux(1)
前言: 堕落了三个月,现在因为被找实习而困扰,着实自己能力不足,从今天开始 每天沉淀一点点 ,准备秋招 加油 注意: 本文章参考qax的网络安全应急响应和知攻善防实验室靶场,记录自己的学习过程&am…...
ffmpeg命令行
ffmpeg 如果要在linux gdb 调试,需要在configure 时候不优化 开启调试 ./configure --enable-debug --disable-optimizations make如何开启gdb 调试 gdb ffmpeg_gset args -i test.hevc -c:v copy -c:a copy output_265.mp4rh264 的流生成mp4 文件,不转…...
VMware虚拟机更换引导顺序
前言 我用wmware装了黑群晖测试,将img转成vmdisk的格式之后发现系统引导盘之后1G,有点太小了 我准备把wmware的黑群晖系统迁移到新添加的虚拟磁盘里 1.登录黑群晖的SSH 请先在黑群晖的控制面板中的终端机和SNMP里面启用SSH功能,才能使用ss…...
RAFT:让大型语言模型更擅长特定领域的 RAG 任务
RAFT(检索增强的微调)代表了一种全新的训练大语言模型(LLMs)以提升其在检索增强生成(RAG)任务上表现的方法。“检索增强的微调”技术融合了检索增强生成和微调的优点,目标是更好地适应各个特定领…...
Stable Diffusion 本地训练端口与云端训练端口冲突解决办法
方法之一,修改本地训练所用的端口 1 首先,进入脚本训练器的根目录 例如:C:\MarkDeng\lora-scripts-v1.7.3 找到gui.py 2 修改端口号 因为云端训练器也是占用28000和6006端口 那么本地改成27999和6007也是可以的 保存退出,运行启动…...
C++学习day1
思维导图 定义自己的命名空间,其中有string类型的变量,再定义两个函数,一个函数完成字符串的输入,一个函数完成求字符串长度,再定义一个全局函数完成对该字符串的反转 #include <iostream> using namespace std;…...
openGauss CM
CM 可获得性 本特性自openGauss 3.0.0版本开始引入。 特性简介 CM(Cluster Manager)是一款数据库管理软件,由cm_server和cm_agent组成。 cm_agent是部署在数据库每个主机上,用来启停和监控各个数据库实例进程的数据库管理组件…...
北斗短报文+4G应急广播系统:实时监控 自动预警 保护校园安全的新力量
安全无小事,生命重如山。学生是祖国的未来,校园安全是全社会安全工作的一个重要的组成部分。它直接关系到青少年学生能否安健康地成长,关系到千千万万个家庭的幸福安宁和社会稳定。 灾害事故和突发事件频频发生,给学生、教职员工…...
2024河北石家庄矿业矿山展览会|河北智慧矿山展会|河北矿博会
2024中国(石家庄)国际矿业博览会 时间:2024年7月4-6日 地点:石家庄国际会展中心.正定 随着全球经济的持续增长和矿产资源需求的不断攀升,矿业行业正迎来前所未有的发展机遇。作为矿业领域的盛会&…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...
