jdk8 | Supplier<T>实践应用

最近呢,看到应该系统应用中有一个一二级缓存模块的设计(基于google guava和redis的一二级缓存设计实现),早期设计虽然鉴于当时实践考虑已经自我感觉涉及还不错,但是最近再看到这块代码,有一种想要继续提炼升华的冲动。鉴于此,有了本篇中在设计中基于Supplier的实践场景应用。

1.场景再现

类图

如上图,抽象类AbstractCacheManager<K,HK,V>的若干个子类实现了相关方法,但是部分方法依然存在冗余特征。

我们来看一下子类的实现示例

/**
 * @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.<Result<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.<Result<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.<Result<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.<Result<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.<Result<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<List<SimpleDistrictRe>> queryCitiesByParentCode(String parentCode) Result<List<SimpleDistrictRe>> queryProvinces()
我们仔细发现都存在相同的业务动作。

  • 判断是否开启缓存,关闭时从数据库加载。
  • 构建上下文对象CacheContext,然后调用primaryCache().get(x,() -> super.getFromSecondary(context))方法。
  • 而且都通过try…catch…包裹。

分析上述业务动作,这就是明显的模板方法,鉴于此,我们把每个子类共同冗余的模板动作提取到抽象类AbstractCacheManager中。

提取一个公共方法V fromCache(K key,CacheContext<V> context,Supplier<V> dataSourceCaller)如下:

/**
 * 从缓存中获取数据
 * 执行逻辑:
 * (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,HK,V>定义为泛型,其中K为缓存key,HK为缓存小Key,V作为缓存加载的返回值。所以我们根据规约,封装
如上方法,我们使用了一个jdk8中的一个函数,Supplier<T>,该函数通过获取可以拿到我们指定类型的返回值<V>

修改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.<Result<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.<Result<List<SimpleDistrictRe>>>builder()
            .key(cacheKey).reference(TypeReferences.SIMPLE_DISTRICT_TYPE).expireSeconds(DEFAULT_EXPIRE_SECONDS)
            .callback(supplier).build();
}

2.Supplier

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

该函数可以返回指定类型,通过标注@FunctionalInterface,声明该方法支持lambda表达式。
在JDK8之前,我们通过需要new Supplier并实现get()方法,这也就是所谓内部匿名类的实现方式。
而JDK8 lambda就是简化这一冗余的代码,只需要 () -> xx一个代码语义表达即可。

3.总结

其实,JDK,stream api大量通过BiFunctionBiConsumerFunctionPredicateConsummerSupplier函数实现函数式编程,而仔细
查看他们源码,发现都通过@FunctionalInterface标注一个注解,以支持lambda表达式。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值