前言
- 好的编码规范可以尽可能的减少一个软件的维护成本 , 并且几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护,如果没有统一的代码规范,那么每个人的代码必定会风格迥异。且不说会存在多个人同时开发同一模块的情况,即使是分工十分明晰的,等到要整合代码的时候也有够头疼的了,一直在填坑的路上。。。下面分几个方面做的总结
通用规范
命名
- 遵循驼峰表示;
- 使用准确意义的英文全拼,而非缩写,通用缩写除外(如HTTP)
- 专有名词等特殊情况允许使用拼音
- 名字中单词顺序应符合常用惯例(如主谓)
- 简洁明了,见名知意
注释
- 类、方法、属性需有注释
- 复杂业务逻辑需有注释
- 尽最大可能保证代码可读性,加少量注释释义,甚至不加注释
代码风格
- 代码风格保持统一,包括但不限于缩进、换行、包引进方式、文件编码
- 使用统一的代码格式模板(确定模板)
日志
- 日志记录应尽量详尽,以利于问题追踪
- 使用适宜的日志级别(error,warn,info,debug)
版本管理
-
代码提交需要有清晰的变更日志
-
删除而非注释无用代码
-
在自有分支解决冲突后在合并master分支
-
分支
- master
- release
- test
- develop
- hotfix
技术规范
模块/分层
严禁跨层调用,小前台统一调用服务层api,因为每个层级都自己的职责,并且对上层而言是透明的,就像 OSI 七层协议模型和 TCP/IP 四层协议模型一样,只有将职责限制在自己的边界内,整体层次结构才清晰明了,注意循环调用的产生,能批量的,就不要循环
一个业务模块一个代码仓库,团队分工明确,代码安全度高
业务类型 组织名-xxx
- xxx-common
- xxx-authority
xxx-common 组织–公共依赖
- java
- com.xxx.common
- annotation 自定义注解
- aspect 切面
- config 公共配置
- constants 公共常量
- enums 公共枚举
- utils 工具类
- exception 公共异常
- annotation 自定义注解
- com.xxx.common
xxx-authority 组织–授权模块
- xxx-authority-api 组织–授权模块api是微服务内部调用接口
- java
- com.xxx.authority.api
- dto 微服务内部调用接口出参实体
- parameter 微服务内部调用接口入参实体
- route 微服务内部调用接口路径定义
- com.xxx.authority.api
- java
- xxx-authority-provide 组织–授权模块provide是api的具体实现
- java
- com.xxx.authority.service
- common 授权模块servicen内部的公用包
- aop 切面实现
- config 配置文件
- extension 扩展相关
- interceptor 拦截器
- utils 工具包
- constants 常量
- enums 枚举
- service 业务层
- BoImpl
- mapper 数据层
- ApiImpl api实现层
- domain 实体
- entity 给数据库表一一对应
- pojo ORM映射的对象
- common 授权模块servicen内部的公用包
- com.xxx.authority.service
- resources
- mapper Mybatis的Mapper.xml
- bootstrap.yml 配置文件公用的
- bootstrap-dev.yml 开发环境配置文件
- bootstrap-test.yml 测试环境
- bootstrap-release.yml 预发布环境
- bootstrap-prod.yml 生产环境
- java
xxx-authority-web 组织–授权模块web小前台
- xxx-authority-sdk 组织–授权模块sdk是对三方提供的sdk需要的话
- java
- com.xxx.authority.sdk 这个可以自定义
- entity 实体
- parameter 入参实体
- com.xxx.authority.sdk 这个可以自定义
- java
- xxx-authority-client 组织–授权模块client是数据返回层也是前端对接层
- java
- com.xxx.authority.client
- common 授权模块servicen内部的公用包
- aop 切面实现
- config 配置文件
- extension 扩展相关
- interceptor 拦截器
- utils 工具包
- constants 常量
- enums 枚举
- service 业务层
- BoImpl
- controller 前端接口层
- domain 实体
- vo 前端数据返回实体
- parameter 前端入参实体
- common 授权模块servicen内部的公用包
- com.xxx.authority.client
- resources
- bootstrap.yml 配置文件公用的
- bootstrap-dev.yml 开发环境配置文件
- bootstrap-test.yml 测试环境
- bootstrap-release.yml 预发布环境
- bootstrap-prod.yml 生产环境
- java
数据安全
- 客户验真。
- 设置黑白名单。
- 隶属于用户个人的页面或者功能必须进行权限控制校验(登录授权验证)。
- 数据完整性检查。
- 防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容。
- 用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
- 数据传输加密。
- 数据出错时的回滚机制。
- 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入。
高可用
- 集群。
- 弹性扩容(自动化扩容)。
- 服务治理(流量控制、降级、熔断)。
- 报警监控,快速安全响应。
- 备份和容灾策略。
- 支持爆发式流量增长。
防止攻击
- 敏感信息控制。
- 异常流量控制。
- 防止数据重复提交,接口幂等性验证。
- 防止渗透检测。
- 防止CSRF和DDOS攻击等。
- 防止XSS和SQL注入攻击等。
- 用户请求传入的任何参数必须做有效性验证。说明:忽略参数校验可能导致:
- page size 过大导致内存溢出
- 恶意 order by 导致数据库慢查询
- 缓存击穿,雪崩,穿透
- 任意重定向
- SQL 注入,Shell 注入,反序列化注入
事务
- 事务边界足够小,事务内不应包含rpc/http,不应大批量数据操作
- 使用@Transactional的方式明确标识和管理事务,事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等
线程
- 使用有边界限制的线程池管理线程,不允许手工创建线程
- 保证代码的线程安全性
接口
- 对外接口必须支持幂等
- 对外接口统一以Result模型(包含状态、数据、通信)返回
task/schedule
- 任务统一接入任务调度平台
- 任务必须支持幂等
异常
- 底层异常向上抛出,除非明确知道处理措施
- 系统边界有统一的异常处理机制
- 异常栈保持完整
- 异常message清晰明了
- 原则上不允许出现空指针异常
数据库规范
建表规范
1.表达是否概念,用is_xxx的方式命名,数据类型是unsigned tinyint (0否 1是)
2.表名,字段名必须使用小写字母,严禁数字开头(Mysql 在windows下不区分大小写,在Linux下默认是区分大小写,避免节外生枝,统一小写)
3.禁用保留字段,如desc,range,match,delayed等
4.索引命名要求,主键索引 pk_字段名,唯一索引 uk_字段名,普通索引 idx_字段名
5.小数类型为 decimal,禁止使用 float 和 double
6.如果存储的字符串长度几乎相等,使用 char 定长字符串类型
7.varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长
度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索
引效率
8.表必备三字段:id, create_time, update_time
9.字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1) 不是频繁修改的字段。
2) 不是 varchar 超长字段,更不能是 text 字段。
3) 不是唯一索引的字段
10.单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表
索引规范
1.业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引(即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生)
2.超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引,因为当一个表如果要被分库分表的时候,之前的链表逻辑,基本都要改
3.在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可
4.页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决(索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引)
5.如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
6.利用覆盖索引来进行查询操作,避免回表(能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效
果,用 explain 的结果,extra 列会出现:using index)
7.利用延迟关联或者子查询优化超多分页场景
8.SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是consts 最好
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索
9.建组合索引的时候,区分度最高的在最左边。
正例:如果 where a=? and b=? ,如果 a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么
即使 c 的区分度更高,也必须把 d 放在索引的最前列,即索引 idx_d_c。
10.防止因字段类型不同造成的隐式转换,导致索引失效
SQL 语句
1.不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关
2.count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0
3.当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题
4.不得使用外键与级联,一切外键概念必须在应用层解决
5.数据订正(特别是删除、修改记录操作)时,要先 select,避免出现误删除,确认无误才能执行更新语句。
ORM 映射
1.在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明
2.不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义其他对象接受
3.sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入
4.不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出(值的类型不可控)
5.不要全字段更新,只更新需要更新的字段(一是易出错;二是效率低;三是增加 binlog 存储)