读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

    不为过去蹉跎,改变当下。

  为什么开篇就送这么一句话给大家,我相信很多处于1-3年码龄的哥们儿们,在平时的编码历程中编码的个性可能是多彩的,每个人都有每个人特定的风格,但是我们现在这么随意写,以后这么随意写,好没问题,但是等你离开这个公司了或者是去开发别的项目了,再等别人过来接手维护你一手写出来的这段个性十足的代码时,那么你的右眼皮时不时地就会跳,因果我就不说了~~

  所以我建议看到这篇博文的朋友们,或许你稍微改变一下你的编码风格,遵从一套好的编码规约对己对人都是有好处的。可能朋友的公司也有专门的编码规约,但是逃不了我们平时在开发一些属于自己的项目,比如有一天从李智慧老师的《大型网站技术架构:核心原理与案例分析》著作中学到一些非常好的东西,那么我们就想开始动手编码来实现一下,这时候如果尽量遵从一套很好的编码规约风格,将写出来的代码分享给身边的同事学习一下,提提建议多好。

  2017-05-20这一天,阿里巴巴集团技术团队终于发布了版本号:1.2.0的Java开发手册,在这之前他们总共发布过5个版本,都有幸被我看到了。当时在地铁上一贯地翻开手机,直接打开《阿里技术》微信公众号,就一眼瞅到了"抢鲜下载|阿里Java开发手册最新完美版,千锤百炼始出炉——阿里巴巴Java开发手册v1.2.0",当时第一反应就是赶紧先拿到它,接着迅速找到下载入口,完美地保存在手机上了。

  快速飘过前言,手册的愿景就是码出高效,码出质量。所谓无规矩不成方圆,无规范不能协作。质量的提升是告诉我们尽可能少踩坑,少吃那些无用的亏,高效协作即降低协同成本,提升沟通效率,再说现在的软件架构都需要多个程序员协同开发完成,公司也不可能让某个技术大牛独自去完成,所以让大家统一方式一起做事,才能提升开发效率。

  手册结构总共由5部分组成:编程规约、异常日志、MySQL数据库、工程结构和安全规约。

  一:编程规约

  首先来说说编程规约吧,这部分是我个人极力推荐各位朋友能够借鉴,一个程序员有一个好的编码风格,真的是异于其他码农。我们谁都想要一个好听的名字,除了好听这个名字它还要有特殊的意义。在编码的过程中我们定义一个变量也是一样的,假如你定义的变量很烂,被人看到不仅会鄙视你,还会从心里认为你没文化,就算我们英文不好,但是也可以百度啊,让人看到一眼就能见名知意,多和谐啊~~

  1、命名风格

  见名知意;类名、方法名、参数名、成员变量和局部变量使用驼峰式(但是这里强调一下,我们项目中难免会有很多分层领域的模型类,比如DO/BO/DTO/VO/AO,这些可以使用UserDO、QueryVO等形式);常量名大写;抽象类命名以Abstract或Base开头;异常类命名使用Exception结尾;测试类以Test结尾;包名统一使用小写;接口中的方法和属性尽量不要加任何修饰符(如果可以public也不要加),为了代码简洁;任何pojo类中布尔类型的变量,不要使用is做前缀,否则部分框架解析会引起序列化错误。

  我们现在软件项目大部分都是分层的,对各个层中的方法和模型类地命名也有一定的讲究,Service/DAO层中各方法的命名:获取单个对象的方法采用get做前缀;获取多个对象的方法采用list做前缀;获取统计值的方法采用count做前缀;插入的方法采用save或insert做前缀,但是推荐使用save;删除的方法采用remove或delete做前缀,但是推荐使用remove;修改的方法采用update做前缀。Service/DAO层中各模型类的命名:数据对象采用XXXDO,一般和数据库中的表名对应;数据传输对象采用XXXDTO,一般是与业务领域相关的传输对象;视图层展示对象采用XXXVO,一般用于传递到视图层。

  2、代码格式:

  (1)、if语句的使用方面,小括号内的俩端不要出现空格并且if关键字后面添加一个空格(不光是if关键字,在if/for/switch/while/do关键字后面都要有空格);

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  (2)、在二目、三目运算符的左右都建议添加一个空格,比如=、==、&&、+-/*等运算符左右都应该添加空格;

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  (3)、在代码缩进方面,尽量不要使用tab键,建议使用4个空格缩进,如果单行代码超过120个字符,则建议换行;

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  (4)、多个参数同时传递时,逗号前建议有个空格;

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  3、OOP规约:

  访问此类的静态变量或静态方法时,直接使用类名访问,无须通过对象引用访问此类的静态资源,如果你使用对象引用访问则无谓增加编译器解析成本;所有的覆盖方法必须添加@Override注解;在使用object类的equals方法时,尽量使用确定的值来调用equals方法,避免出现空指针异常(参考下图代码);所有的pojo类的属性必须使用包装类;定义任何pojo类的属性时,不要设定任何属性的默认值;构造方法中进制加入任何业务逻辑代码;建议所有的pojo类都添加toString()方法,方便排查问题。

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  4、并发处理:

  获取单例对象需要保证线程安全,其中的方法也要保证线程安全;线程资源需要通过线程池获取,不建议显式创建线程;对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。

  5、注释规约

  首先所有的类都应该添加创建者和创建日期;方法内部添加注释时,尽量采用单行注释,注释添加在被注释的代码上方,如果强制使用多行/**/注释,则应该与代码对齐;所有的抽象方法和接口中的方法,都必须在方法上面采用多行注释/***/解析清楚该方法是用来干什么的,实现了什么功能;如果某个方法的功能还未完善,则必须添加待办事宜TODO注释。

  读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

  二:异常日志

  对于异常日志这部分,我们绝对不能忽视,你不敢保证你写出的代码永远不会挂吧,那么我们就必须借助日志的力量来快速定位到问题的位置,以便解决。

  1、异常处理:

  对大段代码进行try-catch,这是不负责任的表现,我们应该精确处理,建议尽量将try块的代码放到事务代码中,一旦代码抛出异常,则必须回滚事务,一定要注意手动回滚事务;如果代码中使用到流等资源finally块必须对它们进行关闭,必须做,而且在关闭的时候如果可能出现异常则必须进行try-catch处理;不能在finally块中使用return语句。

  2、日志规约:

  我们平时的开发过程中,避免不了定时查看日志,分析日志,建议日志文件中的内容必须保存在15天以上,因为有些异常具备以“周”为频次发生的特点;推荐对日志进行分类,将系统运行产生的错误日志和系统运行产生的业务日志分开存储,方便开发人员查看;谨慎的记录日志,生产环境禁止输出debug日志,有选择性的输出info日志,一定要注意日志输出量的问题,避免把硬盘撑爆。

  三:MySQL数据库

  作为一名Java开发程序员,样样都得会点儿,不仅得辛苦的敲代码,而且还要具备大局观有时候还需要你来设计库结构和表关系,但是这时候我们尽量不要随意来搞,说不好听了你一旦随意起来,那么有一天你会前功尽弃地,因为我们的程序大多都是依托数据来运行的,所以,朋友们如果真要你遇上设计库表时,请认真对待。

  (1)、建表规约

  首先在命名上,如果某个表的字段代表是与否的概念,则应该用is_XXX的方式开头,数据类型应该是unsigned tinyint(1表示是、0表示否);表名和字段名应该使用小写或数字,但是不能以数字开头,切记数据库中的字段名修改起来代价可是非常大的,因为无法进行预发布,所以在建表时命名字段名称时一定要慎之又慎;表名不适用负数名词,我们在开发中习惯将表名定义成负数名词,代表多个记录的统称,这样是不建议的;如果某个字段的数据类型为小数时,建议定义成decimal,不建议使用float或double,因为float或double有可能存在精度丢失,在进行的值的比较时会得到错误的结果,如果存储的数据超过decimal数据类型的存储范围时,建议将整数和小数分开存储;如果存储的字符串长度几乎相等,则建议使用定长char;创建一张表,必备的三个字段:id(主键)、gmt_create(创建时间)、gmt_modified(修改时间);表的命名一般建议是加上业务名称来作为表前缀;表中的字段允许存在冗余,以便提高查询效率,但是不能是varchar超长长度,更不能是text数据类型;单表行数超过500万行或大小超过2GB时,才建议分库分表。

  (2)、索引规约

  业务上具有唯一特性的字段,或者是多个字段的组合,也必须建成唯一索引;在查询时超过3张表禁止使用join,需要join的字段,数据类型必须一致,多表关联查询时,保证被关联的字段需要有索引;页面搜索禁止走左模糊或者全模糊,如果需要应该搜索引擎来解决;如果有order by的场景,请注意利用索引的有序性,order by最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询效率;SQL性能优化的目标:至少达到range级别(对索引进行范围检索)、要求是ref级别(指的是使用普通的索引)、如果可以的达到consts(单表中只有一个行匹配,即主键或唯一索引,在优化阶段即可读取到数据)最好;防止因字段类型不同导致的隐式转换,导致索引失效。

  (3)、SQL语句:

  我们在书写sql语句时大多数人都存在一个习惯,就是用count(列名)或count(常量)来代替count(*),count(*)是SQL92定义的标准的统计行数的语法,跟数据库无关,跟NULL和非NULL有关,请记住这句话:count(*)会统计值为NULL的记录,但是count(列名)是不会统计NULL的记录,导致数据读取错误;在代码中写分页查询逻辑时,若总计录数为0时,应该直接返回,避免执行后面的分页语句;禁止使用存储过程,因为存储难以调试和扩展,更没有移植性;我们在进行删除或修改记录时,要学会先select一下,接着再做delete或update也不迟,以免眼疾手快造成毁灭性的灾难;在使用in关键字是能避免则尽量避免,如果避免不了,应该仔细评估in后面的集合元素数量,尽量控制在1000以内,以免影响查询效率。

  (4)、ORM映射:

  在mapper文件中进行全表查询时,一律不要使用*号,需要哪些字段则应该明确写明;pojo类中的属性不应该以is开头,但是数据库中的字段名称必须以is_XXX开头,这时可以在resultMap中进行字段和属性的映射;在mapper文件中尽量不要使用${},应该使用#{},以免造成SQL注入问题;不允许直接拿HashMap和HashTable作为查询结果的输出;@transactional注解尽量不要滥用,事务会影响数据库的QPS,另外使用事务的地方,必须要考虑好事务回滚的最优方案,包括缓存回滚、搜索引擎回滚、消息补偿和统计修正等问题;

  四:安全规约

  我们在开发中不仅要最求效率和质量,也要做到百毒不侵,做好系统的安全工作。一般系统中牵扯到用户个人的页面或者功能时必须做权限校验;用户的一些敏感数据不能直接展示,必须对展示数据进行脱敏;用户输入的SQL参数必须严格进行参数绑定或者metadata字段值限定的操作,防止发生SQL注入的问题,切记:禁止允许用户使用字符串拼接SQL的方式来访问数据库,这危害可就大了去了~~,在开发中用户请求传入的参数必须做有效性验证,如果有必要则实现双重校验,即前后台校验,忽略参数校验,可能会导致page size过大导致内存溢出、恶意order by导致数据库慢查询、任意重定向、反序列化注入等问题;禁止向HTML页面输出未经安全过滤或位正确转义的用户数据;表单、AJAX提交必须执行CSRF安全过滤(CSRF是一类常见的编程漏洞,意为跨站请求伪造,对于存在CSRF漏洞的网站或应用,攻击者可以事先构造好URL,只要受害者用户一旦访问网站或应用,后台便在用户不知情的情况下对数据库中用户参数进行相应的修改);在使用平台资源,譬如短信、邮件、电话、下单和支付等业务场景时,必须实现正确的防重放限制,比如数量的限制、疲劳度控制、验证码校验,避免被滥刷,资损等情况。

  干到这儿我本人学到的一些经验全部分享完毕,能力有限,总结的可能不太到位,希望各位读者见谅!

上一篇:[水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)


下一篇:[水煮 ASP.NET Web API2 方法论](3-8)怎样给指定路由配置处理器