Java开发手册:阿里巴巴

ゞ 浴缸里的玫瑰 2024-03-31 14:03 177阅读 0赞

文章目录

  • 一、前言
  • 二、编程规范
    • 2.1命名规范
    • 2.2常量的定义
    • 2.3代码格式
    • 2.4OOP规约
    • 2.5集合处理
    • 2.6并发处理
    • 2.7控制语句
    • 2.8注释规约
  • 三、异常日志
    • 3.1异常处理
    • 3.2日志规约
  • 四、单元测试
  • 五、安全规范
  • 六、Mysql数据库
    • 6.1建表规约
    • 6.2索引规约
    • 6.3SQL语句
    • 6.4ROM映射
  • 七、工程结构
    • 7.1应用分层
    • 7.2分层领域模型规约
    • 7.3 二方库依赖
    • 7.4服务器
  • 八、设计规约
  • 九、阿里巴巴开发手册下载

一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。

一、前言

在互联网时代,别人都说我们是搬砖的码农,但是我们知道自己是追求个性的艺术家—骨子里追求着代码的美、系统的美、设计的美,代码规范就是一个对程序美的定义。

二、编程规范

2.1命名规范

强制

  • 代码中的命名都不能以下划线(_)或者美元符号($)开始,也不能以下划线或者美元符号结束。

    反例:name/name/$name/name$
    正例:user_name

  • 代码中的命名严谨使用拼音与英文混合的方法。

  • 包名统一使用小写。
  • 类名使用UpperCameCase风格,但DO/PO/DTO/VO/AO/PO等情形例外。例如:UserDO
  • 方法名、参数名、成员变量、局部变量都统一使用lowerCameCase,必须遵循驼峰命名。例如:inputUserId
  • 常量命名全部大写,单词用下划线(_)隔开。例如:MAX_COUNT
  • 抽象类命名使用Abstact或Base开头。
  • 异常类命名使用Exception结尾。
  • 测试类名用类名开始,Test结尾。
  • 定义数组要求类型与括号相连。例如:int[] arr;
  • POJO类中的布尔型变量都不要加is,否则框架解析错误。例如:Boolean delete。

推荐

  • 如果模块、接口、类、方法使用了设计模式,应在命名体现。例如:public class OrderFactory
  • 接口类中的方法和属性不需要加任何访问修饰符,保持代码简洁性。例如:void commit();
  • 枚举类命名带上Enum后缀,枚举成员全部大写,单词用下划线(_)隔开。

参考

  • 获取单个对象的方法用get作为前缀
  • 获取多个对象的方法用list作为前缀
  • 获取统计值的方法用count作为前缀插入的方法用save / insert 作为前缀
  • 删除的方法用remove / delete 作为前缀
  • 修改的方法用update 作为前缀
  • 数据对象:xxxDO、xxx为表名
  • 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
  • 展示对象:xxxVO,xxx一般为网页名称
  • POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

2.2常量的定义

强制

  • 不允许使用魔法值,即未经预先定义的常量,直接出现在代码里,例如 String key=“Id#taobao_” +tradeId。
  • long或Long的初始值赋值,使用大写的L。例如:Long a = 21;

推荐

  • 不要用一个常量类维护所有常量。例如:CacheConsts和ConfigConsts
  • 如果变量值在一个范围内,则用enum类型。

    public enum SeasonEnum{

    1. SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);
    2. Int seq;
    3. SeasonEnum(int seq){
    4. this.seq =seq;
    5. }

    }

2.3代码格式

  • 括号的使用约定,例如下面

    public static void main(String[] args) {

    1. // 缩进4个空格,运算符的左右必须有1个空格
    2. int a = 1;
    3. int b = 2;
    4. // if与括号之间必须有一个空格,运算符左右必须有一个空格
    5. if (a == b){
    6. // 注释的双斜线与内容之间有一个空格
    7. }
    8. int c = a > b ? 1 : 2;

    }

2.4OOP规约

强制

  • 避免通过一个类的对象引用访问此类的静态变量或静态方法,造成无所谓增加编译器解析成本,直接用类名来访问即可。
  • 所有的覆盖方法,必须加@Override注解。
  • 相同数据类型,相同业务含义,才使用java的可变参数。例如:public User listUsers(String type, Long… ids)
  • 对外部正在调用或者第三方依赖的接口,不允许修改方法的签名,若接口过时,必须加@Deprecated注解,说明采用新接口是什么。
  • 不能使用过时的类或方法。
  • Object的equals方法的使用必须是:“test”.equals(a)
  • 所有相同的包装类对象之间值的比较,全部使用equals方法。
  • 所有POJO类属性必须使用包装数据类型。
  • 所有的局部变量使用基本数据类。
  • 当序列化类新增属性时,不要修改private static final long serialVersionUID = 1L,避免反序列混乱。

推荐

  • 类内方法的顺序:公有方法或保护方法->私有方法->getter/setter方法
  • 在循环体内,字符串连接推荐使用SpringBuilder的append方法进行扩展。
  • final可以声明类、成员变量、方法以及本地变量。不允许类被继承,不允许修改引用对象的地址,不允许方法被重写,不允许对基本类型数据重新赋值。
  • 慎用object的clone方法来拷贝对象。对象的clone方法时浅拷贝,若想实现深拷贝必须重写clone方法来实现对象的拷贝。

2.5集合处理

强制

  • 如果重写了equals方法必须重写hashcode方法,例如set集合
  • ArrayList的subList结果不可强转成ArrayList,否则抛出 java.lang.ClassCastException异常。
  • 在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除钧产生ConcurrentModificationException异常。
  • 使用集合转数组,必须使用集合的toArray方法,传入的类型完全一样的数组,大小是list.size()。例如:List list = new ArrayList<>();list.add(“xiaoming”);String[] strings = new String[list.size()];strings = list.toArray(strings);
  • 在使用工具类Arrays.asList方法把数组转换成集合时,不能使用其修改集合的方法,它的add/remove/clear方法会抛出UnsupportedOperationExeception异常。
  • 频繁往外读取内容用<? extends T >;频繁向里面插入的用<? super T>
  • 不要在foreach循环里进行元素的remove/add操作,remove元素请用Iterator方式,如果并发操作,需要对Iterator对象加锁。
  • 在JDK7及以上版本,Comparator要满足三个条件:x、y的比较和y、x的比较结果相反;x>y,y>z则x>z;x=y则x、y比较结果和y、z比较结果相同,不然Arrays.sort,Collections.sort会报IllegalArgumentExeception异常。

推荐

  • 在集合初始化时,指定集合的大小。例如:hashmap的初始容量=(需要存储的元素个数/负载因子)+1,默认设置容量16.
  • 使用entrySet遍历Map类集合k-v,而不是使用keySet方式遍历。
  • 利用set集合的元素唯一的特性,可以快速对集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重。

2.6并发处理

强制

  • 获取单例对象需要保证线程安全,其中的方法也要保证线程安全。例如:资源驱动类、工具类、单例工厂类都需要注意。
  • 在创建线程或线程池时,指定有意义的线程名称。
  • 线程资源必须通过线程池提供,不允许在应用中自行显示创建线程。
  • 线程池不允许使用Executors创建,要通过ThreadPoolExecutor的方式创建,这样处理能让开发明确线程池的运行规则,避免资源耗尽。
  • SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果必须要定义,那必须加锁或者使用DateUtils工具类。例如JDK8使用Instant代替Date,LocalDateTime代理DateTimeFormatter。
  • 在高并发场景中,同步调用应该去考虑锁的性能损耗。能用无锁数据结构就不要用 锁;能锁区块就要去锁整个方法体;能用对象锁就不要用类锁。
  • 在对多个资源、数据库表、对象同时加锁时,需要保持一直的加锁顺序,否则造成死锁。
  • 在并发修改同一记录时,为避免更新丢失,需要加锁。例如:加应用层锁、缓存层锁、数据库乐观锁。乐观锁的重试次数不超过3次,否则使用悲观锁。

推荐

  • 使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法被执行,避免主线程无法执行到await方法,直到超时才返回结果。
  • 避免Random实例被多线程使用,虽然是安全的,但是会竞争同一seed导致性能下降。例如:JDK1.7后使用ThreadLocalRandom。
  • 在并发场景下,通过双重检查锁实现延迟初始化的优化问题,推荐使用属性声明为volatile型。

参考

  • volatile解决多线成内存不可见问题。对于一写多读可以解决变量同步问题,但是多写就无法解决线程安全问题。
  • HashMap在容量不够时进行resize,由于高并发可能出现死链,导致cpu飙升,在开发中可以用别的数据结构或者加锁解决。
  • ThreadLocal无法解决共享对象的更新问题,ThreadLocal对象建议使用static修饰。

2.7控制语句

强制

  • 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪个case为止。在一个switch块内,必须包含一个default语句并且放到最后,即使它什么都没有。
  • 在if/else/for/while/do语句中,即使只有一行代码也必须使用大括号。
  • 在高并发场景中,避免使用”等于号“判断作为中断或者退出条件。

推荐

  • 超过三层的if-else逻辑判断可以使用卫语句、策略模式、状态模式等来实现。
  • 循环体中的语句,像定义对象或者变量、获取数据库连接,经量放在体外处理,避免进行不必要的try-catch操作。
  • 避免采用取反逻辑。
  • 接口入参保护
  • 需要进行参数校验的情况:调用频率低的方法、执行时间开销大的方法、对外提供的开放接口、敏感权限入口。
  • 不需要进行参数的校验:被循环调用的方法、底层调用频度高的方法、自己的private方法。

2.8注释规约

强制

  • 类、类属性、类方法的注释必须使用Javadoc规范。提高阅读效率。
  • 所有抽象方法方法必须要用Javadoc注释。
  • 所有类都必须添加创建者和创建日期。
  • 方法内的单行注释,上方另起一行用//注释。
  • 所有枚举类型字段必须要有注释,说明每个数据项的用途。

推荐

  • 修改代码的同时记得修改注释
  • 注释代码记得说明
  • //TODO表示还未实现

三、异常日志

3.1异常处理

强制

  • 通过预检查方法规避RuntimeException异常,不要通过carch的方法处理。例如数组越界、空指针问题。
  • 异常不要用来 做流程控制、条件控制。
  • 异常捕获是为了处理异常,不要什么都不做。
  • 有try块放在事务代码中,catch异常后,如果需要回滚异常,一定要注意rollback事务,换言之,就是catch块不会去回滚事务,必须自己手动回滚。
  • finnally块必须对资源对象、流对象进行关闭操作,jdk7及以上可以使用try-with-resources方式。
  • 不能再finally块中使用return。
  • 捕获异常与抛异常必须完全匹配或者是抛异常的父类。

推荐

  • 防止NPE:返回类型为基本数据类型,return 包装类型的对象时,自动拆箱有可能产生NPE、数据库查询结果可能为null、集合可能为空或者元素为null、远程调用返回的对象可能为null、session中获取的数据可能为null;可使用jdk1.8的Optional来防止NPE问题。

3.2日志规约

强制

  • 应用中不可直接使用日志系统(Log4j、LogBack)中的API,而应该使用日志框架Slf4j中的API,使用门面模式的日志框架有利于维护和各类的日志处理方式统一。
  • 日志文件至少保存15.
  • 应用中的扩展日志命名:appName_logType_logName.log
  • 对于trance/debug/info日志输出形式 请以占位符方式。
  • 避免重复打印日志:在日志配置文件中设置additivity =false

四、单元测试

强制

  • 单元测试必须遵循AIR原则:A:automatic 自动化、I:independent 独立性、R:repeatable 可重复性
  • 单元测试必须是全自动执行的。可重复执行的。
  • 单元测试使用assert验证。
  • 单元测试不能相互调用。

五、安全规范

强制

  • 属于用户个人的页面或者功能必须进行权限控制校验。例如不能查看别人的私信。
  • 用户敏感数据禁止直接展示,必须对数据进行脱敏。例如手机号中间四位掩码。
  • 用户输入的sq参数样使用参数绑定或者字段值限制,防止sql注入,禁止字符串拼接。
  • 禁止向html页面输出未经安全过滤或者未正确转义的用户数据。
  • 表单、AJax提交必须执行CSRF安全过滤。
  • 在使用平台资源(短信、邮件、电话、下单支付等)必须实现正确的防重放限制,如数量限制、疲劳度控制、验证码校验,避免烂刷。
  • 针对发帖、评论、发送即使信息等用户生成内容的场景,必须实现防刷、文本内容违禁词过滤等风控策略。

六、Mysql数据库

6.1建表规约

强制

  • 表达是否的字段,例如是否删除,必须使用is_xxx的方式命名。数据类型时unsigned tinyint(1是/0否)。
  • 表名、字段名必须使用小写字母或者数字,禁止数字开头
  • 表名不适用复数名词。
  • 禁用保留字,例如desc、range、match、delayed等
  • 主键索引命名为:pk+下划线+字段名,唯一索引:uk+下划线+字段名,普通索引:inx+下划线+字段名。
  • 小数类型为decimal,禁止使用float和double。
  • 字符串使用varchar。如果长度超过5000个字符则使用text类型,独立出一张表来,用主键来对应。
  • 表必备三个字段:id(unsigned bigint)、create(datetime)、modified(datetime)。

推荐

  • 表命名最好加上“业务名称”+“表的作用“,例如:alipay_task
  • 苦命与应用名称尽量一致。
  • 修改字段时需要添加或者修改注释
  • 当表行数超过500万行或者单表容量超过2gb,建议分库分表。
  • 设置合适的字符串长度,节约数据库表空间和索引存储。

6.2索引规约

强制

  • 业务上具有唯一特性的字段,或者多个字段的组合构成唯一。
  • 超过间隔表禁止join,需要join的字段,数据类型必须绝对一致,多表关联查询的字段需要有索引。
  • 在varchar字段上建立索引,必须指定索引长度。
  • 页面搜索禁止左模糊或者全模糊,如果需要请通过搜索引擎来解决。

推荐

  • 利用覆盖索引进行查询操作,避免回表。
  • 利用延迟关联或者子查询优化超多分页场景。
  • sql性能优化,至少达到range级别(对索引进行范围检查),要求时ref级别(使用普通索引),最好是consts(单表中最多只有一个匹配行)。
  • 组合索引的时候,区分度最高的在最左边。
  • 防止因字段类型不哦那个导致的隐式转换,导致索引失效。

6.3SQL语句

强制

  • 不要使用count(列名) 或count(常量)来代替count(*)。count(星号)会统计值为null的行,而count(列明)不会统计此列为null值的行。
  • count(distinct col)计算该列除null外的不重复的行数,count(distinct col1,col2)中有一列为null,即使另外一列有不同的值,页返回0;
  • 当某列值为null,sum()返回null,请求使用if(isnull(sum(g),0,sum(g)) from table;
  • 使用isnull()来判断是否为null值。
  • 分页查询时,若count为0则直接返回。
  • 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
  • 不能使用外键与级联。
  • 尽量避免in操作,使用in的集合元素控制在1000内。

6.4ROM映射

强制

  • 在表查询中,不要使用 * 作为查询的字段列表,会增加查询分析器解析成本、增减字段容易与resultMap配置不一致。
  • pojo类的布尔类型的属性不能加is,而数据库字段必须加is_,要求在resultMap中进行字段与属性之间的映射。
  • 不要用reslutClass作为返回参数,配置映射关系,是字段和DO类解耦。
  • sql.xml配置参数使用:#{},#param,不要使用美元,防止sql注入。
  • mybatis自带的queryForList不推荐使用。
  • 不允许直接HashMap和HashTable作为查询结果集的输出,值类型会不可控。
  • 更新表记录时,必须同时更新修改时间。

七、工程结构

7.1应用分层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GqJDlxx3-1670054713967)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1670051466421.png)]

  1. 开发接口层:可直接封装Service方法暴露成RPC接口;通过web封装成http接口,进行网关安全控制、流量控制等。
  2. 终端显示层:各个端的模板渲染并执行显示的层。主要是js渲染、jsp渲染、velocity渲染、移动端显示等。
  3. web层:主要是对访问控制进行转发,对基本参数校验或对不复用的业务简单处理等。
  4. service层:相对具体的业务逻辑服务层。
  5. manager层:通用业务处理层

    • 对第三方平台封装的层,预处理返回结果以及转化异常信息。
    • 对service层通用能力的下沉,如缓存方案、中间件处理
    • 与dao层交互,对多个dao的组合复用。
  6. dao层:数据访问层,与msql、oracle、Hbase等进行数据交互。
  7. 外部接口或第三方平台:包括其他部门RPC开发接口,基础平台,其他公司的http接口。

7.2分层领域模型规约

  1. DO:date Object :与数据库表结构一一对应。
  2. DTO:date transfer object :数据传输对象
  3. BO:business object : 业务对象,由service层输出的封装业务逻辑的对象。
  4. AO:application object:应用对象,在web层和service层之间抽象的复用对象模型
  5. VO:view object:显示层对象
  6. query:数据查询对象

7.3 二方库依赖

  • 定义GAV遵行以下规则

GroupID格式:com.{公司/BU}.业务线.{子业务线},最多四级

ArtifactID格式:产品线名-模块名

version:主版本号.次版本号.修订号

  • 线上应用不要依赖SNAPSHOT版本,保证应用发布的幂等性。
  • 二方库里面可以定义枚举类型,参数使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的pojo对象。
  • 依赖一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。
  • 避免在子项目的pom依赖出现相同的groupid、ArtifactID,但是不同的version。
  • 二方库不要有配置项。

7.4服务器

  • 高并发服务器建议调小tcp协议的time_wait超时时间,操作系统默认240s后,关闭处理time_wait的连接。在linux服务器上可通过改变/etc/sysctl.conf文件去修改net.ipv4.tcp_fin_timeout=30;
  • 调大服务器所支持的最大文件句柄数(File Descriptor)
  • 给jvm设置-xx:+HeapDumpOnOutOfMemoryError参数,让jvm碰到OOM场景能输出dump信息。
  • 线上生产环境,JVM的Xms和Xmx设置一样大小的内存容量,避免GC后调整堆大小带来的压力。

八、设计规约

强制

  • 存储方法和底层数据结构的设计获得评审一致后定位文档。
  • 在需求分析阶段,如果与系统交互的User超过1类,并且相关的User Case超过5个,那么使用用例图来表达结构化需求更加清楚。
  • 如果一个业务对象的状态超过3个,使用状态图来表达。
  • 如果某系统某个功能的调用链路上涉及的对象超过3个,则使用时序图。
  • 如果系统中模型类超过5个,且存在复杂的依赖关系,则使用类图。
  • 如果系统中超过2个对象之间存在协作关系,并需要处理复杂流程,则使用活动图,

推荐

  • 类在设计与实现要符合单一原则
  • 谨慎使用继承的方式进行扩展,优先使用组合。
  • 尽量依赖抽象类与接口

    九、阿里巴巴开发手册下载

    阿里巴巴Java开发手册百度网盘下载连接: https://pan.baidu.com/s/1gDezB-92jGKqnlEizu5yRg
    提取码: shen

发表评论

表情:
评论列表 (有 0 条评论,177人围观)

还没有评论,来说两句吧...

相关阅读