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本身。

            

  • 相关阅读:
    Palindrome Partitioning
    triangle
    Populating Next Right Pointers in Each Node(I and II)
    分苹果(网易)
    Flatten Binary Tree to Linked List
    Construct Binary Tree from Inorder and Postorder Traversal(根据中序遍历和后序遍历构建二叉树)
    iOS系统navigationBar背景色,文字颜色处理
    登录,注销
    ios 文字上下滚动效果Demo
    经常崩溃就是数组字典引起的
  • 原文地址:https://www.cnblogs.com/zdd-java/p/8798054.html
Copyright © 2011-2022 走看看