为什么80%的码农都做不了架构师?>>>
代码规范
- 所有开发同学使用的ide均需要导入三个文件
cleanup.xml
codetemplates.xml/** * *//* * Copyright ${year} mljr.com All right reserved. This software is the * confidential and proprietary information of mljr.com ("Confidential * Information"). You shall not disclose such Confidential Information and shall * use it only in accordance with the terms of the license agreement you entered * into with mljr.com . *//* (non-Javadoc) * ${see_to_overridden} *//** * ${tags} * ${see_to_target} *//** * ${tags} *//** * @author ${user} ${date} ${time} *//** * ${tags} */
format.xml
,用户代码格式化,使用方式见下图(以eclipse为例):- 打开eclipse选项卡,preferences > Java > Code Style > Clean Up、Code Template、Formatter进行导入操作
- 如果想要在Ctrl + s进行保存是格式化代码,可以在preferences > Java > Editor > Save Actions中按下图所示进行配置(推荐进行该项配置)
- 请安装lombok,所有java bean均采用lombok注解,以减少源码中相对冗余的get、set、toString等方法,使用方法见官方网址https://projectlombok.org/
- 其他代码规范请大家参考阿里巴巴Java开发手册.pdf
项目规范
- 新建项目首次提交到git仓库之前,请加入
.gitignore文件## .gitignore for Grails 1.2 and 1.3 # .gitignore for maven target/ *.releaseBackup # web application files #/web-app/WEB-INF # IDE support files /.classpath /.launch /.project /.settings /*.launch /*.tmproj /ivy* /eclipse # default HSQL database files for production mode /prodDb.* # general HSQL database files *Db.properties *Db.script # logs /stacktrace.log /test/reports /logs *.log *.log.* # project release file /*.war # plugin release file /*.zip /*.zip.sha1 # older plugin install locations /plugins /web-app/plugins /web-app/WEB-INF/classes # "temporary" build files target/ out/ build/ # other *.iws #.gitignore for java *.class # Package Files # *.jar *.war *.ear ## .gitignore for eclipse *.pydevproject .project .metadata .springBeans bin/** tmp/** tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ## .gitignore for intellij *.iml *.ipr *.iws .idea/ ## .gitignore for linux .* !.gitignore !.gitattributes !.editorconfig !.eslintrc !.travis.yml *~ ## .gitignore for windows # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ ## .gitignore for mac os x .DS_Store .AppleDouble .LSOverride Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ## hack for graddle wrapper !wrapper/*.jar !**/wrapper/*.jar
,不要自行改动.gitignore文件,这个文件能够很好的忽略不同操作系统、不同IDE、不同编译插件等外部环境引入的非工程提交文件,简单来说,一个Maven项目,需要提交到git仓库上的文件只包括pom.xml和src文件夹下的文件。
开发规范
-
项目结构示例
工程名称
工程用途
说明
agreement协议主(父)工程之所以用父子工程是为了方便idea用户能够在一个ide中引用相关项目,同时所有子工程之间有强依赖性agreement-inner-api协议二方库(对内)所有以inner-api结尾的工程为内部二方库,仅限项目内项目间使用,不可对外开放agreement-api协议二方库(对外)对外的二方库是让外部工程引用的接口工程,其中所有接口和数据结构均需要仔细衡量开放,一旦有其他应用开始使用,则需要保持向前兼容agreement-common协议公共模块定义通用枚举、工具类、业务异常类及其他任何工程公共使用的数据结构,需要尽量保持最小量级代码,非公共模块请勿放置在此agreement-*-service协议(微)服务协议提供的微服务agreement-task - 所有的po对象均以Po结尾,例如StaffPo,po对象中只能是简单的表映射,除了get、set方法外不应该有额外业务属性(方法)存在
-
在mybatis的mapper接口中,一律使用sql的命名方案,insert:增加,update:更新,delete:删除,select:查询,以StaffPoMapper为例子,其中可能会有:
1) StaffPo selectById(long id);--根据id获取对应的员工对象
2) int insert(StaffPo po);--新建员工
3) int update(StaffPo po);--更新员工信息,注意:这里是强制更新, 如果有null字段就会被更新成null
4) int updateSelective(StaffPo po);--更新员工信息(非空字段),注意:这里是非强制更新,仅仅更新非null字段
5) StaffPo selectByIdForUpdate(long id);--根据id获取对应的员工对象(加锁,悲观锁)
6) ListselectListByCondition(StaffCondition condition);--根据查询条件获取对应的列表数据,不管分页还是查询全列表均采用这种模式,分页是通过分页插件来做的
7) int deleteById(long id);--删除员工数据(这里是举例子,应该采用逻辑删除) -
mapper接口层面,如果有很多的查询仅仅是查询参数不同,其他的用途都是一样的,就应该合并对应查询(某些有意义的单查询需要保留),例如:
ListselectListByMobile(String mobile);
ListselectListByEmail(String email);
ListselectListByStatus(int status);
ListselectListByStaffIds(List staffIds);
这种查询本质上都是根据查询条件获取对应员工列表数据,应该合并相关语义,构建
public class StaffCondition {
private String mobile;
private String email;
private Integer status;
private Listids; --get & set--
}
ListselectListByCondition(StaffCondition condition) - 所有的方法签名和参数命名应该尽可能让阅读代码的人能够理解,而不需要点进去看sql才能理解
- 所有的业务逻辑不能写在sql中,例如:select * from staff where status=0 and mobile = #{mobile},在这个sql中,status=0这个条件被隐藏在sql中,无法被调用方感知到,会带来隐患
- service层代码采用如下命名方案:create:增加,modify:更新,remove:删除,list:查询多个对象,query:查询单个对象,所有方法和参数的命名都需要有业务含义,让调用方能够直观的了解到方法的含义,拒绝一些莫名奇妙的方法签名,例如:add(XXX xx)--是创建还是添加?; selectByPage(XXX xxx)–查询什么东西?ByPage是什么含义?;
-
所有微服务controller入口参数不能是数据库的po对象,这里因为使用的是http协议,所以此处的问题还算是比较好处理,举例说明:StaffController中有个方法
public ClfResultdoCreateStaff(StaffPo po); --创建一个员工,假设现在新接入一个需求,是需要再传一个属性employeeType来判断是否是外包员工,那么面临的情况就是3个解决方案,
1) StaffPo中增加employeeType,违背了Po对象的使用原则
2) 修改方法签名为public ClfResultdoCreateStaff(StaffPo po, int employeeType),导致不能向前兼容老接口
3) 增加一个StaffDto对象,在原有的staffPo属性基础上,增加属性employeeType,(这种解决方案正是因为使用的是http协议才能实现,试想一下dubbo)
所以我们的入参都采用dto对象,可以是po对象的完全copy,也可以增加属性和业务方法,但是推荐还是让dto对象简单一点比较好,业务逻辑还是写在业务方法中 - 删除所有无效的类引用、全局变量、局部变量,所有方法都需要考虑使用范围,绝大多数方法都应该使用private和public2种类型,在使用public范围时候,一定确保方法有人调用,一切没有调用方的public方法应该删除掉,在应用初期应该尽可能给应用瘦身,如果认为以后会有用到的方法,那就用到时候再写,否则等到用到的时候也有可能因为忘记写过,而另外再写一个
数据库规范
-
所有表均需要有以下三个字段,id为第1个字段,created_time、modified_time为表中最后2个字段,如果需要新增字段,必须放到created_time之前。
字段
类型
允许空值
默认值
特殊项
说明
id bigint 否 无 无符号、自增(可选) 主键 created_time timestamp 否 CURRENT_TIMESTAMP 无 创建时间,该字段由数据库自行写入,不要在代码中进行操作 modified_time timestamp 否 CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 最后更新时间,该字段由数据库自行写入,不要在代码中进行操作 - 表命名采用 26 个英文小写字母和 0-9 的自然数(经常不需要)加上下划线'_'组成,单词限定尽量为4个以内。
例如:trade_record,repayment_plan
- 字段命名采用 26 个英文小写字母和 0-9 的自然数(经常不需要)加上下划线'_'组成,单词限定尽量为4个以内,关联字段名称尽量保持相同,如果是单一外键关联需要指明表名。
例如:agreement_no,agreement_id
-
索引命名
类型
格式
例子
主键索引 pk_tablenam_columnname pk_user_id 唯一索引 uk_tablename_columnnames uk_user_mobile 普通索引 idx_tablename_columnnames idx_agreement_code 组合索引 idx_tablename_column1_column2... idex_agreement_code_status -
数据类型使用简介
类型
说明
类型
说明
DECIMAL(M,D) 当表示定点小数的情况下使用该类型,禁止使用浮点类型,会带来不精确。定点数在MySQL内部以字符串形式存储,比浮点数更精确,适合用来表示货币等精度高的数据。 INT 系列 所有整数类型字段使用 INT(TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT),根据所存放的数据大小选择合适的子类型,且所有 INT 类型都不使用长度限制。
VARCHAR 所有可变长度的字段均使用 VARCHAR 类型,对于有限类别的字段(如性别、状态等),均建议使用 VARCHAR 类型存储能明显表现其意义的字符串。
TEXT 系列 仅当需存储的字节数可能超过 20000 时,使用 TEXT 系列类型(TEXT、MEDIUMTEXT、LONGTEXT)。并和原表进行分拆,与原表主键组成新表存储,且每个表只允许有一个 TEXT 系列类型字段。
CHAR 仅当字段确定为定长,且将来不会修改长度时,使用 CHAR 类型。上线以后不允许修改字段型。需谨慎使用。如:性别。
DATE 只需要精确到天的字段使用 DATE 类型。精确到"天"的取当前时期的操作使用 CURDATE()函数实现。
DATETIME 需要精确到时间(时、分、秒)的字段使用 DATETIME 类型。精确到"秒"的取当前时间的操作使用NOW()函数实现。取值范围:'1000-01-01'到'9999-12-31'。
TIMESTAMP 该类型仅允许 created_time和modified_time 字段使用,其它字段不允许使用该类型。取值范围:'1970-01-0100:00:00'到'2037-01-01 00:00:00'。
- 存储引擎和字符集
每张表都要显式指定存储引擎为InnoDB,显式指定字符集为UTF8 - 注释
所有字段和表都要有注释 - 不要使用存储过程,外键
- MySQL版本使用5.6
MySQL5.5限制只能最多有一列使用CURRENT_TIMESTAMP作为默认值,MySQL5.6没有这个限制(https://stackoverflow.com/questions/4489548/why-there-can-be-only-one-timestamp-column-with-current-timestamp-in-default-cla)