最近呢,看到应该系统应用中有一个一二级缓存模块的设计(基于google guava和redis的一二级缓存设计实现),早期设计虽然鉴于当时实践考虑已经自我感觉涉及还不错,但是最近再看到这块代码,有一种想要继续提炼升华的冲动。鉴于此,有了本篇中在设计中基于
Supplier
的实践场景应用。
如上图,抽象类
AbstractCacheManager
的若干个子类实现了相关方法,但是部分方法依然存在冗余特征。
我们来看一下子类的实现示例
/**
* @description: 行政编码-缓存管理器
* @Date : 2019/5/5 下午5:32
* @Author : 石冬冬-Seig Heil
*/
@Component
@Slf4j
public class SimpleDistrictCacheManager extends AbstractCacheManager<String,String,Result<List<SimpleDistrictRe>>> {
@Autowired
DiamondConfig diamondConfig;
@Autowired
DictionaryRegFacade dictionaryRegFacade;
/**
* short name
*/
final String SHORT_NAME = CacheShortName.simpleDistrictCache.name();
/**
* 省份列表缓存key
*/
final String PROVINCE_CACHE_KEY = "provinceCache";
/**
* 二级行政缓存key
*/
final String SECONDARY_DISTRICT_CACHE_KEY = "secondaryDistrictCache";
/**
* 行政缓存key
*/
final String DISTRICT_CACHE_KEY = "districtCache";
/**
* 联动缓存 Key 前缀
*/
final String GANGED_CACHE_KEY = "gangedCache";
/**
* 默认失效时间-2小时(单位秒)
*/
final long DEFAULT_EXPIRE_SECONDS = 7200;
/**
* 根据父级行政查询下级
* @param parentCode
* @return
*/
public Result<List<SimpleDistrictRe>> queryCitiesByParentCode(String parentCode){
if(!useCache()){
return dictionaryRegFacade.queryCitiesByParentCode(parentCode);
}
Result<List<SimpleDistrictRe>> queryResult;
try {
CacheContext<Result<List<SimpleDistrictRe>>> context = CacheContext.<List<SimpleDistrictRe>>>builder()
.key(parentCode).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(() -> Optional.ofNullable(dictionaryRegFacade.queryCitiesByParentCode(parentCode))).build();
queryResult = primaryCache().get(parentCode,() -> super.getFromSecondary(context));
} catch (ExecutionException e) {
log.info("{} focus on an exception,then execute queryDB,parentCode={}",SHORT_NAME,parentCode,e);
queryResult = dictionaryRegFacade.queryCitiesByParentCode(parentCode);
log.info("{} focus on an exception,then execute queryDB,parentCode={},value={}",SHORT_NAME,parentCode, JSONObject.toJSONString(queryResult));
}
return queryResult;
}
/**
* 查询省份列表
* @return
*/
public Result<List<SimpleDistrictRe>> queryProvinces(){
if(!useCache()){
return dictionaryRegFacade.queryProvinces();
}
Result<List<SimpleDistrictRe>> queryResult;
try {
CacheContext<Result<List<SimpleDistrictRe>>> context = CacheContext.<List<SimpleDistrictRe>>>builder()
.key(PROVINCE_CACHE_KEY).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(() -> Optional.ofNullable(dictionaryRegFacade.queryProvinces())).build();
queryResult = primaryCache().get(PROVINCE_CACHE_KEY,() -> super.getFromSecondary(context));
} catch (ExecutionException e) {
log.info("{} focus on an exception,then execute queryDB",SHORT_NAME,e);
queryResult = dictionaryRegFacade.queryProvinces();
log.info("{} focus on an exception,then execute value={}",SHORT_NAME,JSONObject.toJSONString(queryResult));
}
return queryResult;
}
/**
* 查询二级城市
* @return
*/
public Result<List<SimpleDistrictRe>> querySecondaryDistricts(){
if(!useCache()){
return dictionaryRegFacade.querySecondaryDistricts();
}
Result<List<SimpleDistrictRe>> queryResult;
try {
CacheContext<Result<List<SimpleDistrictRe>>> context = CacheContext.<List<SimpleDistrictRe>>>builder()
.key(SECONDARY_DISTRICT_CACHE_KEY).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(() -> Optional.ofNullable(dictionaryRegFacade.querySecondaryDistricts())).build();
queryResult = primaryCache().get(SECONDARY_DISTRICT_CACHE_KEY,() -> super.getFromSecondary(context));
} catch (ExecutionException e) {
log.info("{} focus on an exception,then execute queryDB",SHORT_NAME,e);
queryResult = dictionaryRegFacade.queryProvinces();
log.info("{} focus on an exception,then execute value={}",SHORT_NAME,JSONObject.toJSONString(queryResult));
}
return queryResult;
}
/**
* 查询所有行政列表
* @return
*/
public Result<List<SimpleDistrictRe>> queryAll(){
if(!useCache()){
return dictionaryRegFacade.queryAll();
}
Result<List<SimpleDistrictRe>> queryResult;
try {
CacheContext<Result<List<SimpleDistrictRe>>> context = CacheContext.<List<SimpleDistrictRe>>>builder()
.key(DISTRICT_CACHE_KEY).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(() -> Optional.ofNullable(dictionaryRegFacade.queryAll())).build();
queryResult = primaryCache().get(DISTRICT_CACHE_KEY,() -> super.getFromSecondary(context));
} catch (ExecutionException e) {
log.info("{} focus on an exception,then execute queryDB",SHORT_NAME,e);
queryResult = dictionaryRegFacade.queryAll();
log.info("{} focus on an exception,then execute value={}",SHORT_NAME,JSONObject.toJSONString(queryResult));
}
return queryResult;
}
/**
* 联动查询行政列表
* @param dto
* @return
*/
public Result<List<SimpleDistrictRe>> queryWithGanged(DistrictGangedDTO dto) {
if(!useCache()){
return dictionaryRegFacade.queryWithGanged(dto);
}
Result<List<SimpleDistrictRe>> queryResult;
StringBuilder keyBuilder = new StringBuilder(GANGED_CACHE_KEY).append(":").append(dto.getRegLevel());
if(StringTools.isNotEmpty(dto.getGbCode())) {
keyBuilder.append(":").append(dto.getGbCode());
}
try {
CacheContext<Result<List<SimpleDistrictRe>>> context = CacheContext.<List<SimpleDistrictRe>>>builder()
.key(keyBuilder.toString()).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(() -> Optional.ofNullable(dictionaryRegFacade.queryWithGanged(dto))).build();
queryResult = primaryCache().get(keyBuilder.toString(),() -> super.getFromSecondary(context));
} catch (ExecutionException e) {
log.info("{} focus on an exception,then execute queryDB",SHORT_NAME,e);
queryResult = dictionaryRegFacade.queryWithGanged(dto);
log.info("{} focus on an exception,then execute value={}",SHORT_NAME,JSONObject.toJSONString(queryResult));
}
return queryResult;
}
@Override
public Cache<String, Result<List<SimpleDistrictRe>>> primaryCache() {
return SimpleDistrictCacheFactory.get();
}
@Override
public boolean useCache() {
boolean useCache = false;
try {
useCache = cacheSwitch().simpleDistrictEnable;
log.info("{} useCache={}",SHORT_NAME,useCache);
} catch (Exception e) {
log.error("{} useCache={}",SHORT_NAME,useCache,e);
}
return useCache;
}
@Override
public String shortName() {
return SHORT_NAME;
}
}
如上述子类SimpleDistrictCacheManager
,在实现抽象类相关方法时,并对外部提供的相关方法,譬如Result
、> queryCitiesByParentCode(String parentCode)
Result
> queryProvinces()
我们仔细发现都存在相同的业务动作。
CacheContext
,然后调用primaryCache().get(x,() -> super.getFromSecondary(context))
方法。分析上述业务动作,这就是明显的模板方法,鉴于此,我们把每个子类共同冗余的模板动作提取到抽象类AbstractCacheManager
中。
提取一个公共方法V fromCache(K key,CacheContext
如下:
/**
* 从缓存中获取数据
* 执行逻辑:
* (1)、缓存开关关闭时,从 dataSourceCaller 获取。
* (2)、缓存开关开启时,从一级缓存获取,一级缓存没有则从二级缓存获取。
* @param key 缓存Key
* @param context 构建缓存上下文对象
* @param dataSourceCaller 数据库查找回调器
* @return
*/
V fromCache(K key,CacheContext<V> context,Supplier<V> dataSourceCaller){
final String SHORT_NAME = shortName();
if(!useCache()){
return dataSourceCaller.get();
}
V queryResult;
try {
queryResult = primaryCache().get(key,() -> getFromSecondary(context));
} catch (ExecutionException e) {
log.info("[{}]ExecutionException,degraded queryDB",SHORT_NAME,e);
queryResult = dataSourceCaller.get();
log.info("[{}]ExecutionException,degraded queryDB={}",SHORT_NAME,JSONObject.toJSONString(queryResult));
}
return queryResult;
}
上述,既然抽象类
AbstractCacheManager
定义为泛型,其中K
为缓存key,HK
为缓存小Key,V
作为缓存加载的返回值。所以我们根据规约,封装
如上方法,我们使用了一个jdk8中的一个函数,Supplier
,该函数通过获取可以拿到我们指定类型的返回值。
修改SimpleDistrictCacheManager
的方法实现
@Component
@Slf4j
public class SimpleDistrictCacheManager extends AbstractCacheManager<String,String,Result<List<SimpleDistrictRe>>> {
@Autowired
DictionaryRegFacade dictionaryRegFacade;
/**
* short name
*/
static final String SHORT_NAME = CacheShortName.simpleDistrictCache.name();
/**
* 省份列表缓存key
*/
static final String PROVINCE_CACHE_KEY = "provinceCache";
/**
* 二级行政缓存key
*/
static final String SECONDARY_DISTRICT_CACHE_KEY = "secondaryDistrictCache";
/**
* 行政缓存key
*/
static final String DISTRICT_CACHE_KEY = "districtCache";
/**
* 联动缓存 Key 前缀
* {0} 行政级别
* {1} 国标码
*/
static final String GANGED_CACHE_KEY_PATTERN = "gangedCache:{0}:{1}";
/**
* 默认失效时间-2小时(单位秒)
*/
static final long DEFAULT_EXPIRE_SECONDS = 7200;
/**
* 根据父级行政查询下级
* @param parentCode
* @return
*/
public Result<List<SimpleDistrictRe>> queryCitiesByParentCode(String parentCode){
return fromCache(parentCode,
buildContext(parentCode,() -> Optional.ofNullable(dictionaryRegFacade.queryCitiesByParentCode(parentCode))),
() -> dictionaryRegFacade.queryCitiesByParentCode(parentCode));
}
/**
* 查询省份列表
* @return
*/
public Result<List<SimpleDistrictRe>> queryProvinces(){
return fromCache(PROVINCE_CACHE_KEY,
buildContext(PROVINCE_CACHE_KEY,() -> Optional.ofNullable(dictionaryRegFacade.queryProvinces())),
() -> dictionaryRegFacade.queryProvinces());
}
/**
* 查询二级城市
* @return
*/
public Result<List<SimpleDistrictRe>> querySecondaryDistricts(){
return fromCache(SECONDARY_DISTRICT_CACHE_KEY,
buildContext(SECONDARY_DISTRICT_CACHE_KEY,() -> Optional.ofNullable(dictionaryRegFacade.querySecondaryDistricts())),
() -> dictionaryRegFacade.querySecondaryDistricts());
}
/**
* 查询所有行政列表
* @return
*/
public Result<List<SimpleDistrictRe>> queryAll(){
return fromCache(DISTRICT_CACHE_KEY,
buildContext(DISTRICT_CACHE_KEY,() -> Optional.ofNullable(dictionaryRegFacade.queryAll())),
() -> dictionaryRegFacade.queryAll());
}
/**
* 联动查询行政列表
* @param dto
* @return
*/
public Result<List<SimpleDistrictRe>> queryWithGanged(DistrictGangedDTO dto) {
String cacheKey = MessageFormat.format(GANGED_CACHE_KEY_PATTERN,dto.getRegLevel(),dto.getGbCode());
return fromCache(cacheKey,
buildContext(cacheKey,() -> Optional.ofNullable(dictionaryRegFacade.queryWithGanged(dto))),
() -> dictionaryRegFacade.queryWithGanged(dto));
}
/**
* 构建缓存上下文对象
* @param cacheKey 缓存key
* @param supplier 数据库接口查询回调
* @return
*/
CacheContext<Result<List<SimpleDistrictRe>>> buildContext(String cacheKey,Supplier<Optional<Result<List<SimpleDistrictRe>>>> supplier){
return CacheContext.<List<SimpleDistrictRe>>>builder()
.key(cacheKey).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(supplier).build();
}
@Override
public Cache<String, Result<List<SimpleDistrictRe>>> primaryCache() {
return SimpleDistrictCacheFactory.get();
}
@Override
public boolean useCache() {
boolean useCache = false;
try {
useCache = cacheSwitch().simpleDistrictEnable;
log.debug("{} useCache={}",SHORT_NAME,useCache);
} catch (Exception e) {
log.error("{} useCache={}",SHORT_NAME,useCache,e);
}
return useCache;
}
@Override
public String shortName() {
return SHORT_NAME;
}
}
只需要调用抽象类的
fromCache
方法即可,同时在类,进而把CacheContext
的对象实例构建提取了一个方法。就是如下:
但是总体上发现这些对外提供的方法清爽简洁了许多(try…catch不见了诶等等)。
/**
* 构建缓存上下文对象
* @param cacheKey 缓存key
* @param supplier 数据库接口查询回调
* @return
*/
CacheContext<Result<List<SimpleDistrictRe>>> buildContext(String cacheKey,Supplier<Optional<Result<List<SimpleDistrictRe>>>> supplier){
return CacheContext.<List<SimpleDistrictRe>>>builder()
.key(cacheKey).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
.callback(supplier).build();
}
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
该函数可以返回指定类型,通过标注
@FunctionalInterface
,声明该方法支持lambda表达式。
在JDK8之前,我们通过需要new Supplier
并实现get()
方法,这也就是所谓内部匿名类的实现方式。
而JDK8 lambda就是简化这一冗余的代码,只需要() -> xx
一个代码语义表达即可。
其实,JDK,stream api大量通过
BiFunction
、BiConsumer
、Function
、Predicate
、Consummer
、Supplier
函数实现函数式编程,而仔细
查看他们源码,发现都通过@FunctionalInterface
标注一个注解,以支持lambda表达式。