【原】抽象的な汎用テンプレートによる優雅な設計拡張コード
前言:
コードクラウド上のオープンソースの優れたプロジェクトを学ぶことで、より良い設計方法を発見したので、ここでまとめます。
プロセス:
大体の流れはサービス層からデータを取得し、まず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;
}
上の疑似コードは一見分かりやすいですが、実際の開発では、特にマルチモジュール開発の場面でこのようなコードが大量にあふれているとメンテナンスが難しく、大量の重複コードを書くだけでなく、異なるエンティティタイプを変換する必要がありますが、この問題を回避するにはどうすればいいのでしょうか.1つ目の方法はAOPに詳しいことですが、ここではAOPを紹介するつもりはなく、ブログのタイトルに示すように(抽象的な汎用テンプレートを通じて).
実装方法:
#まずキャッシュ抽象テンプレートを作成します。この抽象テンプレートはベースクラスに属し、汎用的に定義し、後続の拡張を容易にすることが望ましいです。ベースクラスで最も核心的な機能を実現した--』redisからデータを取得し、あれば取り出し、なければdbから取得する。
public abstract class CacheWorker
{
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 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 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
{
@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メソッドを直接呼び出し、keyと対応するClassクラスの2つのパラメータを入力します。
@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;
}
·