Java代码规范-精简版
前端开发规范编码规范目录结构示意开发在各个模块的src目录下进行,以system模块为例:containers 存放前端的页面stores 存放前端页面所需的数据assets 存放样式表和图片资源common 存放公共的配置文件components 存放的是公共的组件local 存放多语言文件命名(1)模块命名...
现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅 是编程知识点,其它维度的知识点也会影响到软件的最终交付质量。
比如:五花八门的错误码人为地 增加排查问题的难度;数据库的表结构和索引设计缺陷带来的系统架构缺陷或性能风险;工程结构混乱导致后续项目维护艰难;没有鉴权的漏洞代码易被黑客攻击等等。
本代码规范基于《阿里巴巴Java开发手册》,根据公司目前现有人员的精力以及能力,以及目前现有代码所存在的问题,进行了一些精简。由于《阿里巴巴Java开发手册》开源,所以本文档同样遵循开源协议。
精简精简精简,所以本文档中的每一条都非常重要,请严格遵守。
一、专有名词解释
- POJO(Plain Ordinary Java Object): 在本规约中,POJO 专指只有 setter/getter/toString 的 简单类,包括 DO/DTO/BO/VO 等。
- DO(Data Object):阿里巴巴专指数据库表一一对应的 POJO 类。此对象与数据库表结构一 一对应,通过 DAO 层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
- BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
- Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
- VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
- CAS(Compare And Swap):解决多线程并行情况下使用锁造成性能损耗的一种机制,这是 硬件实现的原子操作。CAS 操作包含三个操作数:内存位置、预期原值和新值。如果内存位 置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何 操作。
- GAV(GroupId、ArtifactId、Version): Maven 坐标,是用来唯一标识 jar 包。
- OOP(Object Oriented Programming): 本文泛指类、对象的编程处理方式。
- AQS(AbstractQueuedSynchronizer): 利用先进先出队列实现的底层同步工具类,它是很多上 层同步实现类的基础,比如:ReentrantLock、CountDownLatch、Semaphore 等,它们通 过继承 AQS 实现其模版方法,然后将 AQS 子类作为同步组件的内部类,通常命名为 Sync。
- .ORM(Object Relation Mapping): 对象关系映射,对象领域模型与底层数据之间的转换,本 文泛指 iBATIS, mybatis 等框架。
- NPE(java.lang.NullPointerException): 空指针异常。
- OOM(Out Of Memory): 源于 java.lang.OutOfMemoryError,当 JVM 没有足够的内存 来为对象分配空间并且垃圾回收器也无法回收空间时,系统出现的严重状况。
- 一方库: 本工程内部子项目模块依赖的库(jar 包)。
- 二方库: 公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar 包)。
- 三方库: 公司之外的开源库(jar 包)。
二、设计规约
-
存储方案和底层数据结构的设计获得评审一致通过,并沉淀成为文档。
说明:有缺陷的底层数据结构容易导致系统风险上升,可扩展性下降,重构成本也会因历史数据迁移和系 统平滑过渡而陡然增加,所以,存储方案和数据结构需要认真地进行设计和评审,生产环境提交执行后, 需要进行 double check。
正例:评审内容包括存储介质选型、表结构设计能否满足技术方案、存取性能和存储空间能否满足业务发 展、表或字段之间的辩证关系、字段名称、字段类型、索引等;数据结构变更(如在原有表中新增字段) 也需要进行评审通过后上线。
-
在需求分析阶段,如果与系统交互的 User 超过一类并且相关的 User Case 超过 5 个, 使用用例图来表达更加清晰的结构化需求。
-
如果某个业务对象的状态超过 3 个,使用状态图来表达并且明确状态变化的各个触发 条件。
-
如果系统中某个功能的调用链路上的涉及对象超过 3 个,使用时序图来表达并且明确 各调用环节的输入与输出。
-
类在设计与实现时要符合单一原则。
-
系统设计阶段,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护。
-
系统设计阶段,注意对扩展开放,对修改闭合。
-
系统设计阶段,共性业务或公共行为抽取出来公共模块、公共配置、公共类、公共方 法等,在系统中不出现重复代码的情况,即 DRY 原则(Don’t Repeat Yourself)。
-
避免如下误解:敏捷开发 = 讲故事 + 编码 + 发布。
说明:敏捷开发是快速交付迭代可用的系统,省略多余的设计方案,摒弃传统的审批流程,但核心关键点上 的必要设计和文档沉淀是需要的。
反例:某团队为了业务快速发展,敏捷成了产品经理催进度的借口,系统中均是勉强能运行但像面条一样 的代码,可维护性和可扩展性极差,一年之后,不得不进行大规模重构,得不偿失。
-
代码即文档的观点是错误的,清晰的代码只是文档的某个片断,而不是全部。
说明:代码的深度调用,模块层面上的依赖关系网,业务场景逻辑,非功能性需求等问题是需要相应的文 档来完整地呈现的。
三、MySQL数据库
(一)建表规约
-
表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。 注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置从 is_xxx 到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含 义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
-
表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、 字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name 反例:AliyunAdmin,rdcConfig,level_3_name
-
表名不使用复数名词。
-
禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
-
主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
-
小数类型为 decimal,禁止使用 float 和 double。
-
如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
-
varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度 大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效 率。
-
表必备三字段:id, create_time, update_time。
-
表的命名最好是遵循“业务名称_表的作用”。
-
如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
-
字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1) 不是频繁修改的字段。
2) 不是唯一索引的字段。
3) 不是 varchar 超长字段,更不能是 text 字段。
-
单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
-
合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索 速度。
(二)索引规约
- 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
- 超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时, 保证被关联的字段需要有索引。
- 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
- 建组合索引的时候,区分度最高的在最左边。
四、安全规约
-
隶属于用户个人的页面或者功能必须进行权限控制校验。
说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容。
-
用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
-
用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库。
-
禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。
-
在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的机 制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。
说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并 造成短信平台资源浪费。
-
用户请求传入的任何参数必须做有效性验证。
说明:忽略参数校验可能导致:
⚫ page size 过大导致内存溢出
⚫ 恶意 order by 导致数据库慢查询
⚫ 缓存击穿
⚫ SSRF
⚫ 任意重定向
⚫ SQL 注入,Shell 注入,反序列化注入
⚫ 正则输入源串拒绝服务 ReDoS
五、编程规约
(一)命名风格
-
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例:_name / name / n a m e / n a m e / n a m e name / name_ / name name/name/name / name
-
所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,纯拼音命名方式更要避免采用。
正例:ali / alibaba / taobao / cainiao/ aliyun/ youku / hangzhou 等国际通用的名称,可视同英文。
反例:DaZhePromotion [打折] / getPingfenByName() [评分] / String fw[福娃] / int 某变量 = 3
-
代码和注释中都要避免使用任何语言的种族歧视性词语。
-
类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等。
正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion
反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion
-
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格。
-
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
-
抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
-
POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列 化错误。
-
包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。
-
避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名, 使可理解性降低。
-
杜绝完全不规范的缩写,避免望文不知义。
反例:AbstractClass“缩写”成 AbsClass;condition“缩写”成 condi;Function 缩写”成 Fu,此类 随意缩写严重降低了代码的可阅读性。
-
为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组 合来表达。
-
在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。
正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD
-
如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
-
各层命名规约:
A) Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
B) 领域模型命名规约
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
(二)常量定义
- 不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
- 在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟 数字混淆,造成误解。
- 不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。
(三)代码格式
-
使用 idea 的默认格式化快捷键进行格式化
-
注释的双斜线与注释内容之间有且仅有一个空格。
-
单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1)第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起换行。
4)方法调用中的多个参数需要换行时,在逗号后进行。
5)在括号前不要换行,见反例。
正例:
StringBuilder sb = new StringBuilder(); // 超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点号一起换行 sb.append("yang").append("hao")... .append("chen")... .append("chen")... .append("chen");反例:
StringBuilder sb = new StringBuilder(); // 超过 120 个字符的情况下,不要在括号前换行 sb.append("you").append("are")...append ("lucky"); // 参数很多的方法调用可能超过 120 个字符,逗号后才是换行处 method(args1, args2, args3, ... , argsX); -
IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要 使用 Windows 格式。
-
单个方法的总行数不超过 80 行。
说明:除注释之外的方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过 80 行。
正例:代码逻辑分清红花和绿叶,个性和共性,绿叶逻辑单独出来成为额外方法,使主干代码更加清晰;共 性逻辑抽取成为共性方法,便于复用和维护。
-
不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。 说明:任何情形,没有必要插入多个空行进行隔开。
(四)OOP 规约
-
相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。
-
不能使用过时的类或方法。
-
Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
正例:“test”.equals(object);
反例:object.equals(“test”);
说明:推荐使用 JDK7 引入的工具类 java.util.Objects#equals(Object a, Object b)
-
所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
-
任何货币金额,均以最小货币单位且整型类型来进行存储。
-
BigDecimal 的等值比较应使用 compareTo()方法,而不是 equals()方法。
说明:equals()方法会比较值和精度(1.0 与 1.00 返回结果为 false),而 compareTo()则会忽略精度。
-
定义数据对象 DO 类时,属性类型要与数据库字段类型相匹配。
-
关于基本数据类型与包装数据类型的使用标准如下:
1) 【强制】所有的 POJO 类属性必须使用包装数据类型。
2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
3) 【推荐】所有的局部变量使用基本数据类型。
-
构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
-
当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便 于阅读,此条规则优先于下一条。
-
类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter 方法。
(五)集合处理
-
关于 hashCode 和 equals 的处理,遵循如下规则:
1) 只要覆写 equals,就必须覆写 hashCode。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写 这两种方法。
3) 如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。
说明:String 因为覆写了 hashCode 和 equals 方法,所以可以愉快地将 String 对象作为 key 来使用。
-
判断所有集合内部的元素是否为空,使用 isEmpty()方法,而不是 size()==0 的方式。
-
在使用 java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要注 意当 value 为 null 时会抛 NPE 异常。
-
在使用 Collection 接口任何实现类的 addAll()方法时,都要对输入的集合参数进行 NPE 判断。
-
利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains()进行遍历去重或者判断包含操作。
(六)并发处理
-
获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
-
创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
-
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
-
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
-
必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用, 如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用 try-finally 块进行回收。
-
高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。
-
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。
-
并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加 锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。
-
资金相关的金融敏感信息,使用悲观锁策略。
-
避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。
-
HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在 开发过程中注意规避此风险。
(七)控制语句
-
在一个 switch 块内,每个 case 要么通过 continue/break/return 等来终止,要么 注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 defaultJava 开发手册 21/59 语句并且放在最后,即使它什么代码也没有。
说明:注意 break 是退出 switch 语句块,而 return 是退出方法体。
-
当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null 判断。
-
在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
-
表达异常的分支时,少用 if-else 方式,这种方式可以改写成:
f (condition) { ... return obj; } // 接着写 else 的业务逻辑代码;说明:如果非使用 if()…else if()…else…方式表达逻辑,避免后续代码维护困难,请勿超过 3 层。
正例:超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句 示例如下:
public void findBoyfriend (Man man) {
if (man.isUgly()) {
System.out.println("本姑娘是外貌协会的资深会员");
return;
}
if (man.isPoor()) {
System.out.println("贫贱夫妻百事哀");
return;
}
if (man.isBadTemper()) {
System.out.println("银河有多远,你就给我滚多远");
return;
}
System.out.println("可以先交往一段时间看看");
}
-
循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、 获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。
-
避免采用取反逻辑运算符。
说明:取反逻辑不利于快速理解,并且取反逻辑写法一般都存在对应的正向逻辑写法。
正例:使用 if (x < 628) 来表达 x 小于 628。 反例:使用 if (!(x >= 628)) 来表达 x 小于 628。
(八)注释规约
-
类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用 // xxx 方式。
-
所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。
-
所有的类都必须添加创建者和创建日期。
-
方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使 用/* */注释,注意与代码对齐。
-
所有的枚举类型字段必须要有注释,说明每个数据项的用途。
-
与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持 英文原文即可。
-
代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了 导航的意义。
-
谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
说明:代码被注释掉有两种可能性:
1)后续会恢复此段代码逻辑。
2)永久不用。
前者如果没有备注信息, 难以知晓注释动机。后者建议直接删掉即可,假如需要查阅历史代码,登录代码仓库即可。
-
对于注释的要求:第一、能够准确反映设计思想和代码逻辑;第二、能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同 天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看 的,使其能够快速接替自己的工作。
-
好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一 个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释又是相当大的负担。
-
特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1) 待办事宜(TODO):(标记人,标记时间,[预计处理时间])
表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc 还没 有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。
(九)前后端规约
-
前后端交互的 API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响 应体。
说明: 1) 协议:生产环境必须使用 HTTPS。 2) 路径:每一个 API 需对应一个路径,表示 API 具体的请求地址: a) 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。 b) URL 路径不能使用大写,单词如果需要分隔,统一使用下划线。 c) 路径禁止携带表示请求内容类型的后缀,比如".json",".xml",通过 accept 头表达即可。 3) 请求方法:对具体操作的定义,常见的请求方法如下: a) GET:从服务器取出资源。 b) POST:在服务器新建一个资源。 c) PUT:在服务器更新资源。 d) DELETE:从服务器删除资源。 4) 请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type。 5) 响应体:响应体 body 可放置多种数据类型,由 Content-Type 头来确定。 -
前后端数据列表相关的接口返回,如果为空,则返回空数组[]或空集合{}。
说明:此条约定有利于数据层面上的协作更加高效,减少前端很多琐碎的 null 判断。
-
服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,errorCode、 errorMessage、用户提示信息四个部分。
说明:四个部分的涉众对象分别是浏览器、前端开发、错误排查人员、用户。其中输出给用户的提示信息 要求:简短清晰、提示友好,引导用户进行下一步操作或解释错误原因,提示信息可以包括错误原因、上 下文环境、推荐操作等。 errorCode:errorMessage:简要描述后端出错原因,便于错误排 查人员快速定位问题,注意不要包含敏感数据信息。
正例:常见的 HTTP 状态码如下 1) 200 OK: 表明该请求被成功地完成,所请求的资源发送到客户端。 2) 401 Unauthorized: 请求要求身份验证,常见对于需要登录而用户未登录的情况。 3) 403 Forbidden:服务器拒绝请求,常见于机密信息或复制其它登录用户链接访问服务器的情况。 4) 404 Not Found: 服务器无法取得所请求的网页,请求资源不存在。 5) 500 Internal Server Error: 服务器内部错误。 -
在前后端交互的 JSON 格式数据中,所有的 key 必须为小写字母开始的 lowerCamelCase 风格,符合英文表达习惯,且表意完整。
-
HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。
-
HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出 错。
说明:nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调 大服务器端的限制。
-
前后端的时间格式统一为"yyyy-MM-dd HH:mm:ss",统一为 GMT。
(十)其他
-
避免用 Apache Beanutils 进行属性的 copy。
-
在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
-
不要在视图模板中加入任何复杂的逻辑。
-
及时清理不再使用的代码段或配置信息。
说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///) 来说明注释掉代码的理由。如:
public static void hello() { /// 业务方通知活动暂停 // Business business = new Business(); // business.active(); System.out.println("it's finished"); }
六、异常日志
(一)错误码
-
错误码的制定原则:快速溯源、沟通标准化。
-
错误码不体现版本号和错误等级信息。
-
全部正常,但不得不填充错误码时返回五个零:00000。
-
错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号。
说明:错误产生来源分为 A/B/C,A 表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付 超时等问题;B 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C 表示错误来源 于第三方服务,比如 CDN 服务出错,消息投递超时等问题;四位数字编号从 0001 到 9999,大类之间的 步长间距预留 100。
-
编号不与公司业务架构,更不与组织架构挂钩,以先到先得的原则在统一平台上进行, 审批生效,编号即被永久固定。
-
错误码使用者避免随意定义新的错误码。
说明:尽可能在原有错误码附表中找到语义相同或者相近的错误码在代码中使用即可。
-
错误码不能直接输出给用户作为提示信息使用。
-
错误码的后三位编号与 HTTP 状态码没有任何关系。
(二)异常处理
- Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等等。
- 异常捕获后不要用来做流程控制,条件控制。
- catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
- 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。
- 事务场景中,抛出异常被 catch 后,如果需要回滚,一定要注意手动回滚事务。
- finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
- 不要在 finally 块中使用 return。
- 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
- 在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable 类来进行拦截。
- 方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说 明什么情况下会返回 null 值。
(三)日志规约
-
应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 (SLF4J、JCL–Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和 各个类的日志处理方式统一。
-
对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。
-
避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置 additivity=false。
-
生产环境禁止直接使用 System.out 或 System.err 输出日志或使用 e.printStackTrace()打印异常堆栈。
说明:标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动,如果大量输出送往这两个文件,容易 造成文件大小超过操作系统大小限制。
-
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过 关键字 throws 往上抛出。
-
可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适 从。如非必要,请不要在此场景打出 error 级别,避免频繁报警。
附录
错误码列表
错误码 中文描述 说明
00000 一切 ok -正确执行后的返回
A0001 用户端错误
A0100 用户注册错误
A0101 用户未同意隐私协议
A0102 注册国家或地区受限
A0110 用户名校验失败
A0111 用户名已存在
A0112 用户名包含敏感词
A0113 用户名包含特殊字符
A0120 密码校验失败
A0121 密码长度不够
A0122 密码强度不够
A0130 校验码输入错误
A0131 短信校验码输入错误
A0132 邮件校验码输入错误
A0133 语音校验码输入错误
A0140 用户证件异常
A0141 用户证件类型未选择
A0142 大陆身份证编号校验非法
A0143 护照编号校验非法
A0144 军官证编号校验非法
A0150 用户基本信息校验失败
A0151 手机格式校验失败
A0152 地址格式校验失败
A0153 邮箱格式校验失败
A0200 用户登录异常
A0201 用户账户不存在
A0202 用户账户被冻结
A0203 用户账户已作废
A0210 用户密码错误
A0211 用户输入密码错误次数超限
A0220 用户身份校验失败
A0221 用户指纹识别失败
A0222 用户面容识别失败
A0223 用户未获得第三方登录授权
A0230 用户登录已过期
A0240 用户验证码错误
A0241 用户验证码尝试次数超限
A0300 访问权限异常
A0301 访问未授权
A0302 正在授权中
A0303 用户授权申请被拒绝
A0310 因访问对象隐私设置被拦截
A0311 授权已过期
A0312 无权限使用 API
A0320 用户访问被拦截
A0321 黑名单用户
A0322 账号被冻结
A0323 非法 IP 地址
A0324 网关访问受限
A0325 地域黑名单
A0330 服务已欠费
A0340 用户签名异常
A0341 RSA 签名错误
A0400 用户请求参数错误
A0401 包含非法恶意跳转链接
A0402 无效的用户输入
A0410 请求必填参数为空
A0411 用户订单号为空
A0412 订购数量为空
A0413 缺少时间戳参数
A0414 非法的时间戳参数
A0420 请求参数值超出允许的范围
A0421 参数格式不匹配
A0422 地址不在服务范围
A0423 时间不在服务范围
A0424 金额超出限制
A0425 数量超出限制
A0426 请求批量处理总个数超出限制
A0427 请求 JSON 解析失败
A0430 用户输入内容非法
A0431 包含违禁敏感词
A0432 图片包含违禁信息
A0433 文件侵犯版权
A0440 用户操作异常
A0441 用户支付超时
A0442 确认订单超时
A0443 订单已关闭
A0500 用户请求服务异常 二级宏观错误码
A0501 请求次数超出限制
A0502 请求并发数超出限制
A0503 用户操作请等待
A0504 WebSocket 连接异常
A0505 WebSocket 连接断开
A0506 用户重复请求
A0600 用户资源异常 二级宏观错误码
A0601 账户余额不足
A0602 用户磁盘空间不足
A0603 用户内存空间不足
A0604 用户 OSS 容量不足
A0605 用户配额已用光
A0700 用户上传文件异常
A0701 用户上传文件类型不匹配
A0702 用户上传文件太大
A0703 用户上传图片太大
A0704 用户上传视频太大
A0705 用户上传压缩文件太大
A0800 用户当前版本异常
A0801 用户安装版本与系统不匹配
A0802 用户安装版本过低
A0803 用户安装版本过高
A0804 用户安装版本已过期
A0805 用户 API 请求版本不匹配
A0806 用户 API 请求版本过高
A0807 用户 API 请求版本过低
A0900 用户隐私未授权
A0901 用户隐私未签署
A0902 用户摄像头未授权
A0903 用户相机未授权
A0904 用户图片库未授权
A0905 用户文件未授权
A0906 用户位置信息未授权
A0907 用户通讯录未授权
A1000 用户设备异常
A1001 用户相机异常
A1002 用户麦克风异常
A1003 用户听筒异常
A1004 用户扬声器异常
A1005 用户 GPS 定位异常
-
B0001 系统执行出错
B0100 系统执行超时
B0101 系统订单处理超时
B0200 系统容灾功能被触发
B0210 系统限流
B0220 系统功能降级
B0300 系统资源异常
B0310 系统资源耗尽
B0311 系统磁盘空间耗尽
B0312 系统内存耗尽
B0313 文件句柄耗尽
B0314 系统连接池耗尽
B0315 系统线程池耗尽
B0320 系统资源访问异常
B0321 系统读取磁盘文件失败
-
C0001 调用第三方服务出错
C0100 中间件服务出错
C0110 RPC 服务出错
C0111 RPC 服务未找到
C0112 RPC 服务未注册
C0113 接口不存在
C0120 消息服务出错
C0121 消息投递出错
C0122 消息消费出错
C0123 消息订阅出错
C0124 消息分组未查到
C0130 缓存服务出错
C0131 key 长度超过限制
C0132 value 长度超过限制
C0133 存储容量已满
C0134 不支持的数据格式
C0140 配置服务出错
C0150 网络资源服务出错
C0151 VPN 服务出错
C0152 CDN 服务出错
C0153 域名解析服务出错
C0154 网关服务出错
C0200 第三方系统执行超时
C0210 RPC 执行超时
C0220 消息投递超时
C0230 缓存服务超时
C0240 配置服务超时
C0250 数据库服务超时
C0300 数据库服务出错
C0311 表不存在
C0312 列不存在
C0321 多表关联中存在多个相同名称的列
C0331 数据库死锁
C0341 主键冲突
C0400 第三方容灾系统被触发
C0401 第三方系统限流
C0402 第三方功能降级
C0500 通知服务出错
C0501 短信提醒服务失败
C0502 语音提醒服务失败
C0503 邮件提醒服务失败
更多推荐



所有评论(0)