java优化代码常见套路
目錄
- 程序員的痛點(爛代碼)
- 該如何優化代碼
- 前臺后臺兩次md5加鹽加密
- JSR303和全局異常處理
- Redis通用的key生成策略和通用的RedisService方法
- 程序猿的必讀書籍
程序員的痛點(爛代碼)
每次做完項目之后,自己想重新回顧一下以前寫的代碼,整理出一些東西,卻發現如同看天書一般,頭暈眼花,完全感覺不像自己的寫的代碼,辣眼睛,猶如下圖
所以為了愛護本人的眼睛,所以覺得很有必要整理一下一些優化代碼的套路…
首先說一個最重要的優化原則:代碼優化是你覺得你代碼很繁瑣、閱讀性很差的時候一定要馬上優化,立刻馬上,不管你現在有多忙,每天優化才叫重構,每年優化那叫重寫
這個原則為什么重要?因為很多程序員會在寫代碼的時候說「先不優化了,等不忙的時候再優化」,然后……就沒有然后了,我也是這樣,所以就導致了大量撈比代碼的產生
該如何優化代碼
1、邏輯復雜的業務代碼一定要有注釋(可能你寫的是爽了,后面維護你代碼的人可能會想往你頭上暴扣)
2、首先是變量名、方法名這些,命名一定要規范,千萬別出現aa、bb這種命名,然后我們可以對我們的一些狀態變量進行集中管理
這個什么意思呢,比如我們在項目中一個訂單的狀態,0代碼已下單、1代表已付款、2代表交易中等等…這一大堆的狀態代表數據。
可能前期我們寫的時候印象很深刻,萬一后期你要改動,又或者需求有變動?你確定你的一堆狀態數字還記得嗎
所以我們在項目開始初期就可以寫一個工具類,來專門管理我們狀態結果
比如
在實際開發中如果項目比較大,甚至可以分模塊來管理,每一個模塊都專門寫一個工具類來管理你的狀態代碼
3、盡量避免重復代碼
當你發現某些代碼重復出現的次數一多,你就應該有想法把它們抽取出來進行優化了
比如我們在做前后端分離項目的時候,后端每一個方法都需要返回固定的Json格式,以前我們是這樣干的,我們可能會封裝一個JSON格式的工具類JsonData,里面有3個參數,第一個是返回碼、第二個是消息提示、第三個是結果集
比如我下面的登錄方法
然后我們發現我們每次都要重復寫我們的狀態碼、消息提示這些東西,那么我們就可以想辦法優化一下了,在固定的地方寫好,我們調用就好了,我們用泛型T指定類型,成功就返回成功的類型,失敗了返回失敗的類型
package com.p2p.p2pstaff.config;public class Result<T> {private int code;private String msg;private T data;/*** 成功時候的調用* */public static <T> Result<T> success(T data){return new Result<T>(data);}/*** 失敗時候的調用* */public static <T> Result<T> error(CodeMsg codeMsg){return new Result<T>(codeMsg);}private Result(T data) {this.data = data;}private Result(int code, String msg) {this.code = code;this.msg = msg;}private Result(CodeMsg codeMsg) {if(codeMsg != null) {this.code = codeMsg.getCode();this.msg = codeMsg.getMsg();}}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;} }我們就在全局狀態管理類中添加我們的失敗狀態
//staffpublic static CodeMsg STAFF_FAIL = new CodeMsg(0,"登錄失敗");然后最終優化后的代碼
public Result<Staff> login(Staff staff){Staff login = staffService.login(staff);if(login != null){//登錄成功return Result.success(login);} else{return Result.error(CodeMsg.STAFF_FAIL);}}前臺后臺兩次md5加鹽加密
后臺md5加密相比大家是耳孰能詳,我們的shiro等很多權限框架都用到了這一點,而在后臺加密依然可能存在密碼被截取的可能性。
想象你的密碼在被加密前就已經被抓取到了那么加密還有什么用呢?也就是截取我們表單提交的內容,這個是有很多辦法能夠實現的,比如我們利用抓包工具等等,所以說密碼一樣存在泄漏的可能。
所以我們就有了在前臺就先加密一次然后再提交到后臺,這樣就算截取到了也是我們加密后的密碼了
所以我們需要在登錄前進行密碼處理
//獲取我們輸入的密碼var inputPass = $("#password").val();/* var g_passsword_salt="1a2b3c4d" */var salt = g_passsword_salt;var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);var password = md5(str);前臺加密完后進入后臺,用我們加密過的密碼進行二次加密,我們兩次加密的密碼都要存入數據庫的,不然我們登錄是無法驗證的
后臺取出我們需要認證的鹽,然后用我們的shiro去認證密碼,這個工具類就和我們以前shiro使用的驗證的是一樣的
/*** 進行密碼驗證** @param credentials 未加密的密碼* @param salt 鹽* @param encryptCredentials 加密后的密碼* @return*/public static boolean checkCredentials(String credentials, String salt, String encryptCredentials) {return encryptCredentials.equals(createCredentials(credentials, salt));} public String login(HttpServletResponse response, LoginVo loginVo) {if(loginVo == null) {throw new GlobalException(CodeMsg.SERVER_ERROR);}String mobile = loginVo.getMobile();String formPass = loginVo.getPassword();//判斷手機號是否存在MiaoshaUser user = getById(Long.parseLong(mobile));if(user == null) {throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);}//驗證密碼String dbPass = user.getPassword();String saltDB = user.getSalt();if(!PasswordHelper.checkCredentials(formPass, saltDB, dbPass)) {throw new GlobalException(CodeMsg.PASSWORD_ERROR);}//生成cookieString token = UUIDUtil.uuid();addCookie(response, token, user);return token;}就這樣兩次加密就完成了
JSR303和全局異常處理
全局異常處理
如果系統發生了異常,不做統一異常處理,前端會給用戶展示一大片看不懂的文字。做統一異常處理后當異常發生后可以給用戶一個溫馨的提示,不至于使用戶滿頭霧水,所以一方面是為了更好的用戶體驗 如果不統一全局異常,服務端和前端在遇到異常的時候處理起來雜亂無章非常費力。所以另一方面是為了制定規范提高工作效率
我們這里也就是通過寫一個全局異常處理類,來處理我們的運行異常,并給與相對應的提示,而不是返回500錯誤i西南西
package com.javaxl.miaosha_02.exception;import java.util.List;import javax.servlet.http.HttpServletRequest;import com.javaxl.miaosha_02.result.CodeMsg; import com.javaxl.miaosha_02.result.Result; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice @ResponseBody public class GlobalExceptionHandler {@ExceptionHandler(value=Exception.class)public Result<String> exceptionHandler(HttpServletRequest request, Exception e){e.printStackTrace();if(e instanceof GlobalException) {GlobalException ex = (GlobalException)e;return Result.error(ex.getCm());} else if(e instanceof BindException) {BindException ex = (BindException)e;List<ObjectError> errors = ex.getAllErrors();ObjectError error = errors.get(0);String msg = error.getDefaultMessage();return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));}else {return Result.error(CodeMsg.SERVER_ERROR);}} }我們的信息提示類,也就是前面接收的封裝全局信息的類,去繼承我們的全局異常處理類,來返回錯誤提示信息
package com.javaxl.miaosha_02.exception;import com.javaxl.miaosha_02.result.CodeMsg;public class GlobalException extends RuntimeException{private static final long serialVersionUID = 1L;private CodeMsg cm;public GlobalException(CodeMsg cm) {super(cm.toString());this.cm = cm;}public CodeMsg getCm() {return cm;}}JSR-303 是 JAVA EE 6 中的一項子規范,叫做 Bean Validation,官方參考實現是Hibernate Validator。
此實現與 Hibernate ORM 沒有任何關系。 JSR 303 用于對 Java Bean 中的字段的值進行驗證。
Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中對表單提交的數據方便地驗證。
我們大部分前臺項目都是做了JS正則判斷的代碼,那么如果別人知道了你的請求地址,它是不是就能跳過你的js驗證,直接去訪問你的數據庫的某一個方法呢?這當然是可以的,所以我們就需要用JSR303來處理這種請求
比如我們在注冊的時候信息必須滿足格式才能插入數據庫,而果然跳過js驗證,那么數據庫就會多很多垃圾數據,這樣肯定是不行的,所以我們就加了驗證在后臺
public Result<String> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {log.info(loginVo.toString());//登錄String token = userService.login(response, loginVo);return Result.success(token);}我在外面登錄的方法中加了自定義注解,驗證格式是否正確,只有通過了驗證才能訪問方法,否則就進入異常處理
自定義注解的代碼
驗證格式是否符合我們的要求
package com.javaxl.miaosha_02.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext;import com.javaxl.miaosha_02.util.ValidatorUtil; import org.apache.commons.lang3.StringUtils;public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {private boolean required = false;public void initialize(IsMobile constraintAnnotation) {required = constraintAnnotation.required();}public boolean isValid(String value, ConstraintValidatorContext context) {if(required) {return ValidatorUtil.isMobile(value);}else {if(StringUtils.isEmpty(value)) {return true;}else {return ValidatorUtil.isMobile(value);}}} }所以只要加了這個注解的就都會先進入驗證才能訪問數據庫
所以我們最后直接通過錯誤格式并訪問不了,而是進了我們的錯誤處理頁面
Redis通用的key生成策略和通用的RedisService方法
通用key生成是個什么概念呢,也就相當于分組了,我們在項目中需要用到redis的地方肯定不止一個模塊,肯定很多模塊都需要用到redis,所以我們在存儲的時候生成一個文件夾,然后每一個key的名字我們以固定的格式給它拼接上,就如下圖效果
BasePrefix
我們通過放射獲取類名,然后拼接上我們的prefix
MiaoshaUserKey生成策略
這也就是生成我們的prefix和規定我們的過期時間的類
最終我們生成看到了就是我們的ClassName+prefix所生成的key
通用的Redis操作類
高并發redis做緩存是很通用的手段
當數據量過大時操作redis就有可能出現重復的現象
然后我們通過泛型封裝一個通用的存值、取值、自增、自減等操縱的方法,盡量來避免這些問題
package com.javaxl.miaosha_02.redis;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSON;import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool;@Service public class RedisService {@AutowiredJedisPool jedisPool;/*** 獲取當個對象* */public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {Jedis jedis = null;try {jedis = jedisPool.getResource();//生成真正的keyString realKey = prefix.getPrefix() + key;String str = jedis.get(realKey);T t = stringToBean(str, clazz);return t;}finally {returnToPool(jedis);}}/*** 設置對象* */public <T> boolean set(KeyPrefix prefix, String key, T value) {Jedis jedis = null;try {jedis = jedisPool.getResource();String str = beanToString(value);if(str == null || str.length() <= 0) {return false;}//生成真正的keyString realKey = prefix.getPrefix() + key;int seconds = prefix.expireSeconds();if(seconds <= 0) {jedis.set(realKey, str);}else {jedis.setex(realKey, seconds, str);}return true;}finally {returnToPool(jedis);}}/*** 判斷key是否存在* */public <T> boolean exists(KeyPrefix prefix, String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();//生成真正的keyString realKey = prefix.getPrefix() + key;return jedis.exists(realKey);}finally {returnToPool(jedis);}}/*** 刪除* */public boolean delete(KeyPrefix prefix, String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();//生成真正的keyString realKey = prefix.getPrefix() + key;long ret = jedis.del(key);return ret > 0;}finally {returnToPool(jedis);}}/*** 增加值* */public <T> Long incr(KeyPrefix prefix, String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();//生成真正的keyString realKey = prefix.getPrefix() + key;return jedis.incr(realKey);}finally {returnToPool(jedis);}}/*** 減少值* */public <T> Long decr(KeyPrefix prefix, String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();//生成真正的keyString realKey = prefix.getPrefix() + key;return jedis.decr(realKey);}finally {returnToPool(jedis);}}private <T> String beanToString(T value) {if(value == null) {return null;}Class<?> clazz = value.getClass();if(clazz == int.class || clazz == Integer.class) {return ""+value;}else if(clazz == String.class) {return (String)value;}else if(clazz == long.class || clazz == Long.class) {return ""+value;}else {return JSON.toJSONString(value);}}@SuppressWarnings("unchecked")private <T> T stringToBean(String str, Class<T> clazz) {if(str == null || str.length() <= 0 || clazz == null) {return null;}if(clazz == int.class || clazz == Integer.class) {return (T)Integer.valueOf(str);}else if(clazz == String.class) {return (T)str;}else if(clazz == long.class || clazz == Long.class) {return (T)Long.valueOf(str);}else {return JSON.toJavaObject(JSON.parseObject(str), clazz);}}private void returnToPool(Jedis jedis) {if(jedis != null) {jedis.close();}}}程序猿的必讀書籍
第一階段:
《C語言程序與設計》
《c++進階寶典》
《Java數據結構和算法》
第二階段:
《教你怎么不生氣》
《老子》
《沉默的憤怒》
第三階段:
《頸椎病康復指南》
《腰椎間盤突出日常護理》
《強迫癥的自我恢復》
第四階段:
《活著》
end…
總結
以上是生活随笔為你收集整理的java优化代码常见套路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新编计算机办公自动化基础教程,新编计算机
- 下一篇: 自己开发的天视通局域网电脑监控软件,需要