一.多使用lombok的新特性
lombok對每個java後端來說應該都不陌生,但對它的使用不應該僅停留在@Data,@Getter,@Setter...上,推薦多使用以下幾個註解:
@Builder
讓類轉換爲建造者模式,可以讓類的創建和賦值變得更優雅,特別是在該類有很多屬性需要設置的時候
Employee employee = new Employee();
employee.setName("lombok");
employee.setAge(10);
Employee employee1 = Employee.builder()
.name("lombok")
.age(10)
.build();
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
在Spring項目中,當一個類裏面依賴的模塊很多,會有很多Service被注入進來,脫離Lombok,我們通常會用下面這種方式進行注入:
public class TestController {
@Autowired
private TestService1 testService1;
@Autowired
private TestService2 testService2;
...
}
這樣注入會有大量重複的@Autowired註解,如果你用的是Idea編輯器,還會收到煩人的injection not recomand,通過Lombok註解,就可以同時化解上面量大問題:
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TestController {
private final TestService1 testService1;
private final TestService2 testService2;
...
}
當有新的類需要被注入時,不再需要加@Autowied註解,直接引入即可,值得注意的是,加了該@RequiredArgsConstructor(onConstructor = @__(@Autowired))註解後,引入的類需要定義爲final類型或者加@NotNull註解,推薦用final.
另外還有@AllArgsConstructor,@NoArgsconstructor註解也推薦使用,不要再手動寫全參/無參構造了,交給插件去處理不香嗎?
二.巧用Strsubstitutor類處理字符串拼接
在實際項目開發中,總會有一些場景需要字符串拼接,大量的字符串拼接不僅會影響性能,更會讓你的代碼看着很醜很Low,即便可以用StringBuilder來改善性能,但代碼依舊非常醜,這裏我舉個例子,假設最終想要的字符串爲: 2020年5月14日 午餐(11:30-12:30)
如果用傳統方式來實現:
//用字符串拼接
String date = "2020年5月14日";
String skuName = "午餐";
String startTime = "11:30";
String endTime = "12:30";
String result = date + " " + skuName + "(" + startTime + "-" + endTime + ")";
//用StringBuilder實現
String result1 = new StringBuilder()
.append(date)
.append(" ")
.append(skuName)
.append("(")
.append(startTime)
.append("-")
.append(endTime)
.append(")")
.toString();
這樣寫代碼似乎還能看,但如果拼接的內容比上面還多,那代碼就又醜性能又差, 可以考慮用apache-commons-lang3提供的Strsubstitutor重構一下:
Map<String, String> valueMap = new HashMap<>();
valueMap.put("date", date);
valueMap.put("skuName", skuName);
valueMap.put("startTime", startTime);
valueMap.put("endTime", endTime);
final String template = "${date} ${skuName}(${startTime}-${endTime})";
StrSubstitutor strSub = new StrSubstitutor(valueMap);
String result2 = strSub.replace(template);
這樣除去向Map中put的操作,實際僅需要三行代碼即可完成複雜的字符串拼接,再多拼接都不怕!而且底層用的是正則匹配,沒有頻繁的創建String對象,性能要比前面這兩種實現方式高很多,代碼也優雅不少.
三.統一的日誌打印工具
日誌的打印其實是很有講究的,好的日誌不僅可以幫助開發排查線上問題,還可以提高系統性能,但遺憾的是目前大部分公司的日誌都沒有什麼規範可言,到開發這裏更是隨心所欲...我曾在上家公司的生產環境下用Jenkins做過測試,日誌對系統吞吐量有不小的影響,關閉info級別日誌和開啓,QPS大約能相差100多,一分鐘下來就是6000-8000條請求的差距! 所以日誌該如何打,打什麼信息真的值得深究.
這裏舉兩個真實的反例:
A.某開發同學在線上系統,調用一個批量返回數據的rpc接口,並在所有場景下都打印該rpc接口返回的數據,偏偏該接口的使用頻率很高,沒過幾個月,監控開始告警了,磁盤空間不夠用,排查發現日誌文件竟高達28GB...
B.一個項目組有多個成員共同開發,每個人都有自己的日誌打印風格,而且同一個人在不同的接口打印的日誌風格也有差異,於是整個代碼在日誌這塊看着真是一言難盡,非常亂...更可怕的是,有人在接口調用異常的時候居然沒有打日誌,而是僅在每次成功的時候打,當有天線上出了故障,沒有日誌記錄案發現場,排查無從下手...
所以對於日誌,千萬不要忽視,好的日誌應該是記錄必要的案發現場信息,對不同級別的日誌打印內容區別處理,最大限度提高日誌效率,同時儘量做到統一,所有人都一套風格,不要一個系統,N種風格,會讓代碼醜陋,下面我貼一個工具類,供大家參考,以後若有日誌需要打印,可以Copy此工具類到系統中,當然也可以自己封裝.
public class LogUtil {
public static void dataIncoming(Logger log, String methodName, String invokeParam) {
LogUtil.info(log,
StrFormatter.format("dataIncoming;{}", methodName),
"",
invokeParam);
}
public static void invokeSuccess(Logger log, String methodName, String invokeParam, String invokeResult) {
LogUtil.info(log,
StrFormatter.format("{};invoke;success", methodName),
StrFormatter.format("{}", invokeResult),
invokeParam);
}
public static void invokeFail(Logger log, String methodName, String invokeParam, String errorMsg) {
LogUtil.warn(log,
StrFormatter.format("{};invoke;fail", methodName),
StrFormatter.format("errorMsg={}", errorMsg),
invokeParam);
}
public static void invokeAbort(Logger log, String methodName, String invokeParam, String abortReason) {
LogUtil.info(log,
StrFormatter.format("{};invoke;abort", methodName),
StrFormatter.format("{}", abortReason),
invokeParam);
}
public static void invokeEmptyResult(Logger log, String methodName, String invokeParam, String invokeResult) {
LogUtil.warn(log,
StrFormatter.format("{};invoke;emptyResult", methodName),
StrFormatter.format("{}", invokeResult),
invokeParam);
}
public static void invokeError(Logger log, String methodName, String invokeParam, Throwable e) {
LogUtil.error(log,
StrFormatter.format("{};invoke;error", methodName),
StrFormatter.format("cause={}", e.getMessage()),
invokeParam, e);
}
public static void invokeBlocked(Logger log, String methodName, String invokeParam, Throwable e) {
LogUtil.error(log,
StrFormatter.format("{};invoke;blocked by sentinel", methodName),
StrFormatter.format("cause={}", e.getMessage()),
invokeParam, e);
}
/**
* 打印日誌,格式如下:
* 執行了什麼操作|得到了什麼結果|對應的參數
*
* @param logger logger
* @param operate 執行了什麼操作,不能爲空
* @param result 得到了什麼結果,可能爲空
* @param param 對應的參數,可能爲空,若有多個可以轉成json
*/
public static void debug(Logger logger, String operate, String result, String param) {
logger.debug("{}|{}|{}", operate, result, param);
}
/**
* 打印日誌,格式如下:
* 執行了什麼操作|得到了什麼結果|對應的參數
*
* @param logger logger
* @param operate 執行了什麼操作,不能爲空
* @param result 得到了什麼結果,可能爲空
* @param param 對應的參數,可能爲空,若有多個可以轉成json
*/
public static void info(Logger logger, String operate, String result, String param) {
logger.info("{}|{}|{}", operate, result, param);
}
/**
* 打印日誌,格式如下:
* 執行了什麼操作|得到了什麼結果|對應的參數
*
* @param logger logger
* @param operate 執行了什麼操作,不能爲空
* @param result 得到了什麼結果,可能爲空
* @param param 對應的參數,可能爲空,若有多個可以轉成json
*/
public static void warn(Logger logger, String operate, String result, String param) {
logger.warn("{}|{}|{}", operate, result, param);
}
/**
* 打印日誌,格式如下:
* 執行了什麼操作|得到了什麼結果|對應的參數
*
* @param logger logger
* @param operate 執行了什麼操作,不能爲空
* @param result 得到了什麼結果,可能爲空
* @param param 對應的參數,可能爲空,若有多個可以轉成json
* @param e 出現的異常
*/
public static void error(Logger logger, String operate, String result, String param, Throwable e) {
logger.error(StrFormatter.format("{}|{}|{}", operate, result, param), e);
}
}
四.統一的參數合法性校驗
對於入參的校驗,有很多種方式,從最簡單的If判斷到自己寫AssertUtil,使用hutoolUtil工具類,到Hibernate Validator框架... 對於三個以上的參數校驗,個人更推崇用框架來實現,會讓代碼更工整優雅,而且還可以輕鬆通過框架實現校驗提示內容的國際化。
比如我有一個商品對象,裏面有非常多的字段需要做非空,長度,字段類型等合法性校驗,如果不合法,則封裝後統一返回給前端,展示給用戶
@Data
public class ProductDTO {
@NotEmpty(message = "商品名稱必填")
@Length(max = 15,message = "商品名稱最多隻能輸入15個字符")
private String productName;
@NotEmpty(message = "商品編號必填")
@Length(max = 6,message = "商品編號最多隻能輸入6個字符")
private String productCode;
...
}
通過框架,僅需2行代碼就可以拿到所有錯誤信息的集合Set,非常強大,而且建議將Validator封裝僅Util類中,之後需要參數校驗的地方,僅需一行代碼就可以搞定,非常優雅!
Validator validator = Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory().getValidator();
Set<ConstraintViolation<ProductDTO>> validResult = validator.validate(productDTO);
如果有國際化需求的可以參考這兩篇文檔:
https://www.ibm.com/developerworks/cn/java/j-cn-hibernate-validator/index.html
https://docs.oracle.com/javase/tutorial/i18n/locale/create.html
關於代碼優雅,是學不完的,最有效的幾種方式就是啃阿里巴巴代碼規範,啃設計模式,然後在平時多學習別人寫的代碼,實時總結,取其精華,他爲己用,畢竟每個人都有值得學習的地方,如果以上內容有收穫,不妨點個贊加個關注啥的,防迷路!