#項目是採用Spring Boot框架搭建的。定義了一個@Redis註解在控制層,而後當請求過來的時候會被Spring Aop攔截到對應的切面類,接着是解析相關參數拼接key調用Redis工具類查詢,若是沒有再去數據庫查詢,不然直接返回數據。java
#因爲不少地方都用到了這個註解緩存,而且在處理返回數據的時候須要轉換成對應的VO,例如請求的是查詢省份服務,那麼返回的要轉換成List<ProvinceVo> 這種,若是是查詢市區服務,那麼要轉換成List<AreaVo>,記得剛開始在代碼裏是這樣寫的(僞代碼):redis
if(type == 1){ JsonUtil.jsonToObject(dataNode, List.class, AssociateAreasVo.class); }else if(type == 2){ JsonUtil.jsonToObject(dataNode, List.class, XXX.class); }else if(type == 3){ JsonUtil.jsonToObject(dataNode, List.class, XXX.class); }else if(type == 4){ JsonUtil.jsonToObject(dataNode, List.class, XXX.class); }else{ ........... }
#上面的代碼隨着業務的變動和需求的擴展不斷膨脹,本來是處理緩存切面的一個類瞬間耦合了一大堆不相關的代碼,維護起來很是困難,並且有開發人員常常不當心就改到其它人的代碼,致使服務不可用的狀況。所以進行了重構,以免後面不可維護性。spring
#由上代碼可知每一個轉換數據代碼塊都是獨立的,例如省和市是屬於不一樣的模塊,所以把每一個模塊進行拆分紅不一樣的處理器,而後經過spring提供的api,在項目啓動的時候就把不一樣的處理器掃描出來,放到一個集合裏面。 數據庫
public interface IRedisHandler { public String handleKey(Redis redisAnno, BaseReqParam param); //處理轉換數據 public Object handleReturnType(Redis redisAnno, BaseReqParam param, String content, Class clazz) throws IOException; //匹配處理器 public boolean canHandle(Redis redisAnno, BaseReqParam param); //處理結果 public void handleResult(Redis redisAnno, BaseReqParam param, Object result, String redisKey); }
#繼續分析,可否直接實現這個接口?答案是不行。json
緣由:在緩存切面類裏,咱們要根據一些條件區分出選擇哪一個處理器進行處理,若是直接去實現這個接口是沒有意義的。應該是聲明一個抽象模板類(AbstractRedisHandler)實現這個接口, 而後其它處理器再繼承這個抽象模板類。api
public abstract class AbstractRedisHandler implements RedisHandler { private static Logger logger = Logger.getLogger(AbstractRedisHandler.class); @Autowired protected RedisService redisService; @Override public String handleKey(Redis redisAnno, BaseReqParam param) { return redisAnno.key(); } @Override public Object handleReturnType(Redis redisAnno, BaseReqParam param, String content, Class clazz) throws IOException { return handleReturnType(redisAnno, param, content, clazz, null); } protected Object handleReturnType(Redis redisAnno, BaseReqParam param, String content, Class clazz, Class dataClass) throws IOException { JsonNode jsonNode = JsonUtil.parseJson(content); ResultVo result = getResult(jsonNode); if (dataClass == null) { dataClass = getDataClass(clazz); logger.info("獲得數據類型:" + dataClass); } if (dataClass != null) { JsonNode dataNode = jsonNode.path("data"); if (!JsonUtil.isNullNode(dataNode)) { Object data = JsonUtil.jsonToObject(dataNode, dataClass); result.setData(data); } } return result; } private Class getDataClass(Class clazz) { try { BeanInfo beanInfo = Introspector.getBeanInfo(clazz); PropertyDescriptor[] arr = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor propDesc : arr) { String key = propDesc.getName(); if ("data".equals(key)) { Method setter = propDesc.getWriteMethod(); Class<?>[] classArr = setter.getParameterTypes(); return classArr[0]; } } } catch (IntrospectionException e) { e.printStackTrace(); } catch (Throwable e) { e.printStackTrace(); } return null; } @Override public void handleResult(Redis redisAnno, BaseReqParam param, Object result, String redisKey) { try { if (StringUtils.isNotEmpty(redisKey)) { logger.info("set to redis"); String jsonContent = JsonUtil.toJsonString(result); redisService.set(redisKey, jsonContent, redisAnno.expireTime()); } } catch (IOException e) { e.printStackTrace(); } } public ResultVo getResult(JsonNode jsonNode) { String resultCode = null; String resultMsg = null; String errorMsg = null; JsonNode resultCodeNode = jsonNode.path("resultCode"); if (!JsonUtil.isNullNode(resultCodeNode)) { resultCode = resultCodeNode.asText(); } JsonNode resultMsgNode = jsonNode.path("resultMsg"); if (!JsonUtil.isNullNode(resultMsgNode)) { resultMsg = resultMsgNode.asText(); } JsonNode errorMsgNode = jsonNode.path("errorMsg"); if (!JsonUtil.isNullNode(errorMsgNode)) { errorMsg = errorMsgNode.asText(); } ResultVo result = new ResultVo(); result.setResultCode(resultCode); result.setResultMsg(resultMsg); result.setErrorMsg(errorMsg); return result; }
@Service public class ProvinceRedisHandler extends AbstractRedisHandler implements RedisHandler { @Override public Object handleReturnType(Redis redisAnno, BaseReqParam param, String content, Class clazz) throws IOException { JsonNode jsonNode = JsonUtil.parseJson(content); ResultVo result = getResult(jsonNode); JsonNode dataNode = jsonNode.path("data"); if (!JsonUtil.isNullNode(dataNode)) { List<AreaInfoVO> list = JsonUtil.jsonToObject(dataNode, List.class, AreaInfoVO.class); result.setData(list); } return result; } @Override public boolean canHandle(Redis redisAnno, BaseReqParam param) { if ("provinceList".equals(redisAnno.type()) && param instanceof ProvinceListReqParam) { return true; } return false; } }
#最後要作的就是要如何去匹配處理器了,這裏的方案是將處理器封裝到一個List,而後取出@Redis註解裏的type屬性,並循環List判斷當前處理器是否能匹配得。例如@Redis(key="gateway:checkBankAccountData", type = "checkBankAccountData") ,在處理器內部判斷type是否equals "checkBankAccountData",若是是返回true,中斷循環並返回當前處理器,若是不是那麼則繼續循環匹配下一個處理器。緩存
@Service
public class RedisProcessor {
private static Logger logger = Logger.getLogger(RedisProcessor.class);
private List<RedisHandler> handlers;
private boolean isInitHandlers = false;
public String doProcessKey(Redis redisAnno, BaseReqParam param) {
RedisHandler handler = findHandler(redisAnno, param);
if (handler != null) {
return handler.handleKey(redisAnno, param);
}
return null;
}
//這裏是處理返回的數據
public Object doProcessReturnType(Redis redisAnno, BaseReqParam param, String content, Class clazz) throws IOException {
//這裏是根據redisAnno和param兩個參數去匹配對應的處理器。
RedisHandler handler = findHandler(redisAnno, param);
if (handler != null) {
//因爲上面已經匹配到對應的處理器,這裏會調用對應的處理器去處理
return handler.handleReturnType(redisAnno, param, content, clazz);
}
return null;
}
public void doProcessResult(Redis redisAnno, BaseReqParam param, Object result, String redisKey) {
RedisHandler handler = findHandler(redisAnno, param);
if (handler != null) {
handler.handleResult(redisAnno, param, result, redisKey);
}
}
private RedisHandler findHandler(Redis redisAnno, BaseReqParam param) {
initHandlers();
if (handlers != null && handlers.size() > 0) {
RedisHandler defaultRedisHandler = null;
for (RedisHandler handler : handlers) {
if (handler instanceof DefaultRedisHandler) {
defaultRedisHandler = handler;
continue;
}
if (handler.canHandle(redisAnno, param)) {
return handler;
}
}
if (defaultRedisHandler != null) {
return defaultRedisHandler;
}
}
return null;
}
//這裏是初始化handers,並把handler封裝到list,用於調度處理器匹配對應的handler。
private synchronized void initHandlers() {
if (!isInitHandlers) {
handlers = SpringContextUtil.getBeanListOfType(IRedisHandler.class);
isInitHandlers = true;
}
}
}
* 注意: SpringContextUtil.getBeanListOfType 是我本身封裝的一個方法,實際內部調用的是 getBeanOfType。框架
解釋:getBeanOfType 顧名思義:獲取某一類的全部的bean。ide
1.該方法返回一個map類型的實例,map中的key爲bean的名字,key對應的內容未bean的實例。工具
2.該方法有兩種類型的重載:
getBeansOfType(Class),獲取某一類的全部的bean。getBeansOfType(Class,boolean,boolean),後面兩個布爾值,第一表明是否也包含原型(Class祖先)bean或者或者只是singletons(包含FactoryBean生成的),第二個表示是否當即實例化懶加載或者由FactoryBean生成的Bean以保證依賴關係。
經過上面代碼可知,IRedisHandler做爲一個接口,被其它處理器實現後,調用getBeanOfType便能夠獲取到全部實現它的類。例如ProvinceHandlers實現了IRedisHandler,那麼SpringContextUtil.getBeanListOfType即可以找出ProvinceHandlers。
#繼續分析,在上面的代碼中已經拿到了全部的處理器,而後就差一件事,那就調用方要如何選擇對應的處理器進行處理呢?這時候在上面定義的RedisProcessor調度處理器就能夠發揮它的用途了,將調度處理器注入到緩存切面類,使用方式以下:
@Autowired
private RedisProcessor redisProcessor; Object result = redisProcessor.doProcessReturnType(redisAnno, baseReqParam, content, method.getReturnType());
#上面調用流程是這樣的:
1.進入到RedisProcessor調度處理器的doProcessReturnType方法。
2.在doProcessReturnType方法內會執行findHandler方法,根據傳過來的參數去匹配具體處理器,說白了就是路由匹配。
3.經過匹配到的處理器就執行具體的業務操做。
4.返回封裝好的結果給最上層。
經過下面源碼可知該方法返回一個map類型的實例,map中的key爲bean的名字,value則是bean自己。