【原】經過抽象泛型模板優雅的設計可擴展代碼

  前言:

      經過學習碼雲上開源優秀項目後,發現一個較爲不錯設計方法,因此在此進行總結。

  過程:

                   大體流程是從Service層獲取數據,先通過Redis,若是Redis沒有數據再去查詢db,最後把數據塞回到Redis。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,而是如博客標題所示(經過抽象泛型模板)。app

 

      實現方式:

#首先建立一個緩存抽象模板,這個抽象模板屬於基類, 最好定義成泛型,方便後續的擴展。在基類裏實現了最核心的功能---》從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;
    }

 

  ·dom

相關文章
相關標籤/搜索