JAVA语言通过注解处理器重构代码,遵循单一职责
小标 2018-07-19 来源 : 阅读 731 评论 0

摘要:本文主要向大家介绍了JAVA语言通过注解处理器重构代码,遵循单一职责,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言通过注解处理器重构代码,遵循单一职责,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

       业务流程介绍:

         #项目是采用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() 方法。由上代码可知每个转换数据代码块都是独立的,例如省和市是属于不同的模块,因此把每个模块进行拆分成不同的处理器,例如ProvinceHandler,AreaHandler,宗旨是让每个Handler专注自己的业务处理。

  

View Code

 

    #继续分析,这些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,中断循环并返回当前处理器,如果不是那么则继续循环匹配下一个处理器。

    定义处理器调度器:

 

@Servicepublic 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 便可以获取到所有实现它的处理器。

  

#继续分析,在上面的代码中已经拿到了所有的处理器,然后就差一件事,那就是根据业务匹配对应的处理器,所以这时候调度处理器就可以发挥它的用途了,将调度处理器注入到缓存切面类,使用方式如下:

@Autowired

 private RedisProcessor redisProcessor;

 

 Object result = redisProcessor.doProcessReturnType(redisAnno, baseReqParam, content, method.getReturnType());

 

总结

· 1 通过上面的重构方式不仅增加了代码的可读性,也减轻了维护成本。

· 2 遵循了单一职责,即每个处理器都在做自己的事情,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中。

· 3 spring提供的getBeanListOfType 方便我们去获取某一类的所有的bean。

         通过下面源码可知该方法返回一个map类型的实例,map中的key为bean的名字,key对应的内容未bean的实例。

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程