• 面向对象一: 数据加载器完成缓存


    面向对象一: 数据加载器完成缓存

    某微服务,它的作用是导出一系列复杂的组合信息,那么该微服务取到request后,可能需要进行多次与数据库的交互,比如根据用户ID多次取到完整的用户信息,根据商品ID多次取到完整的商品信息,根据时间信息取到多次商品的购买详情,然后整理这些信息并导出。

    当某业务需要进行多次与数据库的相同交互,查询到数据后拼装出结果时,如果每次都进行查询非常耗时,可以考虑做一个缓存的系统,把对象放入map中缓存起来,然后再用key将数据取出:

    Object cache = cacheMap.get(key);
    

    明确获取缓存的方式

    可以设置一个工具类将方法抽取出来:

    Object cache = CacheUtils.getCache(clazz, key);
    

    其中clazz是缓存的类型,因为上面提到的用户信息、商品信息、购买详情信息各不相同,这也就无法统一用某种基类或接口统一表示;key是取缓存需要的键值,比如取出用户信息需要用户ID。

    但是这样的设计有一个问题,那就是开发者在调用缓存方法时,需要同时知道key和类型,其实key并不是必须的,而且也是难以获知的,更好的方法是先根据request生成一个上下文对象,然后再取缓存:

    Context context = new Context(request);
    context.getCache(clazz);
    

    这样处理之后,开发者就只需要给出clazz,而不需要提供key,至于用什么key来取缓存,在context中会根据不同的类型进行灵活选择,封装层次进一步提升。

    当然也可以把context注入另一个类CacheManager中,然后由CacheManager提供缓存的功能,Context仅仅作为上下文对象:

    CacheManager cacheManager = new CacheManager(context);
    cacheManager.getCache(clazz);
    

    这样设计严格符合类的单一职责原则。

    泛型的引入

    比较难的问题在于缓存的对象各不相同,这也就无法统一用某种基类或接口统一表示,初步设计的getCache方法应该如下:

    public <T> T getCache(Class clazz)
    

    当然也可以用统一的接口来替换上述设计,该接口的功能只有标记该对象可以被缓存,但是依旧避免不了在代码某处发生类型转换,所以暂时还是采用泛型的做法。

    多种类型的数据加载器

    可以确定的是,不同类型的缓存对象,查询数据库的方式各不相同,同时利用已知信息context的形式也各不相同,但是每次取缓存的动作是统一的,因此我们建立一个数据加载的接口:

    public interface DataLoad {
        <T> T load(Context context);
    }
    

    然后用不同类型的数据加载器去完成查询数据库的动作,如加载用户数据:

    public <T> T load(Context context) {
        // 查到用户的ID
        String userId = context.getUserId();
        // 根据用户ID取到用户信息
        UserMessage userMessage = userMessageDao.query(userId);
        return userMessage
    }
    

    设置缓存

    用数据加载器加载完之后,将数据返回,然后就可以将该数据缓存起来,准备下一次取用:

    public <T> T getCache(Class clazz) {
        // 类型作为key
        String key = clazz.toString();
        if (cacheMap.get(key) == null) {
            // 根据不同类型取到数据加载器
            DataLoad dataLoad = dataLoads.get(clazz);
            // 加载数据并返回
            T value = dataLoad.load(context);
            // 设置到缓存中
            cacheMap.put(key, value);
            return value;
        }
        return (T) cacheMap.get(key);
    }
    

    上面的dataLoads是一个可以根据类型取数据加载器的map,在初始化时填充这个集合的值,可以利用spring自动找到所有实现了DataLoad的类:

    // 找到所有实现DataLoad的类
    Map<String, DataLoad> nameToDataLoad = SpringContext.getApplicationContext().getBeansOfType(DataLoad.class);
    // 填充dataLoads
    for (DataLoad dataLoad : nameToDataLoad.values()) {
         dataLoads.put(dataLoad.getClass(), dataLoad);
    }
    

    而实现缓存的map就是一个简单的HashMap:

    private Map<String, Object> cacheMap = new HashMap<>();
    
  • 相关阅读:
    朋友你的HTML标签语义化了吗?
    左岸的一篇文章关于早起的:早起的鸟儿有虫吃!
    方法比知识重要
    软件项目经理素质能力的必备要求
    老早以前收藏的一些专业技能
    浅谈如何衡量SEO工作成效
    8.29几个腾讯微博邀请链接
    又是一篇很老的文章:三五个人十来条枪如何走出软件作坊成为开发正规军
    收藏的零碎东西
    拆掉思维里的墙摘抄
  • 原文地址:https://www.cnblogs.com/yinyunmoyi/p/14292831.html
Copyright © 2020-2023  润新知