02.阿里巴巴编程规范

1.命名规范
1.类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等
2.方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
3.抽象类命名使用Abstract或Base开头;异常类命名使用Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test结尾。
4.如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。
5.接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。
6.枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

7.各层命名规约:
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。
2.常量定义
1.不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。
2.如果变量值仅在一个固定范围内变化用 enum 类型来定义。
3.代码格式
1.左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;而左大括号前需要空格。
2.if/for/while/switch/do 等保留字与括号之间都必须加空格。
3.任何二目、三目运算符的左右两边都需要加一个空格。
4.采用 4 个空格缩进,禁止使用 tab 字符。
5.注释的双斜线与注释内容之间有且仅有一个空格。
6.单行字符数限制不超过 120 个,超出需要换行,
7.方法参数在定义和传入时,多个参数逗号后边必须加空格。
8.单个方法的总行数不超过 80 行。
9.不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。
4.并发处理
1.线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
2.线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。
3.高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;
4.对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
5.并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
6.使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至 await 方法,直到超时才返回结果
7.ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static修饰。
5.注释规范
1.类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用// xxx 方式。
2.所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能;
3.所有的类都必须添加创建者和创建日期。
4.方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
5.所有的枚举类型字段必须要有注释,说明每个数据项的用途。
6.代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
7.谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
8.对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书。
9.好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
6.编码规范其它
1.在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
2.获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();
3.不要在视图模板中加入任何复杂的逻辑。
4.及时清理不再使用的代码段或配置信息。对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)来说明注释掉代码的理由。
5.任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
7.异常处理
1.catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。
	对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理
	
2.捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。
3.finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
4.不要在 finally 块中使用 return
5.方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。
6.防止 NPE,是程序员的基本修养

7.定义时区分 unchecked / checked 异常,避免直接抛出 new RuntimeException(),
	更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,
	如:DAOException / ServiceException 等
	
8.对于公司外的 http/api 开放接口必须使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误简短信息”。
8.日志处理
1.对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。
	if (logger.isDebugEnabled()) {
		logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
	}
	
2.避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false;
	<logger name="com.taobao.dubbo.config" additivity="false">
	
3.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出.
	logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
	
4.大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。
	记录日志时请思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
	
5.可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。
	如非必要,请不要在此场景打出 error 级别,避免频繁报警
9.单元化测试
1.好的单元测试必须遵守AIR原则A:Automatic(自动化)/I:Independent(独立性)/R:Repeatable(可重复)
2.单元测试应是全自动执行的,并且非交互式。测试用例通常是被定期执行,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用System.out来进行人肉验证,必须使用 assert 来验证。
3.保持单元测试独立性。为保证单元测试稳定可靠且便于维护,单元测试用例之间不能互相调用,也不能依赖执行先后次序。
4.单元测试是可以重复执行的,不能受到外界环境的影响。
5.对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别
6.核心业务、核心应用、核心模块的增量代码确保单元测试通过。
7.单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下

8.编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。
	B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
	C:Correct,正确的输入,并得到预期的结果。
	D:Design,与设计文档相结合,来编写单元测试。
	E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果
	
9.和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或对单元测试产生的数据有明确的前后缀标识
10.对于不可测的代码建议做必要的重构,使代码变得可测,避免为了达到测试要求而书写不规范测试代码
11.在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例。
12.单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项目提测前完成单元测试。

13.为了更方便地进行单元测试,业务代码应避免以下情况:
	构造方法中做的事情过多。
	存在过多的全局变量和静态方法
	存在过多的外部依赖。
	存在过多的条件语句。多层条件语句建议使用卫语句、策略模式、状态模式等方式重构
10.安全规约
1.隶属于用户个人的页面或者功能必须进行权限控制校验
2.用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
3.用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止SQL注入,禁止字符串拼接 SQL 访问数据库

4.用户请求传入的任何参数必须做有效性验证
	page size 过大导致内存溢出
	恶意 order by 导致数据库慢查询
	SQL 注入
	反序列化注入
	
5.禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据
6.在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损
11.数据库规范
1.表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)
2.表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字,字段名称需要慎重考虑
3.表名不使用复数名词
4.主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名
5.小数类型为 decimal,禁止使用 float 和 double。
6.如果存储的字符串长度几乎相等,使用 char 定长字符串类型
7.varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率
8.表必备三字段:id, gmt_create, gmt_modified
9.表的命名最好是加上“业务名称_表的作用”
10.库名与应用名称尽量一致
11.如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释
12.单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
13.合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
12.索引规范
1.业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引
2.超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
3.在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可
4.页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引
5.如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能
6.利用覆盖索引来进行查询操作,避免回表。
7.利用延迟关联或者子查询优化超多分页场景。
8.建组合索引的时候,区分度最高的在最左边。
9.防止因字段类型不同造成的隐式转换,导致索引失效
13.SQL查询
1.不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关
2.使用 ISNULL()来判断是否为 NULL 值
3.在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句
4.不得使用外键与级联,一切外键概念必须在应用层解决。
5.禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
6.数据订正(特别是删除、修改记录操作)时,要先 select,避免出现误删除,确认无误才能执行更新语句
7.in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内
8.在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明
9.POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射
10.不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义;反过来,每一个表也必然有一个 POJO 类与之对应。配置映射关系,使字段与 DO 类解耦,方便维护
11.sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入
12.不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。resultClass=”Hashtable”,会置入字段名和属性值,但是值的类型不可控
13.@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等
14.应用分层

1.分层领域模型规约
	DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
	DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。 
	BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象。 
	AO(Application Object):应用对象,在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。 
	VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
	Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
	
2.分层异常处理规范:
	在 DAO 层,产生的异常类型有很多,无法用细粒度的异常进行 catch,使用 catch(Exception e)方式,并 throw new DAOException(e),不需要打印日志,
	
	在 Manager/Service 层一定需要捕获并打印到日志文件中去,如果同台服务器再打日志,浪费性能和存储。在 Service 层出现异常时,必须记录出错日志到磁盘,尽可能带上参数信息,相当于保护案发现场。
	
	如果 Manager 层与 Service 同机部署,日志方式与 DAO层处理一致,如果是单独部署,则采用与 Service 一致的处理方式。
	
	Web 层绝不应该继续往上抛异常,因为已经处于顶层,如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面,加上用户容易理解的错误提示信息。
	
	开放接口层要将异常处理成错误码和错误信息方式返回。
15.二方库规范
1.定义 GAV 遵从以下规则
	GroupID 格式:com.{公司/BU }.业务线 [.子业务线],最多 4 级。com.taobao.jstorm
	ArtifactID 格式:产品线名-模块名。语义不重复不遗漏。dubbo-client/fastjson-api
	Version:主版本号.次版本号.修订号,起始版本号必须为:1.0.0,而不是 0.0.1。正式版本号不允许覆盖升级
	
2.线上应用不要依赖 SNAPSHOT 版本

3.二方库的新增或升级,保持除功能点之外的其它 jar 包仲裁结果不变。
	如果有改变,必须明确评估和验证,建议进行 dependency:resolve 前后信息比对,
	如果仲裁结果完全不一致,那么通过 dependency:tree 命令,找出差异点,进行<excludes>排除 jar 包

4.二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的POJO对象
5.依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致

6.所有pom 文件中的依赖声明放在<dependencies>语句块中,所有版本仲裁放在<dependencyManagement>语句块中
	<dependencyManagement>里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖,
	version 和 scope 都读取自父 pom。
	而<dependencies>所有声明在主 pom 的 <dependencies>里的依赖都会自动引入,并默认被所有的子项目继承
	
7.为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则
	精简可控原则。移除一切不必要的 API 和依赖,只包含 Service API、必要的领域模型对象、Utils 类、常量、枚举等。如果依赖其它二方库,尽量是 provided 引入,让二方库使用者去依赖具体版本号;无 log 具体实现,只依赖日志框架。
16.服务器
1.高并发服务器建议调小 TCP 协议的 time_wait 超时时间
	服务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接。
	在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值(秒):
	net.ipv4.tcp_fin_timeout = 30
	
2.调大服务器所支持的最大文件句柄数(File Descriptor,简写为 fd)
	主流的 linux 服务器默认所支持最大 fd 数量为 1024,
	当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 
	建议将 linux服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)
	
3.给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。	
4.在线上生产环境,JVM 的 Xms 和 Xmx 设置一样大小的内存容量,避免在 GC 后调整堆大小带来的压力
5.服务器内部重定向使用 forward;外部重定向地址使用 URL 拼装工具类来生成,否则会带来 URL 维护不一致的问题和潜在的安全风险
17.设计规范
1.类在设计与实现时要符合单一原则
2.谨慎使用继承的方式来进行扩展,优先使用聚合/组合的方式来实现
3.系统设计时,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护
4.系统设计时,注意对扩展开放,对修改闭合
5.系统设计阶段,共性业务或公共行为抽取出来公共模块、公共配置、公共类、公共方法等,避免重复代码或重复配置的情况
6.避免如下误解:敏捷开发 = 讲故事 + 编码 + 发布。
7.系统设计主要目的是明确需求、理顺逻辑、后期维护,次要目的用于指导编码
8.设计的本质就是识别和表达系统难点,找到系统的变化点,并隔离变化点。

9.系统架构设计的目的:
	确定系统边界。确定系统在技术层面上的做与不做。
	确定系统内模块之间的关系。确定模块之间的依赖关系及模块的宏观输入与输出
	确定指导后续设计与演化的原则。使后续的子系统或模块设计在规定的框架内继续演化
	确定非功能性需求。非功能性需求是指安全性、可用性、可扩展性等
posted @ 2022-10-18 21:02  NIANER2011  阅读(1603)  评论(0编辑  收藏  举报