【原】JAVA责任链模式重构代码,遵循单一职责
2018-06-18 02:09:41来源:未知 阅读 ()
前言:最近在看一个内部网关代码的时候,发现处理Redis返回数据这块写的不错,今天有时间好好研究下里面的知识点。
业务流程介绍:
#项目是采用Spring Boot框架搭建的。定义了一个@Redis注解在控制层,然后当请求过来的时候会被Spring Aop拦截到对应的切面类,接着是解析相关参数拼接key调用Redis工具类查询,如果没有再去数据库查询,否则直接返回数据。
亮点:
#由于很多地方都用到了这个注解缓存,并且在处理返回数据的时候需要转换成对应的VO,例如请求的是查询省份服务,那么返回的要转换成List<ProvinceVo> 这种,如果是查询市区服务,那么要转换成List<AreaVo>,记得刚开始在代码里是这样写的(伪代码):
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的 getBeansOfType() 方法,这个方法可以获取某一类的所有的bean,由上代码可知每个转换数据代码块都是独立的,例如省和市是属于不同的模块,因此把每个模块进行拆分成不同的处理器,例如ProvinceHandler,AreaHandler,宗旨是让每个Handler专注自己的业务处理。
@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; } }
#继续分析,这些Handler有公共的处理方法,那就是转换数据,匹配处理器等等,于是定义一个接口IRedisHandler。
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);
}
#继续分析,能否直接实现这个接口?答案是不行。
原因:在缓存切面类里,我们要根据一些条件区分出选择哪个处理器进行处理,如果直接去实现这个接口是没有意义的。应该是声明一个抽象模板类(AbstractRedisHandler)实现这个接口, 然后其它处理器再继承这个抽象模板类。
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; }
#最后要做的就是要如何去匹配处理器了,这里的方案是将处理器封装到一个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;
}
}
}
@核心地方:getBeanListOfType ---->获取某一类的所有的bean。通过上面代码可知,IRedisHandler作为一个接口,被其它处理器实现后,调用getBeanListOfType便可以获取到所有实现它的类。例如ProvinceHandlers实现了IRedisHandler,那么SpringContextUtil.getBeanListOfType便可以找出ProvinceHandlers。
#继续分析,在上面的代码中已经拿到了所有的处理器,然后就差一件事,那就调用方要如何选择对应的处理器进行处理呢?这时候在上面定义的RedisProcessor调度处理器就可以发挥它的用途了,将调度处理器注入到缓存切面类,使用方式如下:
@Autowired
private RedisProcessor redisProcessor;
Object result = redisProcessor.doProcessReturnType(redisAnno, baseReqParam, content, method.getReturnType());
#当执行redisProcessor.doProcessReturnType这段代码的时候,流程是这样的:进入到RedisProcessor调度处理器的doProcessReturnType方法,然后会执行findHandler,在里面根据传过来的参数去匹配具体处理器,然后由这个处理器就执行具体的业务操作,最后返回封装好的结果给最上层。
总结
- 1 通过上面的重构方式不仅增加了代码的可读性,也减轻了维护成本。
- 2 遵循了单一职责,即每个处理器都在做自己的事情,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中。
- 3 spring提供的getBeanListOfType 方便我们去获取某一类的所有的bean。
通过下面源码可知该方法返回一个map类型的实例,map中的key为bean的名字,key对应的内容未bean的实例。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash