我正在尝试使用返回嵌套类型缓存的方法实现通用缓存工厂。 我面临着在工厂中配置缓存的问题,具体取决于用于获取缓存实例的泛型类型参数。
我认为我的模型不适合处理工厂中的特定配置,但我想在一个地方管理我的不同缓存。你有什么建议这样做的?
我的实施:
public class GuavaCacheFactory {
public static final String STRING_CACHE = "STRING_CACHE";
public static final String SERVICES_LIST_CACHE = "SERVICES_LIST_CACHE";
private volatile Map<String, Cache<String, ? extends Object>> cacheMap = Maps.newHashMap();
public final <I extends Object> Cache<String, I> getCache(String name) {
Cache<String, I> cache = (Cache<String, I>) cacheMap.get(name); // Unchecked cast :(
if (cache == null) {
if(STRING_CACHE.equals(name)) {
cache = CacheBuilder.newBuilder()
.maximumSize(300)
.expireAfterWrite(12, TimeUnit.HOURS)
.removalListener(new RemovalListener<String, I>() {
@Override
public void onRemoval(RemovalNotification<String, I> notification) { // I -> String here
System.out.println("Remove parameter '" + notification + "' from cache with cause " + notification.getCause()));
}
})
.build();
} else if(SERVICES_LIST_CACHE.equals(name)) {
// I need to do an unchecked cast if I want to manipulate "List<Service>" instead of I
cache = (Cache<String, I>) CacheBuilder.newBuilder()
.maximumWeight(1000L)
.weigher(new Weigher<String, List<Service>>() {
@Override
public int weigh(String key, List<Service> services) { // I -> List<Service>
return services != null ? services.size() : 0;
}
})
.expireAfterWrite(1, TimeUnit.HOURS)
.build();
/*cache = CacheBuilder.newBuilder()
.maximumWeight(1000L)
.weigher(new Weigher<String, I>() {
@Override
public int weigh(String key, I services) { // I -> List<Service> here
return services != null ? services.size() : 0; // Error -> Here I want manipulate "List"
}
})
.expireAfterWrite(1, TimeUnit.HOURS)
.build();*/
} else {
throw new IllegalStateException("Cache with name '"+ name + "' cannot be created by GuavaCacheFactory");
}
cacheMap.put(name, cache);
}
return cache;
}
// singleton
}
我需要如何使用它:
Cache<String, String> stringCache = GuavaCacheFactory.getInstance().getCache(GuavaCacheFactory.STRING_CACHE);
String value = stringCache.get("str1", ...);
// or
Cache<String, List<Service>> listCache = GuavaCacheFactory.getInstance().getCache(GuavaCacheFactory.SERVICES_LIST_CACHE);
List<Service> services = listCache.get("serv1", ...);
根据名称,我知道我想要使用什么类型。没有更好的解决方案来解决我的问题?
答案 0 :(得分:1)
您可以使用预期缓存的实际类型替换标识缓存类型的字符串常量。这适用于像String
这样的无类型类。对于服务列表,您必须定义自己的类才能使其工作:
public class ServiceList extends List<Service> { }
public class GuavaCacheFactory {
public final <I> Cache<String, I> getCache(Class<I> cacheType) {
if cacheType.equals(String.class) {
// ...
} else if cacheType.equals(ServiceList.class) {
// ...
}
}
然后像这样使用它:
GuavaCacheFactory f = GuavaCacheFactory.getInstance()
// String-Cache
Cache<String, String> stringCache = f.getCache(String.class);
String value = stringCache.get("str1", ...);
// ServiceList-Cache
Cache<String, ServiceList> listCache = f.getCache(ServiceList.class);
ServiceList services = listCache.get("serv1", ...);
答案 1 :(得分:1)
注意有趣的代码:
private volatile Map<String, Cache<String, ? extends Object>> cacheMap = Maps.newHashMap();
volatile在这里没有帮助,它只影响对字段cacheMap的修改,而不影响地图本身。查看Collections.synchronizedMap(...)或使getCache方法同步。
集中配置或上帝对象反模式:您的应用程序最终需要多少个不同的缓存?您可能有双向依赖关系:缓存用户依赖于工厂来构建缓存,缓存工厂依赖于用户来定义所需的值类型,可能需要额外的监听器等等。最后一切都取决于彼此,这不是一个分层架构。
暴露强大的缓存接口:谁在使用缓存工厂?是否允许每个人请求服务缓存并删除服务?也许服务工厂只需要服务缓存。为什么要使服务缓存成为公共和一般产品呢?
容易出错的重新配置:您可能希望将配置置于中心位置。但是,工厂内部的某些配置选项无意更改,或者缓存用户也需要更改。这是凝聚力,但代码是分开的。
因此,更好的解决方案是实例化您需要的缓存。
您还在编写更高级缓存中已有的内容。查看具有CacheManager的EHCache或cache2k或任何符合JCache的缓存实现。
对于稍微偏离主题的回答感到抱歉。