1.命名规范
-
【强制】取名要有具体的含义,好的命名是最好的注释
-
【强制】命名严禁使用拼音与英文混合的方式,更不允许使用中文的方式 反例: getPingfenByName()
-
【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO 正例: UserDO / TcpUdpDeal 反例: UserDo / TCPUDPDeal
-
【强制】方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase 风格,必须遵从驼峰形式。 正例: localValue / getHttpMessage()
-
【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。 正例:MAX_STOCK_COUNT 反例:MAX_COUNT
-
【建议】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
-
【强制】POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。 反例:定义为基本数据类型Boolean isDeleted;的属性,它的方法也是isDeleted(),RPC框架在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
-
【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。 正例: 应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考 spring 的框架结构)
-
【强制】杜绝完全不规范的缩写,避免望文不知义。 反例:AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。
-
【推荐】如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。 正例:public class OrderFactory; public class LoginProxy;
-
【推荐】接口类中的方法和属性不要加任何修饰符号(public也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量 正例:接口方法签名:void f(); 接口基础常量表示:String COMPANY = "alibaba"; 反例:接口方法定义:public abstract void f();
-
【推荐】业务类:Service类以Service结尾;实现类用 ServiceImpl 的后缀;Dao类以Dao为结尾;controller类以Controller结尾(Action类以Action结尾)等
-
【推荐】实体类:对应表的为entity:和表名对应;用在服务与web层之间的为dto:以DTO结尾;用在web层与页面之间的为vo:以VO结尾;
-
【推荐】 controller和action命名与pages中的命名对应。比如XXXController与之对应的主view就在pages下的XXX.ftl中
-
【推荐】如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able 的形式)。 正例:AbstractTranslator 实现 Translatable。
-
【推荐】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。 正例:枚举名字为ProcessStatusEnum的成员名称:SUCCESS / UNKOWN_REASON。
-
【推荐】方法命名:
-
获取单个对象的方法用load/get做前缀。
-
获取多个对象的方法用find/list做前缀。
-
获取统计值的方法用count做前缀。
-
插入的方法用add/insert做前缀。
-
删除的方法用remove/delete做前缀。
-
修改的方法用update做前缀。
-
【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中。
-
【强制】long 或者 Long 初始赋值时,使用大写的 L,不能是小写的 l,小写容易跟数字 1 混 淆,造成误解。说明:Long a = 2l; 写的是数字的21,还是Long型的2?
2.代码注释
- 类和类成员属性、方法用文档注释: /** 注释 */;
- 方法中的代码段注释:/*注释*/;
- 方法中的代码行注释://注释;
- pigeon接口注释中加 ”服务的Url“,方法参数中的枚举加@link注清枚举详细包路径;
- 接口、类、方法、类字段必须有注释,代码块视复杂程度添加适量的逻辑解释注释;
- 不要将无用的代码注释掉,应该直接删除(git有历史记录,不用担心要用时找不回来)。
- 字段需要导出doc文档使用/** 注释 */
- 【强制】外部正在调用的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
3.方法规范
- 方法多考虑通用性,扩展性,提高重复利用率,不要添加个性化的定制业务;
- 超过3个参数可以考虑使用对象实体进行封装,方便扩展
- 单个方法代码长度控制在80行以内,超过要考虑拆出一些private子方法,这样确保主方法结构清晰易维护。
- 暴露出去的dubbo服务方法参数,如果有枚举类型,请用int,不直接用枚举类型(例如:feedType、newFeedStatus参数)。 服. 端返回的FriendDTO的对象,也不要带有枚举字段,如果在返回结果的枚举类型中添加了新的枚举值,会导致仍然在使用老的客户端的那些应用出现调用失败的情况!
- 对象实体记得实现可序列化并生成对应序列化ID,数据传输和添加缓存都依赖
- 跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误简短信息”。
- 【参考】避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。 说. : 随意复制和粘贴代码,会导致代码的重复,以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。
- 如果只有setter和getter方法,建议用lombok插件的@Data、@Setter、@Getter注解;
- 数据库查询映射使用entity(表名),字段要用包装类,不要用基本数据类型。
4.集合规范
- 【强制】在 subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、 删除均会产生ConcurrentModificationException 异常。
- 【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
- 【推荐】集合初始化时,指定集合初始值大小。 说明:HashMap使用HashMap(int initialCapacity) 初始化, 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。 反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量7次被迫扩大,resize需要重建 hash表,严重影响性能。
- 【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。 说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。 正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是 一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
- 【推荐】 Map 类集合 K/V 尽量不要存储 null 值的情况
- 【参考】 HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在 开发过程中可以使用其它数据结构或加锁来规避此风险。
- 【强制】 在 JDK7 版本及以上,Comparator 要满足如下三个条件,不然 Arrays.sort, Collections.sort 会报 IllegalArgumentException 异常。
说明:三个条件如下
- x,y的比较结果和y,x的比较结果相反
- x>y,y>z,则x>z。
- x=y,则x,z比较结果和y,z比较结果相同
5.并发规范
-
【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
-
【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
-
【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
-
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
-
FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
- CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
- 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为 static,必须加锁
- 【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。
- 【推荐】使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行 至 await 方法,需要设置最大等待时间。
6.异常处理
- 调用pigeon都应该try catch,确保服务异常不会导致系统瘫痪;非关键服务单独try catch,确保不影响主流程;
- 高QPS的服务(>100),要考虑采用降级+try catch。
- 不允许直接catch all语句,例如 catch Throwable,对于异常中的Error没法处理的。原则上最好也不要Catch Exception, 因为Exception里面有包含RuntimeException。
- 【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
- 【强制】对大段代码进行 try-catch,这是不负责任的表现。catch 时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分 异常类型,再做对应的异常处理。
- 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。
7.方法性能
- 超过三个没有依赖关系的方法,采用异步调用;
- fast return 代替 if else;
- 有通用模块或者工具类,用已有的,不要重复劳动;
- 批量或者分页查询要做上限控制;
- 万不得已不要用pagemodel,而是用list;
- 超长的富文本拼接、过于频繁的字符串拼接(>10次以上)用StringBuffer;
- 一级入口、高qps(>100)服务加缓存,加缓存前评估数据量+命中率(大数据量、低命中率慎用缓存);
- 代码里读文件的时候不要把整个文件读到一个String里面再进行各种处理,很容易造成oom,应该bufferedReader.readLine() ,一行一行的读取。
- 并发调用dubbo服务不要另开线程池,强制使用Dubbo并发组件
8.空处理规范
- 调用对象方法属性之前,做空校验(除非有try catch 或者 确定非空);
- 暴露出去的服务方法参数要做空校验;
- 空校验尽量使用第三方权威的类库apache、guava等
- 服务方法返回值:集合默认返回empty,实体默认返回null(无值情况下);
- DAO不要用基本数据类型,应用包装类型。
9.格式化规范
- 【强制】包引入、类名、类属性、类方法 之间应该空一行;
- 【强制】=、>=、&等运算符号两边有一个空格,方法参数之间应该空一格;
- 【强制】if后面必须有{},即使只有一行代码;
- 【强制】源文件编码格式为UTF-8
- 【强制】源文件内空白字符只允许“空格”和“换行符”,缩进使用4个空格
- 源文件以其顶层类名(相对于内部类)来命名,大小写敏感,首字母大写
- 【强制】去除没有用到的import
- 尽可能不用static import
- 【强制】package和import语句不换行
- 【强制】import语句里面不允许出现*
- 【强制】switch代码要显式写出default语句
- 【强制】删除无用代码、IDE自动生成的无用注释、消除warning
- 一行只有一条语句或者一个注解,最好不超过140列(保证一屏显示得下)
- 大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。
- 【强制】禁止使用魔法数(直接使用数字),使用常量代替
- 各种IDE中的换行强制使用LF
- 【强制】新写代码不允许使用被丢弃的类
10.注解规范
- 【强制】如果使用注解,建议用@Resource,禁止用@Autowired
- 被spring托管的bean中注入方式使用@Resource或Setter,非托管类才可使用SpringLocator
11.maven规范
- maven项目中的pom.xml:如果打包插件是maven-assembly-plugin,必须写明version,不能偷懒不写(否则存在子pom中的jar无法覆盖父pom中jar的bug)。
- 线上禁止使用snapshot版本,上线之前一定要升正式版本
- api对应的pom能不依赖尽量不依赖其他包,即使要依赖也应尽量依赖基础通用的包,禁止依赖公司业务包
12.日志规范
- 【强制】新项目的log必须基于日志中心提供的日志组件,
- 【强制】避免打印敏感信息,如身份证号、银行卡号等,防止日志文件外泄导致用户敏感信息泄露。
- 如果用本地log不要输出太多日志信息,避免拖慢应用系统。
- 【强制】Debug/info级别的信息,信息本身需要计算或合并的,必须加 isXxxEnabled() 判断在前,或者使用参数化日志特性。因为在关闭日志的情况下,日志语句依然会执行,从而影响性能。
- 【强制】不得使用System.out, System.err进行日志记录,请改使用log.debug、log.error
13.Job规范
- job需要比web,service还要做更详细的注释,如作业功能,调度周期,数据范围等
- job如果涉及数据库的操作,需要额外注重数据库的压力和性能,写数据需要控制频率,通常写一部分数据,sleep一段时间;读数据需要做SQL分析,避免慢SQL。
- 作业设计的时候要做好幂等处理,支持重复运行
- 要避免同时把大批数据一次性加载到内存处理
- 需要记录详尽的log,对代码做try catch,避免某步处理出错整体执行失败
14、Kafka使用规范
- 【强制】消息体中需要包含二个字段timestamp:消息产生时间,eventId或messageId:消息的唯一Id。
15、其它规范
- 【参考】慎用Enum.valueOf(),Integer.valueOf()等类型转换方法,需要做异常捕获。