zoukankan      html  css  js  c++  java
  • 【原】通过抽象泛型模板优雅的设计可扩展代码

    前言:

       通过学习码云上开源优秀项目后,发现一个较为不错设计方法,所以在此进行总结。

    过程:

                       大致流程是从Service层获取数据,先经过Redis,如果Redis没有数据再去查询db,最后把数据塞回到Redis。

                       看似很简单的一个步骤,实际可以写出不同风格的方式。一般来说首先想到的无非是下面这样(面向过程的伪代码):

        

    Object val = redisutils.get("key");
    if(val == null || val == ""){
        Object obj = userdaoMapper.getUserById("key");
    BeanUtils.cover(obj,User.class); }
    else{ return val; }

              上面的伪代码一看通俗易懂,但是在实际开发中,特别是多模块开发的场景下大量充斥着如上的代码的话那么维护起来是一件费劲的事情,不仅要编写大量的重复代码,还要对不同的实体类型进行一个转换,那么有什么办法可以避免这个问题呢?第一个办法想到的就是熟悉AOP,但在这里不打算介绍AOP,而是如博客标题所示(通过抽象泛型模板)。

          实现方式:

    #首先创建一个缓存抽象模板,这个抽象模板属于基类, 最好定义成泛型,方便后续的扩展。在基类里实现了最核心的功能---》从redis获取数据,如果有则取出来,如果没有则从db获取。  

    public abstract class CacheWorker<P, R>
    {
        private static Log logger = LogFactory.getLog(CacheWorker.class);
    
        @Autowired
        protected RedisUtil redisUtil;
    
        /**
         * get方式获取缓存
         * 
         * @param params 查询参数
         * @param expireSeconds 缓存过期时间
         * @return 结果
         * @throws SQLException
         */
        @SuppressWarnings("unchecked")
        public R get(P params, Class<R> clazz)
        {
            // 获取key,由子类实现
            String key = getKey(params);
            Object res = getCache(key, clazz);
    
            // 如果缓存中存在,直接返回
            if (res != null)
            {
                if (logger.isDebugEnabled())
                {
                    StringBuilder sb = new StringBuilder();
                    sb.append("从redis获取数据 (key:{").append(key).append("})");
    
                    logger.debug(sb.toString());
                }
                return (R) res;
            }
    
            if (logger.isDebugEnabled())
            {
                StringBuilder sb = new StringBuilder();
                sb.append("从redis获取数据失败(key:{").append(key).append("}), 准备从DB获取.");
    
                logger.debug(sb.toString());
            }
    
            // 否则去DB中取
            R dataFromDb = getDataWhenNoCache(params);
            // 回写cache
            if (dataFromDb != null)
            {
                setCache(getExpireSeconds(), key, dataFromDb);
            }
    
            return dataFromDb;
        }
    
        /**
         * 获取过期时间
         * 
         * @return
         */
        protected abstract int getExpireSeconds();
    
        /**
         * set操作 设定缓存
         * 
         * @param expireSeconds
         * @param key
         * @param dataFromDb
         */
        protected void setCache(int expireSeconds, String key, R dataFromDb)
        {
            redisUtil.set(key, dataFromDb, expireSeconds);
        }
    
        /**
         * set操作 从缓存中取值
         * 
         * @param key
         * @return
         */
        protected Object getCache(String key, Class<R> clazz)
        {
            // 尝试获取缓存值
            return redisUtil.get(key, clazz);
        }
    
        public void del(P params)
        {
            // 获取key,由继承者拼接
            String key = getKey(params);
            redisUtil.delete(key);
        }
    
        /**
         * 当获取不到缓存时,使用该方法去DB或其他途径取数据
         * 
         * @param params
         * @return
         * @throws SQLException
         */
        protected abstract R getDataWhenNoCache(P params);
    
        /**
         * 获取key
         * 
         * @param params
         * @return
         */
        protected abstract String getKey(P params);

         

    # 创建一个子类并继承上面的基类,这个子类是一个具体的工作组件。在这个子类里扩展自己的方法。

    @Component
    public class GoodsInfoCacheWorker extends CacheWorker<Integer, Goods>
    {
        @Autowired
        private GoodsMapper goodsMapper;
    
        @Override
        protected Goods getDataWhenNoCache(Integer goodsId)
        {
            return goodsMapper.selectByPrimaryKey(goodsId);
        }
    
        @Override
        protected String getKey(Integer goodsId)
        {
            String key = MessageFormat.format(CommonConstant.RedisKey.GOODS_INFO_BY_ID, new Object[] { goodsId });
            return key;
        }
    
        @Override
        protected int getExpireSeconds()
        {
            return CommonConstant.RedisKeyExpireSeconds.GOODS_STORE_BY_ID;
        }
    
    }

             

    #调用者,通过Spring注入后直接调用 抽象摸板类的 get方法,并传入2个参数,分别是key和对应的Class类。

        @Autowired
        private GoodsInfoCacheWorker goodsInfoCacheWorker;
    
    
        public String getGoodsRandomName(Integer goodsId)
        {
            Goods goods = goodsInfoCacheWorker.get(goodsId, Goods.class);
            long now = System.currentTimeMillis();
    
            // 已经开始了活动,则输出抢购链接
            if (goods.getStartTime().getTime() < now && now < goods.getEndTime().getTime())
            {
                return goods.getRandomName();
            }
    
            return StringUtils.EMPTY;
        }

      ·

  • 相关阅读:
    夺命雷公狗---node.js---21之项目的构建在node+express+mongo的博客项目6之数据的遍历
    夺命雷公狗---node.js---20之项目的构建在node+express+mongo的博客项目5mongodb在项目中实现添加数据
    夺命雷公狗---node.js---19之项目的构建在node+express+mongo的博客项目4mongodb在项目中的基本引入
    夺命雷公狗---node.js---18之项目的构建在node+express+mongo的博客项目3头尾左侧分离法
    夺命雷公狗---node.js---17之项目的构建在node+express+mongo的博客项目2之一,二级路由
    夺命雷公狗---node.js---16之项目的构建在node+express+mongo的博客项目1
    夺命雷公狗---node.js---15之加密
    夺命雷公狗---node.js---14之DNS
    夺命雷公狗---node.js---13之Buffer的转换
    夺命雷公狗---node.js---12之fs模块文件的操作
  • 原文地址:https://www.cnblogs.com/zdd-java/p/8690129.html
Copyright © 2011-2022 走看看