zoukankan      html  css  js  c++  java
  • 【原】通过Spring重构代码,解耦不同业务

     流程介绍:

             #项目是采用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提供的api,在项目启动的时候就把不同的处理器扫描出来,放到一个集合里面。  

    •  首先抽取出一个Handler接口如下:
    public interface IRedisHandler {
        //匹配key
        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;
        }
    • 编写一个业务处理器(ProvinceRedisHandler,主要实现了2个核心的方法,一个是 handleReturnType,一个是 canHandle)

       handleReturnType:具体处理逻辑

            canHandle:是否匹配,如何匹配呢?看自身业务逻辑

    @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 是我自己封装的一个方法,实际内部调用的是 Spring的 getBeanOfType方法。

       解释:getBeanOfType:获取某一类型下的所有的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.返回封装好的结果给最调用方。

    总结

    • 1 通过上面的重构方式不仅增加了代码的可读性,也减轻了维护成本。
    • 2 遵循了单一职责,即每个处理器都在做自己的事情,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中。
    • 3 spring提供的  getBeanListOfType方法方便我们去获取某一类的所有的bean。

             通过下面源码可知该方法返回一个map类型的实例,map中的key为bean的名字,value则是bean本身。

            

  • 相关阅读:
    九度OJ 1010 A+B
    九度OJ 1052 找x
    oracle数据库创建备份与恢复 脚本
    在select标签中添加a标签
    如何在select标签中使用a标签跳转页面
    网站访问不了
    js实现input的赋值
    PHP如何实现百万级数据导出
    互联网产品需求管理杂思2需求收集,互联网营销 狼人:
    做网站用UTF8还是GB2312?,互联网营销 狼人:
  • 原文地址:https://www.cnblogs.com/zdd-java/p/8798054.html
Copyright © 2011-2022 走看看