如何实现Guava缓存来存储和获取不同类型的对象?
How to implement Guava cache to store and get different types of objects?
现在我的缓存如下所示:
public class TestCache {
private LoadingCache<String, List<ObjectABC>> cache;
TestCache() {
cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(25)
.build(new CacheLoader<String, List<ObjectABC>>(
) {
@Override
public List<ObjectABC> load(String key) throws Exception {
// TODO Auto-generated method stub
return addCache(key);
}
});
}
private List<ObjectABC> addCache(String key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key), null, true);
final List<ObjectABC> configsList = new ArrayList<>();
allConfigFiles.forEach(configFile -> {
try {
configsList.add(mapper.readValue(configFile, new TypeReference<ObjectABC>() {
}));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return configsList;
}
public List<ObjectABC> getEntry(String key) {
try {
return cache.get(key);
} catch (ExecutionException e) {
throw new NonRetriableException(String.format(
"Exception occured while trying to get data from cache for the key : {} Exception: {}",
key.toString(), e));
}
}
}
在上面的代码中,当我传递 String key
(这是本地文件夹的路径)时,它获取该位置中存在的所有文件并使用 [=13] 将它们映射到 ObjectABC
=].
现在我的问题是我想要一个像
这样的通用加载缓存
LoadingCache<String, List<Object>>
.
而且我想将不同文件夹中的文件映射到不同的对象,例如将 /root/Desktop/folder1 中的文件映射到 List<ObjectABC>
并将 /root/Desktop/folder2 中的文件映射到 List<ObjectDEF>
和能够从缓存中存储和检索该信息。
如何向缓存传递用于映射的对象的信息?
您可以创建自定义 class 包裹 LoadingCache<Key<?>, Object>
像这样:
class HeterogeneousCache {
private final LoadingCache<Key<?>, Object> cache;
public <T> T get(Key<T> key) throws ExecutionException {
return key.getType().cast(cache.get(key));
}
}
@Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final Class<T> type;
}
(为简单起见,我使用了 Lombok 的 @Value 注释)
当然,这只是一个存根,您可能需要根据自己的需要对其进行调整。主要问题可能是您无法获得 Class<List<ObjectABC>>
- 您只能获得 Class<List>
。最简单的方法是将 List<ObjectABC>
包装在某种自定义类型中。更难的方法(不推荐)是使用 Guava 的 TypeToken
.
署名:此答案基于post by Frank Appel entitled How to Map Distinct Value Types Using Java Generics, which itself is based on Joshua Bloch's typesafe hetereogeneous containers from Effective Java.
编辑:完整的解决方案
因为 OP 想要 List<T>
作为结果,并且因为他需要 TypeReference<T>
的实例,所以我在 Key<T>
中用 TypeReference<T>
替换了 Class<T>
:
@Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final TypeReference<T> typeReference;
}
这是 CustomHeterogeneousCache
现在的样子:
class CustomHeterogeneousCache {
private final LoadingCache<Key<?>, List<?>> cache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.maximumSize(25)
.build(CacheLoader.from(this::computeEntry));
@SuppressWarnings("unchecked")
public <T> List<T> getEntry(Key<T> key) {
return (List<T>) cache.getUnchecked(key);
}
private <T> List<T> computeEntry(Key<T> key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key.getIdentifier()), null, true);
return allConfigFiles.stream()
.map(configFile -> {
try {
return mapper.readValue(configFile, key.getTypeReference());
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
}
由于 TypeReference
的实现没有值语义,用户必须确保每个 Key
创建一次,然后只被引用,例如:
class Keys {
public static final Key<ObjectABC> ABC = new Key<>("/root/Desktop/folder1", new TypeReference<ObjectABC>() {
});
public static final Key<ObjectDEF> DEF = new Key<>("/root/Desktop/folder2", new TypeReference<ObjectDEF>() {
});
}
现在我的缓存如下所示:
public class TestCache {
private LoadingCache<String, List<ObjectABC>> cache;
TestCache() {
cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(25)
.build(new CacheLoader<String, List<ObjectABC>>(
) {
@Override
public List<ObjectABC> load(String key) throws Exception {
// TODO Auto-generated method stub
return addCache(key);
}
});
}
private List<ObjectABC> addCache(String key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key), null, true);
final List<ObjectABC> configsList = new ArrayList<>();
allConfigFiles.forEach(configFile -> {
try {
configsList.add(mapper.readValue(configFile, new TypeReference<ObjectABC>() {
}));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return configsList;
}
public List<ObjectABC> getEntry(String key) {
try {
return cache.get(key);
} catch (ExecutionException e) {
throw new NonRetriableException(String.format(
"Exception occured while trying to get data from cache for the key : {} Exception: {}",
key.toString(), e));
}
}
}
在上面的代码中,当我传递 String key
(这是本地文件夹的路径)时,它获取该位置中存在的所有文件并使用 [=13] 将它们映射到 ObjectABC
=].
现在我的问题是我想要一个像
这样的通用加载缓存
LoadingCache<String, List<Object>>
.
而且我想将不同文件夹中的文件映射到不同的对象,例如将 /root/Desktop/folder1 中的文件映射到 List<ObjectABC>
并将 /root/Desktop/folder2 中的文件映射到 List<ObjectDEF>
和能够从缓存中存储和检索该信息。
如何向缓存传递用于映射的对象的信息?
您可以创建自定义 class 包裹 LoadingCache<Key<?>, Object>
像这样:
class HeterogeneousCache {
private final LoadingCache<Key<?>, Object> cache;
public <T> T get(Key<T> key) throws ExecutionException {
return key.getType().cast(cache.get(key));
}
}
@Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final Class<T> type;
}
(为简单起见,我使用了 Lombok 的 @Value 注释)
当然,这只是一个存根,您可能需要根据自己的需要对其进行调整。主要问题可能是您无法获得 Class<List<ObjectABC>>
- 您只能获得 Class<List>
。最简单的方法是将 List<ObjectABC>
包装在某种自定义类型中。更难的方法(不推荐)是使用 Guava 的 TypeToken
.
署名:此答案基于post by Frank Appel entitled How to Map Distinct Value Types Using Java Generics, which itself is based on Joshua Bloch's typesafe hetereogeneous containers from Effective Java.
编辑:完整的解决方案
因为 OP 想要 List<T>
作为结果,并且因为他需要 TypeReference<T>
的实例,所以我在 Key<T>
中用 TypeReference<T>
替换了 Class<T>
:
@Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final TypeReference<T> typeReference;
}
这是 CustomHeterogeneousCache
现在的样子:
class CustomHeterogeneousCache {
private final LoadingCache<Key<?>, List<?>> cache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.maximumSize(25)
.build(CacheLoader.from(this::computeEntry));
@SuppressWarnings("unchecked")
public <T> List<T> getEntry(Key<T> key) {
return (List<T>) cache.getUnchecked(key);
}
private <T> List<T> computeEntry(Key<T> key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key.getIdentifier()), null, true);
return allConfigFiles.stream()
.map(configFile -> {
try {
return mapper.readValue(configFile, key.getTypeReference());
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
}
由于 TypeReference
的实现没有值语义,用户必须确保每个 Key
创建一次,然后只被引用,例如:
class Keys {
public static final Key<ObjectABC> ABC = new Key<>("/root/Desktop/folder1", new TypeReference<ObjectABC>() {
});
public static final Key<ObjectDEF> DEF = new Key<>("/root/Desktop/folder2", new TypeReference<ObjectDEF>() {
});
}