面向对象一: 数据加载器完成缓存
面向对象一: 数据加载器完成缓存
某微服务,它的作用是导出一系列复杂的组合信息,那么该微服务取到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<>();