工程结构
(一) 应用分层
【推荐】图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web层,也可以直接依赖于Service层,依此类推:• 开放接口层:可直接封装Service方法暴露成RPC接口;通过Web封装成http接口;网关控制层等。• 终端显示层:各个端的模板渲染并执行显示的层。当前主要是velocity渲染,JS渲染,JSP渲染,移动端展示等。• Web层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。• Service层:相对具体的业务逻辑服务层。• Manager层:通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息。 2) 对Service层通用能力的下沉,如缓存方案、中间件通用处理。 3) 与DAO层交互,对多个DAO的组合复用。• DAO层:数据访问层,与底层MySQL、Oracle、Hbase、OB等进行数据交互。• 外部接口或第三方平台:包括其它部门RPC开放接口,基础平台,其它公司的HTTP接口。
【参考】(分层异常处理规约)在DAO层,产生的异常类型有很多,无法用细粒度的异常进 ...
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
【强制】表名不使用复数名词。 说 ...
安全规约
【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。 说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容。
【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。 说明:中国大陆个人手机号码显示为:137****0969,隐藏中间4位,防止隐私泄露。
【强制】用户输入的SQL参数严格使用参数绑定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL访问数据库。反例:某系统签名大量被恶意修改,即是因为对于危险字符 # –没有进行转义,导致数据库更新时,where后边的信息被注释掉,对全库进行更新。
【强制】用户请求传入的任何参数必须做有效性验证。 说明:忽略参数校验可能导致:⚫ page size过大导致内存溢出⚫ 恶意order by导致数据库慢查询⚫ 缓存击穿⚫ SSRF⚫ 任意重定向⚫ SQL注入,Shell注入,反序列化注入⚫ 正则输入源串拒绝服务ReDoSJava代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。
【强制】禁止向 ...
单元测试
【强制】好的单元测试必须遵守AIR原则。 说明:单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。 ⚫ A:Automatic(自动化) ⚫ I:Independent(独立性) ⚫ R:Repeatable(可重复) 2. 【强制】单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用System.out来进行人肉验证,必须使用assert来验证。 3. 【强制】保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。 反例:method2需要依赖method1的执行,将执行结果作为method2的输入。 4. 【强制】单元测试是可以重复执行的,不能受到外界环境的影响。 说明:单元测试通常会被放到持续集成中,每次有代码check in时单元测试都会被执行。如果单测对外部环境(网络、服务、中间件等)有依赖, ...
异常日志
(一) 错误码
【强制】错误码的制定原则:快速溯源、简单易记、沟通标准化。 说明: 错误码想得过于完美和复杂,就像康熙字典中的生僻字一样,用词似乎精准,但是字典不容易随身携带并且简单易懂。正例:错误码回答的问题是谁的错?错在哪?
错误码必须能够快速知晓错误来源,可快速判断是谁的问题。
错误码易于记忆和比对(代码中容易equals)。
错误码能够脱离文档和系统平台达到线下轻量化地自由沟通的目的。
【强制】错误码不体现版本号和错误等级信息。 说明:错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义来决定。
【强制】全部正常,但不得不填充错误码时返回五个零:00000。
【强制】错误码为字符串类型,共5位,分成两个部分:错误产生来源+四位数字编号。 说明:错误产生来源分为A/B/C,A表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付超时等问题;B表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C表示错误来源于第三方服务,比如CDN服务出错,消息投递超时等问题;四位数字编号从0001到9999,大类之间的步长间距预留100 ...
阿里巴巴编程规范-(十) 其它
【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。 说明:不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);
【强制】避免用Apache Beanutils进行属性的copy。 说明:Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier,注意均是浅拷贝。
【强制】velocity调用POJO类的属性时,直接使用属性名取值即可,模板引擎会自动按规范调用POJO的getXxx(),如果是boolean基本数据类型变量(boolean命名不需要加is前缀),会自动调用isXxx()方法。 说明:注意如果是Boolean包装类对象,优先调用getXxx()的方法。
【强制】后台输送给页面的变量必须加$!{var}——中间的感叹号。 说明:如果var等于null或者不存在,那么${var}会直接显示在页面上。
【强制】注意 Math.random() 这个方法返回是double类型,注意取值的范围 0≤x<1(能够取到零值,注意 ...
阿里巴巴编程规范-(九) 注释规约
【强制】类、类属性、类方法的注释必须使用Javadoc规范,使用/*内容/格式,不得使用// xxx方式。 说明:在IDE编辑窗口中,Javadoc方式会提示相关注释,生成Javadoc可以正确输出相应注释;在IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
【强制】所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 说明:对子类的实现要求,或者调用注意事项,请一并说明。
【强制】所有的类都必须添加创建者和创建日期。 说明:在设置模板时,注意IDEA的@author为${USER},而eclipse的@author为${user},大小写有区别,而日期的设置统一为yyyy/MM/dd的格式。 正例: /**
@author yangguanbao
@date 2016/10/31*/
【强制】方法内部单行注释,在被注 ...
阿里巴巴编程规范-(八) 控制语句
【强制】在一个switch块内,每个case要么通过continue/break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。 说明:注意break是退出switch语句块,而return是退出方法体。
【强制】当switch括号内的变量类型为String并且此变量为外部参数时,必须先进行null判断。 反例:如下的代码输出是什么?public class SwitchString {public static void main(String[] args) {method(null);}public static void method(String param) {switch (param) {// 肯定不是进入这里case “sth”:System.out.println(“it’s sth”);break;// 也不是进入这里case “null”:System.out.println(“it’s ...
阿里巴巴编程规范-(七) 并发处理
【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。 说明:资源驱动类、工具类、单例工厂类都需要注意。
【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。 正例:自定义线程工厂,并且根据外部特征进行分组,比如,来自同一机房的调用,把机房编号赋值给whatFeaturOfGrouppublic class UserThreadFactory implements ThreadFactory {private final String namePrefix;private final AtomicInteger nextId = new AtomicInteger(1);// 定义线程组名称,在jstack问题排查时,非常有帮助UserThreadFactory(String whatFeaturOfGroup) {namePrefix = “From UserThreadFactory’s “ + whatFeaturOfGroup + “-Worker-“;}@Overridepublic Thread newTh ...
阿里巴巴编程规范-(六) 集合处理
【强制】关于hashCode和equals的处理,遵循如下规则: 1) 只要重写equals,就必须重写hashCode。 2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。 3) 如果自定义对象作为Map的键,那么必须覆写hashCode和equals。 说明:String因为重写了hashCode和equals方法,所以我们可以愉快地使用String对象作为key来使用。
【强制】判断所有集合内部的元素是否为空,使用isEmpty()方法,而不是size()==0的方式。 说明:前者的时间复杂度为O(1),而且可读性更好。 正例:Map<String, Object> map = new HashMap<>();if(map.isEmpty()) {System.out.println(“no element in this map.”);}
【强制】在使用java.util.stream.Collectors类的toMap()方法转为Map集合时,一定要 ...