JAVA 开发规范
v1.0.0 2021/08/27
本篇规范基于阿⾥巴巴、华为的开发⼿册,补充了⼀些细节。
规范不是为了约束和禁锢⼤家的创造⼒,⽽是为了帮助⼤家能够在正确的道路上,尽可能的避免踩坑和跑偏。
规范可以让我们⽆论单匹马还是与众⼈同⾏的时候都能得⼼应⼿。
规范可以让我们在⾯对⽇益变态的需求和做代码接盘侠的时候,更优雅从容。
⼀、编程规范
1、好代码的原则
我们参考 Kent Beck 的简单设计四原则来指导我们的如何写出优秀的代码,如何有效地判断我们的代码是优秀的。
通过所有测试(Passes its tests):强调的是外部需求,这是代码实现最重要的
尽可能消除重复 (Minimizes duplication):代码的模块架构设计,保证代码的正交性,保证代码更容易修改
尽可能清晰表达 (Maximizes clarity):代码的可阅读性,保证代码是容易阅读的
更少代码元素 (Has fewer elements):保证代码是简洁的,在简洁和表达⼒之间,我们更看重表达⼒
以上四个原则的重要程度依次降低,这组定义被称做简单设计原则。
2
2-1
全部采⽤⼩写⽅式,以中划线分隔。
正例:mall-management-system / order-service-client / user-api
反例:mall_management-system / mallManagementSystem / orderServiceClient
2-2
模块名称:{项⽬名称}-{模块名称} 模块名称简洁体现职责
模块名字作为模块组件的名称(即maven中的标签)
2-3
包名不应该⽤来表达模块完整的意思,包名应该仅⽤作与同包下的其他包做区分。
但尽可能使⽤单个单词命名,如果单个单词⽆法正确表达,可采⽤.分割,实在不⾏可采⽤全部单词⼩写(参考的spring命名)
2-4
类名使⽤ UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO ;
抽象类命名使⽤ Abstract 或 Base 开头;
异常类命名使⽤ Exception 结尾;
测试类命名以它要测试的类的名称开始,以 Test 结尾;
如果使⽤到了设计模式,建议在类名中体现出具体模式;
枚举类名建议带上 Enum 后缀,枚举成员名称需要全⼤写,单词间⽤下划线隔开。
正例:
1MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:
1macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
3、TODO/FIXME 规范
TODO/TBD(to be determined)注释⼀般⽤来描述已知待改进、待补充的修改点,并且加上作者名称。
FIXME注释⼀般⽤来描述已知缺陷,它们都应该有统⼀风格,⽅便⽂本搜索统⼀处理。如:
1// TODO 2// FIXME 4、⽅法参数规范 ⽆论是controller,service,manager,dao亦或是其他的代码,每个⽅法最多3个参数,如果超出3个参数的话,要封装成javabean对象。 1. ⽅便他⼈调⽤,降低出错⼏率。尤其是当参数是同⼀种类型,仅仅依靠顺序区分,稍有不慎便是灾难性后果,⽽且排查起来也极其恶 ⼼。 2. 保持代码整洁、清晰度。当⼀个个⽅法⾥充斥着⼀堆堆参数的时候,再坚强的⼈,也会⾝⼼疲惫。 反例: 1/** 2* 使⽤证书加密数据⼯具⽅法 3* 4* @param param 5* @param password 加密密码 6* @param priCert 私钥 7* @param pubCert 公钥 8* @return 返回加密后的字符串 9*/ 10public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert){} 5 5-1 注释是我们披荆斩棘历经磨难翻越需求这座⼤⼭时,留下的踪迹和收获的经验教训,这些宝贵的知识除了证明我们曾经存在过,也提醒着后来的⼈们殷鉴不远、继往开来。 注释除了说明作⽤、逻辑之外。还有⼀个很重要的原因:当业务逻辑过于复杂,代码过于庞⼤的时候,注释就变成了⼀道道美化环境、分离与整理逻辑思路的路标。这是很重要的⼀点,它能有效得帮助我们免于陷⼊代码与业务逻辑的泥沼之中。 正例: 1/** 2* 开始抽奖⽅法 3* 保存中奖信息、奖励⽤户积分等 4* @param luckDrawDTO 5* @return ResponseDTO 返回中奖信息 6*/ 7public ResponseDTO 8 9// -------------- 1、校验抽奖活动基本信息 ------------------------ 10xxx伪代码⼀顿操作 11 12// -------------- 2、新增抽奖记录 ------------------------------- 13xxx伪代码⼀顿操作 14 15// -------------- 3、如果需要消耗积分,则扣除钢镚积分 ------------- 16xxx伪代码⼀顿操作 17 18// -------------- 4、获取奖品信息,开始翻滚吧 -------------------- 19xxx伪代码⼀顿操作 20 21return ResponseDTO.succ(luckDrawPrizeVO); 22} 5-2 注释并不是越多越好,当注释过多,维护代码的同时,还需要维护注释,不仅变成了⼀种负担,也与我们当初添加注释的初衷背道⽽驰。 ⾸先:⼤家应该通过清晰的逻辑架构,好的变量命名来提⾼代码可读性;需要的时候,才辅以注释说明。注释是为了帮助阅读者快速读懂代码,所以要从读者的⾓度出发,按需注释。注释内容要简洁、明了、⽆⼆义性,信息全⾯且不冗余。 其次:⽆论是修改、复制代码时,都要仔细核对注释内容是否正确。只改代码,不改注释是⼀种不⽂明⾏为,破坏了代码与注释的⼀致性,会让阅读者迷惑、费解,甚⾄误解。 反例: 1// 查询部门 2EmployeeDTO employee = employeeDao.listByDeptId(deptId); 5-3⽅法要尽量通过⽅法名⾃解释,不要写⽆⽤、信息冗余的⽅法头,不要写空有格式的⽅法头注释。 ⽅法头注释内容可选,但不限于:功能说明、返回值,⽤法、算法实现等等。尤其是对外的⽅法接⼝声明,其注释,应当将重要、有⽤的信息表达清楚。 正例: 1/** 2* 解析转换时间字符串为 LocalDate 时间类 3* 调⽤前必须校验字符串格式 否则可能造成解析失败的错误异常 4* 5* @param dateStr 必须是 yyyy-MM-dd 格式的字符串 6* @return LocalDate 7*/ 8public static LocalDate parseYMD(String dateStr){} 反例: 1/** 2* 校验对象 3*4* @param t 5* @return String 6*/7public static 反例中出现的问题: ⽅法注释没有说明具体的作⽤、使⽤事项。 参数、返回值,空有格式没内容。这是⾮常重要⼀点,任何⼈调⽤任何⽅法之前都需要知道⽅法对参数的要求,以及返回值是什么。 ⼆、项⽬规范 1、代码⽬录结构 统⼀的⽬录结构是所有项⽬的基础。 1src 源码⽬录|-- common 各个项⽬的通⽤类库|-- config 项⽬的配置信息|-- constant 全局公共常量|-- handler 全局处理器|-- interceptor 全局连接器|-- listener 全局|--module 各个业务|-- |--- employee 员⼯模块|-- |--- role ⾓⾊模块|-- |--- login 登录模块|-- third 三⽅服务,⽐如redis, oss ,微信sdk 等等|-- util 全局⼯具类|-- Application.java 启动类 2、common ⽬录规范 common ⽬录⽤于存放各个项⽬通⽤的项⽬,但是⼜可以依照项⽬进⾏特定的修改。 1src 源码⽬录|-- common 各个项⽬的通⽤类库|-- |--- anno 通⽤注解,⽐如权限,登录等等|-- |--- constant 通⽤常量,⽐如 ResponseCodeConst|-- |--- domain 全局的javabean ,⽐如 BaseEntity,PageParamDTO 等|-- |--- exception 全局异常,如 BusinessException|-- |--- json json 类库,如 LongJsonDeserializer ,LongJsonSerializer|--|--- swagger swagger ⽂档|-- |--- validator 适合各个项⽬的通⽤ validator ,如 CheckEnum ,CheckBigDecimal 等 2 3、config config ⽬录⽤于存放各个项⽬通⽤的项⽬,但是⼜可以依照项⽬进⾏特定的修改。 1src 源码⽬录|-- config 项⽬的所有配置信息|-- |--- MvcConfig mvc 的相关配置,如interceptor,filter 等|-- |--- DataSourceConfig 数据库连接池的配置|-- |--- MybatisConfig mybatis 的配置|-- |--- .... 其他 2 4、module module ⽬录⾥写项⽬的各个业务,每个业务⼀个独⽴的顶级⽂件夹,在⽂件⾥进⾏ mvc 的相关划分。 其中,domain 包⾥存放 entity, dto, vo ,bo 等 javabean 对象 1src|-- module 所有业务模块|-- |-- role ⾓⾊模块|-- |-- |--RoleController.java controller|-- |-- |--RoleConst.java role 相关的常量|-- |-- |--RoleService.java service|-- |-- |-- RoleDao.java dao|-- |-- |--domain domain|-- |-- |-- |-- RoleEntity.java 表对应实体|-- |-- |-- |-- RoleDTO.java dto 对象|-- |-- |-- |-- RoleVO.java 返回对象|-- |-- employee 员⼯模 5、 domain 包中的 javabean 命名规范 1)javabean的整体要求: 不得有任何的业务逻辑或者计算 基本数据类型必须使⽤包装类型(Integer, Double、Boolean 等) 不允许有任何的默认值 每个属性必须添加注释,并且必须使⽤多⾏注释。 必须使⽤lombok简化getter/setter⽅法 建议对象使⽤lombok的@Builder ,@NoArgsConstructor,同时使⽤这两个注解,简化对象构造⽅法以及set⽅法。 正例: 1@Builder@NoArgsConstructor@Datapublic class DemoDTO { private String name; private Integer age;}// 使⽤⽰例:DemoDTO demo = DemoDTO.builder() .name("yeqiu") .age(66) .build(); 2)数据对象;XxxxEntity,要求: 以Entity为结尾(阿⾥是为 DO 为结尾) Xxxx 与数据库表名保持⼀致 类中字段要与数据库字段保持⼀致,不能缺失或者多余 类中的每个字段添加注释,并与数据库注释保持⼀致 不允许有组合 项⽬内的⽇期类型必须统⼀,建议使⽤java.util.Date,java.sql.Timestamp,java.time.LocalDateTime其中只⼀。 3)传输对象;XxxxDTO,要求: 不可以继承⾃Entity DTO可以继承、组合其他DTO,VO,BO等对象 DTO只能⽤于前端、RPC 的请求参数 3)视图对象;XxxxVO,要求: 不可继承⾃Entity VO可以继承、组合其他DTO,VO,BO等对象 VO只能⽤于返回前端、rpc 的业务数据封装对象 4)业务对象BO,要求: 不可以继承⾃Entity BO对象只能⽤于service,manager,dao层,不得⽤于controller层 三、MVC 规范 1 controller 层 service 层 manager 层 dao 层 2、controller 1)只允许在 method 上添加RequestMapping注解,不允许加在 class 上(为了⽅便的查找 url,放到 url 不能⼀次性查找出来) 正例: 1@RestControllerpublic class DepartmentController { @GetMapping("/department/list") public ResponseDTO 反例: 1@RequestMapping ("/department")public class DepartmentController { @GetMapping("/list") public ResponseDTO 2)不推荐使⽤ rest 命名 url,只能使⽤get/post⽅法。url 命名上规范如下: 虽然 Rest ⼤法好,但是有时并不能⼀眼根据 url 看出来是什么操作,所以我们选择了后者,这个没有对与错,只有哪个更适合我们的团队。 /业务模块/⼦模块/动作 正例: 1GET /department/get/{id} 查询某个部门详细信息POST /department/query 复杂查询POST /department/add 添加部门POST /department/update 更新部门GET /department/delete/{id} 删除部门 3)每个⽅法必须添加 swagger ⽂档注解 @ApiOperation ,并填写接⼝描述信息,描述最后必须加上作者信息 @author 哪吒 。 正例: 1@ApiOperation("更新部门信息 @author 哪吒") @PostMapping("/department/update") public ResponseDTO 4)controller 负责协同和委派业务,充当路由的⾓⾊,每个⽅法要保持简洁:不做任何的业务逻辑操作 不做任何的参数、业务校验,参数校验只允许使⽤@Valid 注解做简单的校验 不做任何的数据组合、拼装、赋值等操作 正例: 1@ApiOperation("添加部门 @author 哪吒") @PostMapping("/department/add") public ResponseDTO 5)只能在 controller 层获取当前请求⽤户,并传递给 service 层。 因为获取当前请求⽤户是从 ThreadLocal ⾥获取取的,在 service 、manager 、dao 层极有可能是其他⾮ request 线程调⽤,会出 现 null 的情况,尽量避免 1@ApiOperation("添加员⼯ @author yandanyang") @PostMapping("/employee/add") public ResponseDTO EmployeeAddDTO employeeAddDTO) { LoginTokenBO requestToken = SmartRequestTokenUtil.getRequestUser(); return employeeService.addEmployee(employeeAddDTO, requestToken); } 3、 service 1)合理拆分 service ⽂件,如果业务较⼤,请拆分为多个 service 。如订单业务,所有业务都写到 OrderService 中会导致⽂件过⼤,故需要进⾏拆分如下: OrderQueryService 订单查询业务 OrderCreateService 订单新建业务 OrderDeliverService 订单发货业务 OrderValidatorService 订单验证业务 2)谨慎处理 @Transactional 事务注解的使⽤,不要简单对 service 的⽅法添加个 @Transactional 注解就觉得万事⼤吉了。应当合并对数据库的操作,尽量减少添加了@Transactional ⽅法内的业务逻辑。 @Transactional 注解内的 rollbackFor 值必须使⽤异常的基类 Throwable.class 对于@Transactional 注解,当 spring 遇到该注解时,会⾃动从数据库连接池中获取 connection ,并开启事务然后绑定到 ThreadLocal 上,如果业务并没有进⼊到最终的 操作数据库环节,那么就没有必要获取连接并开启事务,应该直接将 connection 返回给数据库连接池,供其他使⽤(⽐较难以讲解清楚,如果不懂的话就主动去问)。 反例: 1@Transactional(rollbackFor = Throwable.class) public ResponseDTO departmentEntity = departmentDao.selectById(departmentId); if (departmentEntity == null) { return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS); } // 验证 2 DepartmentEntity swapEntity = departmentDao.selectById(swapId); if (swapEntity ==null) { return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS); } // 验证 3 Long count = employeeDao.countByDepartmentId(departmentId) if (count != null && count > 0) { return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE); } // 操作数据库 4 Long departmentSort = departmentEntity.getSort(); departmentEntity.setSort(swapEntity.getSort()); departmentDao.updateById(departmentEntity); swapEntity.setSort(departmentSort);departmentDao.updateById(swapEntity); return ResponseDTO.succ(); } 以上代码前三步都是使⽤ connection 进⾏验证操作,由于⽅法上有@Transactional 注解,所以这三个验证都是使⽤的同⼀个 connection 。若对于复杂业务、复杂的验证逻辑,会导致整个验证过程始终占⽤该 connection 连接,占⽤时间可能会很长,直⾄⽅法结束,connection 才会交还给数据库连接池。 对于复杂业务的不可预计的情况,长时间占⽤同⼀个 connection 连接不是好的事情,应该尽量缩短占⽤时间。 正例: 1DepartmentService.java public ResponseDTO departmentDao.selectById(departmentId); if (departmentEntity == null) { return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS); } DepartmentEntity swapEntity = departmentDao.selectById(swapId); if (swapEntity == null) { return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS); } Long count =employeeDao.countByDepartmentId(departmentId) if (count != null && count > 0) { return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE); }departmentManager.upOrDown(departmentSort,swapEntity); return ResponseDTO.succ(); } DepartmentManager.java @Transactional(rollbackFor = Throwable.class) public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){ Long departmentSort = departmentEntity.getSort(); departmentEntity.setSort(swapEntity.getSort()); departmentDao.updateById(departmentEntity); swapEntity.setSort(departmentSort); departmentDao.updateById(swapEntity); } 将数据在 service 层准备好,然后传递给 manager 层,由 manager 层添加@Transactional 进⾏数据库操作。 3)需要注意的是:注解 @Transactional 事务在类的内部⽅法调⽤是不会⽣效的 反例:如果发⽣异常,saveData ⽅法上的事务注解并不会起作⽤ 1@Servicepublic class OrderService{ public void createOrder(OrderCreateDTO createDTO){ this.saveData(createDTO); } @Transactional(rollbackFor = Throwable.class)public void saveData(OrderCreateDTO createDTO){ orderDao.insert(createDTO); }} Spring 采⽤动态代理(AOP)实现对bean 的管理和切⽚,它为我们的每个class ⽣成⼀个代理对象。只有在代理对象之间进⾏调⽤ 时,可以触发切⾯逻辑。⽽在同⼀个class 中,⽅法A 调⽤⽅法B ,调⽤的是原对象的⽅法,⽽不通过代理对象。所以Spring ⽆法 拦截到这次调⽤,也就⽆法通过注解保证事务了。简单来说,在同⼀个类中的⽅法调⽤,不会被⽅法拦截到,因此事务不 会起作⽤。 解决⽅案: 1. 可以将⽅法放⼊另⼀个类,如新增 manager 层,通过spring 注⼊,这样符合了在对象之间调⽤的条件。 2. 启动类添加 @EnableAspectJAutoProxy(exposeProxy = true),⽅法内使⽤AopContext.currentProxy()获得代理类,使⽤事务。 1SpringBootApplication.java@EnableAspectJAutoProxy(exposeProxy = true)@SpringBootApplicationpublic class SpringBootApplication {}OrderService.javapublic void createOrder(OrderCreateDTO createDTO){ OrderService orderService = (OrderService)AopContext.currentProxy(); orderService.saveData(createDTO);} 4)service 是具体的业务处理逻辑服务层,尽量避免将web 层某些参数传递到service 中。 反例: 1public ResponseDTO InputStreamReader(request.getInputStream(), "GBK"); BufferedReader reader = new BufferedReader(inputStreamReader); StringBuilder sb = new StringBuilder(); String str; while ((str = reader.readLine()) != null) { sb.append(str); } if(!JSON.isValid(msg)){ return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM); } PinganMsgDTO PinganMsgDTO = JSON.parseObject(msg,PinganMsgDTO.class); // ⽰例结束}反例中出现的问题: 反例中把 HttpServletRequest 传递到service 中,是为了获取Request 流中的字符信息,然后才是真正的业务处理。按照分层的初衷:将代码、业务逻辑解耦,正确的做法应该是handlePinganRequest ⽅法将String 字符作为参数直接处理业务,将从Request 中获取字符的操作放⼊controller 中。 另⼀个坏处是不⽅便做单元测试,还得⼀个new ⼀个HttpServletRequest 并制造⼀个InputStream ,然⽽这样做并不能模拟到真实的业务情景及数据。 4、 manager 层规范 manager 层的作⽤(引⾃《阿⾥ java ⼿册》):对第三⽅平台封装的层,预处理返回结果及转化异常信息; 对 Service 层通⽤能⼒的下沉,如缓存⽅案、中间件通⽤处理; 与 DAO 层交互,对多个 DAO 的组合复⽤。 5、 dao 层规范 优先使⽤ mybatis-plus 框架。如果需要多个数据源操作的,可以选择使⽤ SmartDb 框架。 1)所有 Dao 继承⾃ BaseMapper 2)禁⽌使⽤ Mybatis-plus 的 Wrapper 条件构建器 3)禁⽌直接在 mybatis xml 中写死常量,应从 dao 中传⼊到 xml 中 3)建议不要使⽤星号 * 代替所有字段 正例: 1NoticeDao.java Integer noticeCount(@Param("sendStatus") Integer sendStatus);--------------------------------------------- NoticeMapper.xml 反例: 1NoticeDao.java Integer noticeCount();--------------------------------------------- NoticeMapper.xml 3)dao 层⽅法命名规范(适⽤于service ) 获取单个对象的⽅法⽤ get 做前缀。 获取多个对象的⽅法⽤ list 做前缀。 获取统计值的⽅法⽤ count 做前缀。 插⼊的⽅法⽤ save/insert 做前缀。 删除的⽅法⽤ remove/delete 做前缀。 修改的⽅法⽤ update 做前缀。 建议:dao 层⽅法命名尽量以sql 语义命名,避免与业务关联。 正例: 1List 反例: 1List get 代表单个查询,批量查询的应该 list 开头。 命名与业务关联,局限了dao ⽅法的使⽤场景和范围,降低了⽅法的复⽤性,造成他⼈困惑以及重复造轮⼦。 6、boolean 类型的属性命名规范 类中布尔类型的变量,都不要加is ,否则部分框架解析会引起序列化错误。反例:定义为基本数据类型 Boolean isDeleted ;的属性,它的⽅法也是 isDeleted(),RPC 在反向解析的时候,“以为”对应的属性名称是 deleted ,导致属性获取不到,进⽽抛出异 常。 这是阿⾥巴巴开发⼿册中的原⽂,我们团队的规定是:boolean 类型的类属性和数据表字段都统⼀使⽤ flag 结尾。虽然使⽤ isDeleted ,is_deleted 从字⾯语义上更直观,但是⽐起可能出现的潜在错误,这点牺牲还是值得的。 正例: 1deletedFlag ,deleted_flag ,onlineFlag ,online_flag 四、数据库 1 建表规范 表必备三字段:id, create_time, update_time id 字段 Long 类型,单表⾃增,⾃增长度为 1create_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP update_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP 2 枚举类表字段注释需要将所有枚举含义进⾏注释 修改或增加字段的状态描述,必须要及时同步更新注释。 如下表的 sync_status 字段 同步状态 0 未开始 1同步中 2同步成功 3失败。 正例: 1CREATE TABLE `t_change_data` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步状态 0 未开始 1同步中 2同步成功 3失败', `sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`change_data_id`))反例: 1CREATE TABLE `t_change_data` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0'COMMENT '同步状态 ', `sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`change_data_id`)) 3 具体索引规范请参照《阿⾥巴巴 Java 开发⼿册》索引规约 1、代码提交规范 master: 主分⽀,主要⽤来版本发布。 develop:⽇常开发分⽀,该分⽀正常保存了开发的最新代码。 feature:具体的功能开发分⽀,只与 develop 分⽀交互。 release:release 分⽀可以认为是 master 分⽀的未测试版。⽐如说某⼀期的功能全部开发完成,那么就将 develop 分⽀合并到release 分⽀,测试没有问题并且到了发布⽇期就合并到 master 分⽀,进⾏发布。 hotfix:线上 bug 修复分⽀。 提交前应该冷静、仔细检查⼀下,确保没有忘记加⼊版本控制或不应该提交的⽂件。 提交前应该先编译⼀次(idea⾥ctrl+F9),防⽌出现编译都报错的情况。 提交前先更新pull⼀次代码,提交前发⽣冲突要⽐提交后发⽣冲突容易解决的多。 提交前检查代码是否格式化,是否符合代码规范,⽆⽤的包引⼊、变量是否清除等等。 提交时检查注释是否准确简洁的表达出了本次提交的内容。 2、maven项⽬ pom禁⽌出现相同 groupId,artifactId 的依赖配置。 项⽬名称应该与 artifactId 保持⼀致。 定期检查jar包依赖关系,及时排除解决冲突的jar包。 3、保持项⽬整洁 使⽤git,必须添加 .gitignore 忽略配置⽂件。 不要提交与项⽬⽆关的内容⽂件:idea配置、target包等。> listDepartment() { return departmentService.listDepartment(); }
> listDepartment() { return departmentService.listDepartment(); }