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 后綴,枚舉成員名稱需要全大寫,單詞間用下划線隔開。
正例:
MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:
macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
3、TODO/FIXME 規范
TODO/TBD(to be determined)
注釋一般用來描述已知待改進、待補充的修改點,並且加上作者名稱。
FIXME
注釋一般用來描述已知缺陷,它們都應該有統一風格,方便文本搜索統一處理。如:
// TODO <author-name>: 補充XX處理
// FIXME <author-name>: XX缺陷
4、方法參數規范
無論是 controller,service,manager,dao
亦或是其他的代碼,每個方法最多 3
個參數,如果超出 3
個參數的話,要封裝成 javabean
對象。
- 方便他人調用,降低出錯幾率。尤其是當參數是同一種類型,僅僅依靠順序區分,稍有不慎便是災難性后果,而且排查起來也極其惡心。
- 保持代碼整潔、清晰度。當一個個方法里充斥着一堆堆參數的時候,再堅強的人,也會身心疲憊。
反例:
/**
* 使用證書加密數據工具方法
*
* @param param
* @param password 加密密碼
* @param priCert 私鑰
* @param pubCert 公鑰
* @return 返回加密后的字符串
*/
public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert){}
5、注釋規范
5-1、注釋和代碼一樣重要
注釋是我們披荊斬棘歷經磨難翻越需求這座大山時,留下的蹤跡和收獲的經驗教訓,這些寶貴的知識除了證明我們曾經存在過,也提醒着后來的人們殷鑒不遠、繼往開來。
注釋除了說明作用、邏輯之外。還有一個很重要的原因:當業務邏輯過於復雜,代碼過於龐大的時候,注釋就變成了一道道美化環境、分離與整理邏輯思路的路標。這是很重要的一點,它能有效得幫助我們免於陷入代碼與業務邏輯的泥沼之中。
正例:
/**
* 開始抽獎方法
* 保存中獎信息、獎勵用戶積分等
* @param luckDrawDTO
* @return ResponseDTO 返回中獎信息
*/
public ResponseDTO<String> startLuckDraw(LuckDrawDTO luckDrawDTO) {
// -------------- 1、校驗抽獎活動基本信息 ------------------------
xxx偽代碼一頓操作
// -------------- 2、新增抽獎記錄 -------------------------------
xxx偽代碼一頓操作
// -------------- 3、如果需要消耗積分,則扣除鋼鏰積分 -------------
xxx偽代碼一頓操作
// -------------- 4、獲取獎品信息,開始翻滾吧 --------------------
xxx偽代碼一頓操作
return ResponseDTO.succ(luckDrawPrizeVO);
}
5-2、注釋和代碼的一致性
注釋並不是越多越好,當注釋過多,維護代碼的同時,還需要維護注釋,不僅變成了一種負擔,也與我們當初添加注釋的初衷背道而馳。
首先:大家應該通過清晰的邏輯架構,好的變量命名來提高代碼可讀性;需要的時候,才輔以注釋說明。注釋是為了幫助閱讀者快速讀懂代碼,所以要從讀者的角度出發,按需注釋。注釋內容要簡潔、明了、無二義性,信息全面且不冗余。
其次:無論是修改、復制代碼時,都要仔細核對注釋內容是否正確。只改代碼,不改注釋是一種不文明行為,破壞了代碼與注釋的一致性,會讓閱讀者迷惑、費解,甚至誤解。
反例:
// 查詢部門
EmployeeDTO employee = employeeDao.listByDeptId(deptId);
5-3、方法注釋
方法要盡量通過方法名自解釋,不要寫無用、信息冗余的方法頭,不要寫空有格式的方法頭注釋。
方法頭注釋內容可選,但不限於:功能說明、返回值,用法、算法實現等等。尤其是對外的方法接口聲明,其注釋,應當將重要、有用的信息表達清楚。
正例:
/**
* 解析轉換時間字符串為 LocalDate 時間類
* 調用前必須校驗字符串格式 否則可能造成解析失敗的錯誤異常
*
* @param dateStr 必須是 yyyy-MM-dd 格式的字符串
* @return LocalDate
*/
public static LocalDate parseYMD(String dateStr){}
反例:
/**
* 校驗對象
*
* @param t
* @return String
*/
public static <T> String checkObj(T t);
反例中出現的問題:
- 方法注釋沒有說明具體的作用、使用事項。
- 參數、返回值,空有格式沒內容。這是非常重要一點,任何人調用任何方法之前都需要知道方法對參數的要求,以及返回值是什么。
二、項目規范
1、代碼目錄結構
統一的目錄結構是所有項目的基礎。
src 源碼目錄|-- common 各個項目的通用類庫|-- config 項目的配置信息|-- constant 全局公共常量|-- handler 全局處理器|-- interceptor 全局連接器|-- listener 全局監聽器|-- module 各個業務|-- |--- employee 員工模塊|-- |--- role 角色模塊|-- |--- login 登錄模塊|-- third 三方服務,比如redis, oss,微信sdk等等|-- util 全局工具類|-- Application.java 啟動類
2、common 目錄規范
common 目錄用於存放各個項目通用的項目,但是又可以依照項目進行特定的修改。
src 源碼目錄|-- common 各個項目的通用類庫|-- |--- anno 通用注解,比如權限,登錄等等|-- |--- constant 通用常量,比如 ResponseCodeConst|-- |--- domain 全局的 javabean,比如 BaseEntity,PageParamDTO 等|-- |--- exception 全局異常,如 BusinessException|-- |--- json json 類庫,如 LongJsonDeserializer,LongJsonSerializer|-- |--- swagger swagger 文檔|-- |--- validator 適合各個項目的通用 validator,如 CheckEnum,CheckBigDecimal 等
3、config 目錄規范
config 目錄用於存放各個項目通用的項目,但是又可以依照項目進行特定的修改。
src 源碼目錄|-- config 項目的所有配置信息|-- |--- MvcConfig mvc的相關配置,如interceptor,filter等|-- |--- DataSourceConfig 數據庫連接池的配置|-- |--- MybatisConfig mybatis的配置|-- |--- .... 其他
4、module 目錄規范
module 目錄里寫項目的各個業務,每個業務一個獨立的頂級文件夾,在文件里進行 mvc 的相關划分。
其中,domain 包里存放 entity, dto, vo,bo 等 javabean 對象
src|-- module 所有業務模塊|-- |-- role 角色模塊|-- |-- |--RoleController.java controller|-- |-- |--RoleConst.java role相關的常量|-- |-- |--RoleService.java service|-- |-- |--RoleDao.java dao|-- |-- |--domain domain|-- |-- |-- |-- RoleEntity.java 表對應實體|-- |-- |-- |-- RoleDTO.java dto對象|-- |-- |-- |-- RoleVO.java 返回對象|-- |-- employee 員工模塊|-- |-- login 登錄模塊|-- |-- email 郵件模塊|-- |-- .... 其他
5、 domain 包中的 javabean 命名規范
1) javabean
的整體要求:
- 不得有任何的業務邏輯或者計算
- 基本數據類型必須使用包裝類型
(Integer, Double、Boolean 等)
- 不允許有任何的默認值
- 每個屬性必須添加注釋,並且必須使用多行注釋。
- 必須使用
lombok
簡化getter/setter
方法 - 建議對象使用
lombok
的@Builder ,@NoArgsConstructor
,同時使用這兩個注解,簡化對象構造方法以及set方法。
正例:
@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 不能一次性查找出來)
正例:
@RestControllerpublic class DepartmentController { @GetMapping("/department/list") public ResponseDTO<List<DepartmentVO>> listDepartment() { return departmentService.listDepartment(); }
反例:
@RequestMapping ("/department")public class DepartmentController { @GetMapping("/list") public ResponseDTO<List<DepartmentVO>> listDepartment() { return departmentService.listDepartment(); }
2)不推薦使用 rest 命名 url, 只能使用 get/post
方法。url 命名上規范如下:
雖然 Rest 大法好,但是有時並不能一眼根據 url 看出來是什么操作,所以我們選擇了后者,這個沒有對與錯,只有哪個更適合我們的團隊。
/業務模塊/子模塊/動作
正例:
GET /department/get/{id} 查詢某個部門詳細信息POST /department/query 復雜查詢POST /department/add 添加部門POST /department/update 更新部門GET /department/delete/{id} 刪除部門
3)每個方法必須添加 swagger
文檔注解 @ApiOperation
,並填寫接口描述信息,描述最后必須加上作者信息 @author 哪吒
。
正例:
@ApiOperation("更新部門信息 @author 哪吒") @PostMapping("/department/update") public ResponseDTO<String> updateDepartment(@Valid @RequestBody DeptUpdateDTO deptUpdateDTO) { return departmentService.updateDepartment(deptUpdateDTO); }
4)controller 負責協同和委派業務,充當路由的角色,每個方法要保持簡潔:
- 不做任何的業務邏輯操作
- 不做任何的參數、業務校驗,參數校驗只允許使用@Valid 注解做簡單的校驗
- 不做任何的數據組合、拼裝、賦值等操作
正例:
@ApiOperation("添加部門 @author 哪吒") @PostMapping("/department/add") public ResponseDTO<String> addDepartment(@Valid @RequestBody DepartmentCreateDTO departmentCreateDTO) { return departmentService.addDepartment(departmentCreateDTO); }
5)只能在 controller
層獲取當前請求用戶,並傳遞給 service
層。
因為獲取當前請求用戶是從 ThreadLocal 里獲取取的,在 service、manager、dao 層極有可能是其他非 request 線程調用,會出現 null 的情況,盡量避免
@ApiOperation("添加員工 @author yandanyang") @PostMapping("/employee/add") public ResponseDTO<String> addEmployee(@Valid @RequestBody 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 返回給數據庫連接池,供其他使用(比較難以講解清楚,如果不懂的話就主動去問)。
反例:
@Transactional(rollbackFor = Throwable.class) public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) { // 驗證 1 DepartmentEntity 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 連接不是好的事情,應該盡量縮短占用時間。
正例:
DepartmentService.java public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) { DepartmentEntity departmentEntity = 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方法上的事務注解並不會起作用
@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無法攔截到這次調用,也就無法通過注解保證事務了。簡單來說,在同一個類中的方法調用,不會被方法攔截器攔截到,因此事務不會起作用。
解決方案:
- 可以將方法放入另一個類,如新增
manager層
,通過spring注入,這樣符合了在對象之間調用的條件。 - 啟動類添加
@EnableAspectJAutoProxy(exposeProxy = true)
,方法內使用AopContext.currentProxy()
獲得代理類,使用事務。
SpringBootApplication.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中。
反例:
public ResponseDTO<String> handlePinganRequest(HttpServletRequest request){ InputStreamReader inputStreamReader = new 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)建議不要使用星號 *
代替所有字段
正例:
NoticeDao.java Integer noticeCount(@Param("sendStatus") Integer sendStatus);--------------------------------------------- NoticeMapper.xml <select id="noticeCount" resultType="integer"> select count(1) from t_notice where send_status = #{sendStatus} </select>
反例:
NoticeDao.java Integer noticeCount();--------------------------------------------- NoticeMapper.xml <select id="noticeCount" resultType="integer"> select count(1) from t_notice where send_status = 0 </select>
3)dao層方法命名規范(適用於service)
- 獲取單個對象的方法用
get
做前綴。 - 獲取多個對象的方法用
list
做前綴。 - 獲取統計值的方法用
count
做前綴。 - 插入的方法用
save/insert
做前綴。 - 刪除的方法用
remove/delete
做前綴。 - 修改的方法用
update
做前綴。
建議:dao層方法命名盡量以sql語義命名,避免與業務關聯。
正例:
List<PerformanceDTO> listByMonthAndItemId(@Param("month") String month, @Param("itemId") Integer itemId);
反例:
List<PerformanceDTO> getInternalData(@Param("month") String month, @Param("itemId") Integer itemId);
反例中出現的不規范操作:
- get代表單個查詢,批量查詢的應該 list 開頭。
- 命名與業務關聯,局限了dao方法的使用場景和范圍,降低了方法的復用性,造成他人困惑以及重復造輪子。
6、boolean類型的屬性命名規范
類中布爾類型的變量,都不要加is,否則部分框架解析會引起序列化錯誤。反例:定義為基本數據類型 Boolean isDeleted;的屬性,它的方法也是 isDeleted(),RPC在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋出異常。
這是阿里巴巴開發手冊中的原文,我們團隊的規定是:boolean
類型的類屬性和數據表字段都統一使用 flag
結尾。雖然使用 isDeleted,is_deleted
從字面語義上更直觀,但是比起可能出現的潛在錯誤,這點犧牲還是值得的。
正例:
deletedFlag,deleted_flag,onlineFlag,online_flag
四、數據庫 規范
1 建表規范
表必備三字段:id, create_time, update_time
- id 字段 Long 類型,單表自增,自增長度為 1
- create_time 字段 datetime 類型,默認值 CURRENT_TIMESTAMP
- update_time 字段 datetime 類型,默認值 CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP
2 枚舉類表字段注釋需要將所有枚舉含義進行注釋
修改或增加字段的狀態描述,必須要及時同步更新注釋。
如下表的 sync_status
字段 同步狀態 0 未開始 1同步中 2同步成功 3失敗
。
正例:
CREATE 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`))
反例:
CREATE 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包等。