开发规范

2023-12-05

开发规范

方法命名规范

save、remove、update、get、list、count

  1. 获取单个对象的方法用 get 作前缀
    • 例如:查询单个用户 getStudent ,按照 ID 查询单个用户 getStudentById
  2. 获取多个对象的方法用 list 作前缀
    • 例如:按照 IDS 查询多个用户, listStudentByIds
  3. 获取统计值的方法用 count 作前缀
    • 例如:统计全量用户, countUser
  4. 插入的方法用 save 作前缀
    • 例如:新增用户, saveUser
  5. 删除的方法用 remove 作前缀
    • 例如:删除用户, removeUser
  6. 修改的方法用 update 作前缀
    • 例如:修改用户, updateUser

if/for/while/switch/do等保留字与括号之间都必须加空格

领域模型命名规约

类名使用驼峰法,DO/BO/DTO/VO/AO/PO除外

DO(Data Object) DTO(Data Transfer Object) BO(Business Object) PO(Persistant Object) AO(ApplicationObject) VO(Value Object)

  1. DO数据对象(数据库中的表映射到程序中的对象)
    • xxxDO ,其中 xxx 即为数据表名。
  2. DTO数据传输对象(数据传输时的对象)
    • xxxDTO ,其中 xxx 为业务领域相关的名称。
  3. BO业务对象,BO对内,DTO在BO的基础上只要自己需要的数据,然后对外提供
  4. PO持久对象(数据库中的记录),一条记录就是一个PO对象
  5. AO应用对象,在Web层与Service层之间抽象的复用对象模型, 极为贴近展示层,复用度不高
  6. Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装, 禁止使用 Map 类来传输。
  7. 展示对象(在用户界面上显示信息的对象) 可以优化掉,展示业务不复杂的可以直接用DTO
    • xxxVO ,其中 xxx 一般为网页名称。
  8. 注意事项
    • POJO DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO
抽象类使用Abstract或Base
枚举成员名称需要全大写
常量定义

1、long或者Long初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。

2、不要使用一个常量类维护所有常量,按常量功能进行归类,比如缓存相关常量放在类CacheConsts下,系统配置相关常量放在类ConfigConsts下

API 路径规约

  1. Get 方法尽量把 ID 等变量放到路径上。
    • 例如:获取指定用户的信息,使用 GET /user/{id}
  2. 多个不可分割的单词,使用中划线拼接。
    • 例如:用户验证码接口,使用 GET /user-verification-code
  3. 参数使用驼峰拼写。
    • 例如:获取指定用户购买的指定商品,使用 GET /user/{userId}/purchasedProduct/{productId}
  4. 指向集合的复数名称。
    • 例如:获取所有用户列表接口,使用 GET /users
  5. 不用使用动词定义 URL
    • 错误示例:
      • /update/user
      • /get/user
    • 正确应该通过 HTTP 方法的语义来定义 URL 的行为。
      • 获取用户: GET /user/{id}
      • 添加用户: POST /user
      • 修改用户: PUT /user
  6. 对非资源 URL 使用动词
    • 如果有一个接口,并不是 CRUD 操作,这种情况可以使用动词。
      • 例如:向用户发送邮件接口,使用 POST /user/{id}/send-mail .
  7. 在嵌套资源的 URL 中使用关系
    • 获取指定订单中所有商品列表,使用 GET /order/{id}/products .
    • 获取指定订单中所有指定商品信息,使用 GET /order/{orderId}/product/{productId} .

注释规范

注释说明意图;class interface说明意图;方法可以详细一点比如加上对参数的说明;注释要在上一行加;在同行加的更体现的是让人理解的说明

  1. 注释说明意图即可,无需补充冗余字段

    • Class、Interface、Enum、@interface等文件类型,类上注释仅需说明类的意图即可。不需要补充时间和创建人,因为往往开发代码的不止一个人,容易造成信息干扰。需要的话,查看提交记录即可。
    /**
     * 适配第三方框架的线程池
     */
    public interface ThreadPoolAdapter {
    
    }
    
  2. 方法上需要添加注释

    • 方法上需添加注释,并说明清楚方法的意图(接口实现类无需注释);必要时描述 @param @return。如果方法为内部引用方法,并且方法名称见名知意,无需方法注释.
    /**
     * 适配第三方框架的线程池
     */
    public interface ThreadPoolAdapter {
    
        /**
         * 修改框架线程池的核心参数
         *
         * @param threadPoolBaseInfo  修改线程池的基础参数
         * @return  线程池核心参数修改结果
         */
        boolean updateThreadPool(ThreadPoolBaseInfo threadPoolBaseInfo);
    }
    
  3. 方法块内部注释规范

    • 方法内部的注释,应该新起一行,而不是跟在代码后面.、
    正例:
    // 刷新动态线程池参数
    refreshDynamicPool(parameter, executor);
    
    反例:
    refreshDynamicPool(parameter, executor); // 刷新动态线程池参数
    
  4. 方法命名说明方法本身意图

    • 私有方法尽量通过方法命名说明方法语义.

代码开发规约:

  • 类、方法和变量的命名要做到顾名思义,避免使用缩写.

  • 静态变量使用大写,多个单词使用下划线连接。示例: MESSAGE_CENTER_SEND_TYPE .

  • 捕获的异常名称命名为 ex ;捕获异常且不做任何事情,异常名称命名为 ignored .

// 强制规定:捕获的异常名称命名为 ex;捕获异常且不做任何事情,异常名称命名为 ignored
public class DataProcessor {
    public void processData() {
        try {
            // 一些处理逻辑
        } catch (Exception ex) {
            // 处理异常情况
        }
    }
    
    public void processWithIgnoredException() {
        try {
            // 一些处理逻辑
        } catch (Exception ignored) {
            // 不做任何事情,只是捕获并忽略异常
        }
    }
}
  • 返回值变量使用 result 命名;循环中使用 each 命名循环变量;map 中使用 entry 代替 each .
// result 命名示范:
private void parseDate(String data) {
    Result result = JSONUtil.parseObject(data, Result.class);
    return result;
}

// 或采用 result 为前缀:
private void parseDate(String data) {
    Result resultDate = JSONUtil.parseObject(data, Result.class);
    return resultDate;
}

// each 命名示范:
appNameLeaseMap.values().forEach(each -> appNameLeaseList.add(each));

// 或是 for 循环:
for (Lease<InstanceInfo> each : appNameLeaseMap.values()) {
    appNameLeaseList.add(each);
}
  • 业务系统中优先使用 Guava、HuTool、Common3 等工具类中的方法,不存在指定方法时再创建自定义工具类,禁止创建相同语义方法的工具类.
  • 空实现接口或类不允许存在空格.
// 正例
public interface AdapterThreadPoolMonitor extends ThreadPoolMonitor {
}

// 反例
public interface AdapterThreadPoolMonitor extends ThreadPoolMonitor {
    
}
  • 优先使用卫语句替换嵌套判断,提高代码简洁度.(卫语句提前返回)

下面这个不是卫语句

public void processOrder(Order order) {
    if (order != null) {
        if (order.isValid()) {
            // 执行订单处理逻辑
        } else {
            // 处理无效订单
        }
    } else {
        // 处理空订单
    }
}

卫语句

public void processOrder(Order order) {
    if (order == null) {
        // 处理空订单
        return;
    }

    if (!order.isValid()) {
        // 处理无效订单
        return;
    }

    // 执行订单处理逻辑
}

  • 方法参数自定义实体对象别名尽量使用 requestParam ,非自定义对象可以使用对应语义命名.
@GetMapping("/add")
@ApiOperation(value = "新增购物车")
public Result<CartItemRespDTO> addCartItem(@RequestBody CartItemAddReqDTO requestParam) {
    return Results.success(null);
}

OOP规约

1、Object的equals方法容易抛出空指针异常,应使用常量或确定有值的对象来调用equals

正例:“test”.equals(object)

2、关于基本数据类型与包装数据类型的使用标准如下:

所有的POJO类属性必须使用 包装数据类型

RPC方法的返回值和参数必须使用 包装数据类型

所有的局部变量使用 基本数据类型

3、构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中

4、使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有角标越界异常

5、类内方法定义的顺序依次是:公有方法或保护方法>私有方法>getter/setter方法

6、循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展

7、final可以声明类、成员变量、方法、以及本地变量,下列情况使用final关键字

1)不允许被继承的类,如:String类

2)不允许修改引用的域对象,如POJO类的域变量

3)不允许被重写的方法,如:POJO类的setter方法

4)不允许运行过程中重新赋值的局部变量

5)避免上下文重复使用一个变量,使用final描述可以强制重新定义一个变量,方便更好地进行重构

8、慎用Object的clone方法来拷贝对象,对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝

9、类成员与方法访问控制从严

1)如果不允许外部直接通过new来创建对象,那么构造方法必须是private

2)工具类不允许有public或者default构造方法

3)类非static成员变量并且与子类共享,必须是protected

4)类非static成员变量并且仅在本类使用,必须是private

5)类static成员变量如果仅在本类使用,必须是private

6)若是static成员变量,必须考虑是否为final

7)类成员方法只供类内部调用,必须是private

8)类成员方法只对继承类公开,那么限制为protected

集合处理

1、关于hashcode和equals的处理,遵循如下规则:

1)只要重写equals,就必须重写hashCode

2)因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法

3)如果自定义对象作为Map的键,那么必须重写hashCode和equals,String重写了hashCode和equals方法,所以可以使用String对象作为key

2、不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁

3、Map类集合K/V能不能存储null值的情况

集合类 key Value Super 说明
Hashtable 不允许为null 不允许为null Dictionary 线程安全
ConcurrentHashMap 不允许为null 不允许为null AbstractMap 锁分段技术(JDK8:CAS)
TreeMap 不允许为null 允许为null AbstractMap 线程不安全
HashMap 允许为null 允许为null AbstractMap 线程不安全

并发处理

1、线程资源必须通过线程池提供,不允许在应用中自行显式创建线程,线程不允许使用Executors去创建,而是通过ThraedPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

2、SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类;

如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormatter代替SimpleDateFormat

3、高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁,避免在锁代码块中调用RPC方法。

4、 并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库使用乐观锁,使用version作为更新依据,如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。

5、多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其他任务便会自动终止运行,使用SchedExecutorService则没有这个问题。

6、使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保 countDown方法被执行到,避免主线程无法执行至await方法,直到超时才返回结果。子线程抛出异常堆栈,不能在主线程try-catch到。

控制语句

1、在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止,在switch块内,都必须包含一个default语句并且放在最后,即使空代码

2、在高并发场景中,避免使用“等于”判断作为判断或退出的条件,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。

3、循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的try-catch操作。

MySQL数据库

(一)建表规约

1、表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigneed tinyint(1表示是,0表示否)

2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。

3、小数类型为decimal,禁止使用float和double,如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储

4、varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其他字段索引效率

5、表必备三字段:id主键id、gmt_create创建时间、gmt_modified修改时间

6、单表行数超过500万行,或者单表容量超过2GB,才推荐进行分库分表

(二)索引规约

1、业务上具有唯一特性的字段,必须建成唯一索引

2、超过三个表禁止join,需要join的字段,数据类型必须绝对一致,保证被关联的字段需要有索引

3、在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可,一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用count(distinct left(列名,索引长度))/count(“”)的区分度来确定

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、SQL性能优化的目标:至少要达到range级别,要求是ref级别,如果可以是consts最好;consts单表在中最多只有一个匹配行,ref指的是使用普通的索引,range对索引进行范围检索,type=index,索引物理文件全扫描,速度非常慢

(三)SQL语句

1、数据订正时,特别是删除、修改记录操作,要先select,避免出现误删除,确认无误才能执行更新语句

2、in操作能避免就避免,避免不了,控制在1000个之内

3、sql.xml配置参数使用#{},#param#,不要使用${},此种方式容易出现SQL注入。

代码格式化Spotless

Spotless 是一个用于代码格式化和风格检查的工具,可用于许多编程语言和项目类型。Spotless 的目标是确保项目中的代码始终符合定义的代码格式和风格规范。

代码检查Checkstyle

在日常生活中有两种常用的使用方式,分别是通过代码编辑器 IDEA 和 Maven 配合使用。

消息队列正常使用

命名规范

RocketMQ 相关命名强制使用英文小写。

1)【强制】Topic 命名:业务线_项目名_topic。业务线或项目包含多个单词,使用 - 分割,例如: common_message-center_topic

2)【强制】Tag 命名:业务线_项目_业务_tag,例如: common_message-center_send-message_tag

3)【强制】生产者组命名:业务线_项目*_*业务_pg,例如: common_message-center_send-message_pg

4)【强制】消费者组命名:业务线_项目*_*业务_cg,例如: common_message-center_send-message_cg

申请规范

创建 Topic 需要走线上申请,申请时补充下述信息。


申请人:马称

Topic: common_message-center_topic

Produce:

生产应用 生产者组 Tag 备注
1 message-center common_message-center_send-message_pg common_message-center_send-message_tag 流量削峰
2 message-center common_message-center_send-message_pg insurance_trading-order_send-message-fail_tag 微信模板消息发送失败通知保险项目

Consume:

消费应用 消费者组 Tag 消费模型 备注
1 message-center common_message-center_send-message_cg common_message-center_send-message_tag 集群模式 发送微信模板、短信、小程序等消息
2 trading-order insurance_trading-order_send-message-fail_cg insurance_trading-order_send-message-fail_tag 集群模式 微信模板消息发送失败通知保险项目

使用规范

1. 消息发送

1)【强制】消息生产者创建时,必须指定生产者组。

2)【强制】一个系统对应一个 Topic,系统下的不同业务根据 Tag 区分,参考申请规范-消费应用 Tag。

3)【强制】发送消息时,需设置 KEYS。KEYS 建议定义为业务唯一标识,比如订单 ID。

4)【强制】发送消息不管发送成功或失败,需打印 KEYS、Payload、执行时间以及 SendResult

5)【强制】发送消息时,需设置超时时间,避免应用被拖垮;建议超时时间设置为 2000ms 内。

6)【建议】针对可靠性较高的消息,发送失败后可以存储到 DB,开启定时任务扫描,并重新投递。

2. 消息消费

1)【强制】消费端创建时,必须指定消费者组。

2)【强制】消费端需要保证数据幂等。

3)【强制】消费消息不管成功或失败,需打印 KEYS、MsgId、执行时间以及 Message。

4)【强制】不同的应用集群应使用不同的消费者组,如果不同的应用集群需要订阅同一消费者组,需保证 Topic Tag 订阅关系一致。

5)【强制】引入 easymall-rocketmq-spring-boot-starter 打印消息消费日志。

log.info("Execute result: {}, Keys: {}, Dispatch time: {} ms, Execute time: {} ms, Message: {}", ...);

6)【建议】消费时尽量不设置重试,大部分情况下,执行失败的消息重试后会再次失败,反而会影响消费进度。开发者应该针对特定场景在代码中设置重试逻辑。

7)【建议】消费者并发消费数量默认为 1,即串行化,应该基于不同系统场景来设置并发数,同时要考虑消费过程中其它组件的压力。

  • 系统 CPU 任务少: CPU 核数 / (1 - 阻塞系数 0.8)
  • 系统 CPU 任务较多,建议 CPU 核数 + 1 即可。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

开发规范 的相关文章

随机推荐

  • orthanc 读取数据库过程

    index LookupAttachment bool StatelessDatabaseOperations LookupAttachment FileInfo attachment int64 t revision const std
  • 解决websocket集群的session共享问题

    在websocket中 服务端主要使用的是session打交道 但是由于session无法实现序列化 不能存储到redis这些中间存储里面 因此这里我们只能把session存储在本地的内存中 那么如果是集群的话 我们如何实现session准
  • android 13.0 framework禁用系统所有通知

    1 概述 在13 0的系统rom产品开发中最近公司项目要求 禁用系统所有通知 不需要在下拉状态栏显示通知功能实现 要控制系统通知的开关功能 需要屏蔽系统通知 而系统通知都是由NoticationManagerServices java来管理
  • 基于Selenium和python的UI自动化测试方案

    一 概述 对于比较复杂的系统 每次有小的迭代测试同学不可能会把所有的流程验证一遍 如果开发无意改动影响了某些流程而测试又没测试到 就可能会出现生产问题 因此很有必要通过自动化的测试去确保系统的稳定性 自动化测试可以选择接口自动化测试和UI自
  • 自定义注解验证数据字典选项及bean注入问题

    我们在工作中经常需要对字典选项进行定义 如果客户端传来的字典项不符合要求 那么根本无法保存 但是已有的注解并没有字典值的验证 那我们就自己实现一个 一 自定义字典值验证的注解 DictValid import javax validatio
  • 如何在Linux上搭建本地Docker Registry镜像仓库并实现公网访问

    Linux 本地 Docker Registry本地镜像仓库远程连接 文章目录 Linux 本地 Docker Registry本地镜像仓库远程连接 1 部署Docker Registry 2 本地测试推送镜像 3 Linux 安装cpol
  • Windows家庭版组策略问题解决及权限维持

    实验环境 windows10虚拟机 问题一 组策略问题解决 windows家庭版组策略未能打开问题 1 在桌面创建一个记事本文件 txt 并填入以下代码 echo off pushd dp0 dir b systemroot Windows
  • C/C++,树算法——Ukkonen的“后缀树“构造算法的源程序

    1 文本格式 A C program to implement Ukkonen s Suffix Tree Construction And then build generalized suffix tree include
  • 安全行业招聘信息汇总

    1 阿里巴巴 淘天集团 安全部 社招岗位 Java开发 招聘层级 P5 P6 工作年限 本科毕业1 3年 硕士毕业1 2年 base地点 杭州 职位描述 负责淘天安全部风控基础标签平台0到1能力建设及产品规划和落地 负责标签应用的产品沉淀和
  • webpack查找配置文件的策略

    Webpack 在执行时会按照一定的策略来查找配置文件 以下是它查找配置文件的基本流程 1 命令行指定 如果在运行 Webpack 时通过 config 或 c 参数指定了配置文件的路径 那么 Webpack 将使用这个指定的配置文件 2
  • 6-15 复制字符串

    include
  • 9-3用结构体定义学生,用函数输出学生成绩

    include
  • Android 13.0 SystemUI电池电量为0时延迟关机的解决方案

    1 简述 在13 0系统rom定制化开发中 在系统开发中可能会遇到了在电池电量为0时这时未出现立即关机的情况 产生延时关机的问题 下面就来分析这个问题所产生的原因 然后解决这个问题 2 SystemUI电池电量为0延迟关机的核心代码 fra
  • 机器学习笔记 - 什么是3D语义场景完成/补全?

    一 什么是3D语义场景补全 3D 语义场景完成 Semantic Scene Completion 是一种机器学习任务 涉及以体素化形式预测给定环境的完整3D场景 完成3D形状的同时推断场景的 3D 语义分割的任务 这是通过使用深度图和为场
  • 【go语言开发】Minio基本使用,包括环境搭建,接口封装和代码测试

    本文主要介绍go语言使用Minio对象存储 首先介绍搭建minio 创建bucket等 然后讲解封装minio客户端接口 包括但不限于 上传文件 下载 获取对象url 最后测试开发的接口 文章目录 前言 Minio docker安装mini
  • 什么是跨站脚本攻击

    跨站脚本攻击 1 定义 2 跨站脚本攻击如何工作 3 跨站脚本攻击类型 4 如何防止跨站脚本攻击 1 定义 跨站脚本攻击 Cross site Scripting 通常称为XSS 是一种典型的Web程序漏洞利用攻击 在线论坛 博客 留言板等
  • 前端分片上传

    前端分片上传是一种将大文件分成若干个小块进行上传的方式 以解决大文件上传时网络不稳定或上传速度慢的问题 下面是前端分片上传的基本步骤 使用JavaScript读取文件 将文件分成若干块 可以使用File API来实现这个功能 使用XMLHt
  • 6-3 求3*3整数矩阵对角线元素之和

    include
  • JavaScript的创建对象时的语法糖

    js中创建一个自定义对象有两种方法 一种是使用new 另一种是使用对象字面量形式 即直接构建 关于字面量详见 https blog csdn net bigcarp article details 134777091 使用对象字面量定义对象
  • 开发规范

    目录 开发规范 方法命名规范 领域模型命名规约 类名使用驼峰法 DO BO DTO VO AO PO除外 抽象类使用Abstract或Base