以前写过一篇博客《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》对Guava的缓存机制进一步进行了便利化封装。已经在我的项目中被广泛使用。
guava的LoadingCache在调用时要求只能有一个输入参数,算计后返回一个输出结果。
但是在实际应用中计算参数往不止一个,对于两个或三个输入参数的时候还可以用Pair,Triple
之类的对象封装,但更多参数就不好搞了。
为了解决多参数计算结果缓存问题,最好能用对象数组(Object[])来做为Key,这样就可以广泛支持任意类型和数量参数的计算缓存。
但是我们知道一般情况下,数组是不能直接做为Map的KEY的,而guava的缓存机制正是基于Map实现的。
关于这个问题也有了解决方法,参见我更久以前写的博客《guava:Cache中使用数组(Object[],int[]...)作为KEY》
简单说在构建guava的com.google.common.cache.Cache
实例时允许通过自己实现的com.google.common.base.Equivalence
实例来指定如何对KEY进行比较,
参见
com.google.common.cache.CacheBuilder.keyEquivalence(Equivalence<Object> equivalence)
方法
基于上面的技术准备,我更新了FunctionCached
类的实现。增加内置类FunctionCached.Builder
来实现FunctionCached
实例的灵活构造,使用keyDeepEqual()
方法指定对KEY执行深度比较
cpp
/**
* 对K执行比较时执行深度比较,<p>
* 对于K为数组类型时,必须指定深度比较
* @see CacheBuilder#keyEquivalence(Equivalence)
*/
public Builder<K, V> keyDeepEqual() {
ObjectDeepEquals.keyDeepEqual(cacheBuilder);
return this;
}
FunctionCached.java
java
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.ObjectDeepEquals;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.Weigher;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
/**
* 基于GUAVA {@link LoadingCache}实现类型转转换接口{@link Function},
* 当一个输入K的计算结果已经存在于缓存时,直接返回,无需再次计算,
* 避免对同一个输入数据多次重复计算。
* 适用于输入值与结果恒定对应的场景
* @author guyadong
*
* @param <K> 输入参数类型
* @param <V> 输出参数类型
*/
public class FunctionCached<K,V> implements Function<K, V> {
/**
* 输入值与计算结果的映射,计算结果可能是正常计算出的V值,也可能是异常(Exception)
*/
protected final LoadingCache<K, Object> cache;
protected final Function<K, V> getterFunction;
protected final V defaultValue;
/**
* 构造方法
* @param getterFunction 原类型转换接口实现
* @param defaultValue K为{@code null}时返回的默认值
* @param cacheBuilder 外部提供的CacheBuilder实例,为{@code null}则忽略
* @since 2.7.9
*/
public FunctionCached(final Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
this.getterFunction = checkNotNull(getterFunction,"getterFunction is null");
/** 输入参数不可以是缓存实例 */
checkArgument(!(getterFunction instanceof FunctionCached),"getterFunction must not be instance of %s",getClass().getName());
this.defaultValue = defaultValue;
if(null == cacheBuilder ) {
cacheBuilder= CacheBuilder.newBuilder();
}
this.cache = cacheBuilder.build(new CacheLoader<K, Object>(){
@Override
public Object load(K key) {
try {
return asCached(getterFunction.apply(key));
} catch (Exception e) {
return e;
}
}});
}
/**
* 构造方法
* @param getterFunction 原类型转换接口实现
* @param defaultValue K为{@code null}时返回的默认值
*/
public FunctionCached(final Function<K, V> getterFunction,V defaultValue){
this(getterFunction,defaultValue,null);
}
/**
* 构造方法,
* K 为{@code null}时返回{@code null}
* @param getterFunction 原类型转换接口实现
*/
public FunctionCached(Function<K, V> getterFunction){
this(getterFunction, null);
}
/**
* 将 {@link LoadingCache}的值存储类型转为Value
* @param cached
*/
@SuppressWarnings("unchecked")
protected V asValue(Object cached ){
return (V) cached;
}
/**
* 将 value转为 {@link LoadingCache}的值存储类型
* @param value
*/
protected Object asCached(V value ){
return value;
}
/**
* 非缓存调用
* @param key
* @return {@code key}为{@code null}返回{@link #defaultValue}
*/
public V getUncached(K key){
return null == key ? defaultValue: asValue(getterFunction.apply(key));
}
/**
* 缓存调用
* @param key
* @return {@code key}为{@code null}返回{@link #defaultValue}
*/
public V get(K key){
if(null != key){
Object value = cache.getUnchecked(key);
if(value instanceof Exception) {
Throwables.throwIfUnchecked((Exception)value);
throw new RuntimeException((Exception)value);
}
return asValue(value);
}
return defaultValue;
}
/**
* 缓存调用
* @see #get(Object)
* @see com.google.common.base.Function#apply(java.lang.Object)
*/
@Override
public V apply(K input) {
return get(input);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例,
* 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
* @param getterFunction
* @param defaultValue
* @param cacheBuilder 外部提供的CacheBuilder实例,为{@code null}则忽略
* @see #FunctionCached(Function, Object, CacheBuilder)
* @since 2.7.9
*/
public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
if(getterFunction instanceof FunctionCached){
return (FunctionCached<K, V>) getterFunction;
}
return new FunctionCached<K, V>(getterFunction, defaultValue,cacheBuilder);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例,
* 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
* @param getterFunction
* @param defaultValue
* @see #of(Function, Object, CacheBuilder)
*/
public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction,V defaultValue){
return of(getterFunction, defaultValue,null);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例(默认实例为{@code null})
* @param getterFunction
* @see #of(Function, Object)
*/
public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction){
return of(getterFunction,null);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例,
* 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
* @param getterFunction 计算函数对象,允许返回{@code null}
* @param defaultValue
* @param cacheBuilder 外部提供的CacheBuilder实例,为{@code null}则忽略
* @see #of(Function, Object, CacheBuilder)
*/
public static <K, V> FunctionCached<K, Optional<V>> nullableOf(final Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
checkNotNull(getterFunction,"getterFunction is null");
return of(new Function<K, Optional<V>>(){
@Override
public Optional<V> apply(K input) {
return Optional.fromNullable(getterFunction.apply(input));
}
}, Optional.fromNullable(defaultValue),cacheBuilder);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例,
* 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
* @param getterFunction 计算函数对象,允许返回{@code null}
* @param defaultValue
* @see #nullableOf(Function, Object, CacheBuilder)
*/
public static <K, V> FunctionCached<K, Optional<V>> nullableOf(Function<K, V> getterFunction,V defaultValue){
return nullableOf(getterFunction,defaultValue,null);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例(默认实例为{@code null})
* @param getterFunction 计算函数对象,允许返回{@code null}
* @see #nullableOf(Function, Object, CacheBuilder)
*/
public static <K, V> FunctionCached<K, Optional<V>> nullableOf(Function<K, V> getterFunction){
return nullableOf(getterFunction,null,null);
}
public static<K, V>Builder<K, V> builder(){
return new Builder<>();
}
public static class Builder<K, V>{
private CacheBuilder<Object, Object> cacheBuilder;
private Function<K, V> getterFunction;
private V defaultValue;
private boolean nullable;
protected Builder() {
this.cacheBuilder = CacheBuilder.newBuilder();
}
protected Builder(CacheBuilder<Object, Object> cacheBuilder, Function<K, V> getterFunction, V defaultValue,
boolean nullable) {
this.cacheBuilder = cacheBuilder;
this.getterFunction = getterFunction;
this.defaultValue = defaultValue;
this.nullable = nullable;
}
public <V1> Builder<Object[],V1> invokeFunction( MethodInvokeFunction<V1> invokeFunction){
return new Builder<Object[],V1>().keyDeepEqual().getterFunction(invokeFunction);
}
@SuppressWarnings("unchecked")
public<K1, V1> Builder<K1, V1> getterFunction( Function<K1, V1> getterFunction){
return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),(V1)defaultValue,this.nullable);
}
public <K1, V1> Builder<K1, V1> getterFunction( Function<K1, V1> getterFunction,V1 defaultValue){
return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),defaultValue,this.nullable);
}
/**
* 指定当{@link #getterFunction}返回值为{@code null}或{@link FunctionCached#get(Object)}参数为{@code null}时返回的默认值,
* 默认为{@code null}
* @param defaultValue
*/
public Builder<K, V> defaultValue(V defaultValue) {
this.defaultValue = defaultValue;
return this;
}
/**
* 对K执行比较时执行深度比较,<p>
* 对于K为数组类型时,必须指定深度比较
* @see CacheBuilder#keyEquivalence(Equivalence)
*/
public Builder<K, V> keyDeepEqual() {
ObjectDeepEquals.keyDeepEqual(cacheBuilder);
return this;
}
/**
* 允许 {@link #getterFunction}返回值为{@code null}
*/
public Builder<K, V> nullable() {
nullable = true;
return this;
}
/**
* @see CacheBuilder#initialCapacity(int)
*/
public Builder<K, V> initialCapacity(int initialCapacity) {
cacheBuilder.initialCapacity(initialCapacity);
return this;
}
/**
* @see CacheBuilder#concurrencyLevel(int)
*/
public Builder<K, V> concurrencyLevel(int concurrencyLevel) {
cacheBuilder.concurrencyLevel(concurrencyLevel);
return this;
}
/**
* @see CacheBuilder#maximumSize(long)
*/
public Builder<K, V> maximumSize(long size) {
cacheBuilder.maximumSize(size);
return this;
}
/**
* @see CacheBuilder#maximumWeight(long)
*/
public Builder<K, V> maximumWeight(long weight) {
cacheBuilder.maximumWeight(weight);
return this;
}
/**
* @see CacheBuilder#weigher(Weigher)
*/
public <K1, V1> Builder<K, V> weigher(Weigher<? super K1, ? super V1> weigher) {
cacheBuilder.weigher(weigher);
return this;
}
/**
* @see CacheBuilder#weakValues()
*/
public Builder<K, V> weakValues() {
cacheBuilder.weakValues();
return this;
}
/**
* @see CacheBuilder#softValues()
*/
public Builder<K, V> softValues() {
cacheBuilder.softValues();
return this;
}
/**
* @see CacheBuilder#expireAfterWrite(long, TimeUnit)
*/
public Builder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
cacheBuilder.expireAfterWrite(duration, unit);
return this;
}
/**
* @see CacheBuilder#expireAfterAccess(long, TimeUnit)
*/
public Builder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
cacheBuilder.expireAfterAccess(duration, unit);
return this;
}
/**
* @see CacheBuilder#refreshAfterWrite(long, TimeUnit)
*/
public Builder<K, V> refreshAfterWrite(long duration, TimeUnit unit) {
cacheBuilder.refreshAfterWrite(duration, unit);
return this;
}
/**
* @see CacheBuilder#ticker(Ticker)
*/
public Builder<K, V> ticker(Ticker ticker) {
cacheBuilder.ticker(ticker);
return this;
}
/**
* @see CacheBuilder#removalListener(RemovalListener)
*/
public Builder<K, V> removalListener(RemovalListener<? super K, ? super V> listener) {
cacheBuilder.removalListener(listener);
return this;
}
/**
* @see CacheBuilder#recordStats()
*/
public Builder<K, V> recordStats() {
cacheBuilder.recordStats();
return this;
}
public FunctionCached<K,V> build() {
if(nullable) {
return new FunctionCached<K,V>(getterFunction, defaultValue, cacheBuilder) {
final Optional<V> defOpt = Optional.fromNullable(defaultValue);
@SuppressWarnings("unchecked")
@Override
protected V asValue(Object cached) {
return ((Optional<V>)cached).or(defOpt).orNull();
}
@Override
protected Object asCached(V value) {
return Optional.fromNullable(value);
}
};
}
return new FunctionCached<>(getterFunction, defaultValue, cacheBuilder);
}
}
public static class MethodInvokeFunction<T> implements Function<Object[], T>{
private final Method method;
private final Object target;
public MethodInvokeFunction(Object target, Method method) {
this.method = checkNotNull(method,"method is null") ;
if(Modifier.isStatic(method.getModifiers())) {
this.target = target;
}else {
this.target = checkNotNull(target,"target is null");
}
}
public MethodInvokeFunction(Method method) {
this(null,method);
}
public MethodInvokeFunction(Object target, Class<?>clazz,String name,Class<?>...parameterTypes) {
this(target,methodOf(clazz,name,null, parameterTypes));
}
public MethodInvokeFunction(Class<?>clazz,String name,Class<?>...parameterTypes) {
this(null,methodOf(clazz,name,true, parameterTypes));
}
public MethodInvokeFunction(Object target, String name,Class<?>...parameterTypes) {
this(target,methodOf(checkNotNull(target,"target is null").getClass(),name,false, parameterTypes));
}
@SuppressWarnings("unchecked")
@Override
public T apply(Object[] input) {
try {
if(!method.isAccessible()) {
method.setAccessible(true);
}
return (T) method.invoke(target, input);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
private static Method methodOf(Class<?>clazz, String name,Boolean staticRequired, Class<?>...parameterTypes) {
try {
checkArgument(null!=parameterTypes && parameterTypes.length>0,"INVALID parameterTypes.length,>0 required");
Method method = checkNotNull(clazz,"clazz is null").getMethod(checkNotNull(name,"name is null"), parameterTypes );
if(Boolean.TRUE == staticRequired) {
checkArgument(Modifier.isStatic(method.getModifiers()),"%s is not static method",method.toString());
}else if(Boolean.FALSE == staticRequired) {
checkArgument(!Modifier.isStatic(method.getModifiers()),"%s is static method",method.toString());
}
return method;
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
}
}
调用示例
java
private static final FunctionCached<Object[], LinkedHashSet<Class<?>>> FIND_CLASSES_CACHED =
/** builder模式构建 FunctionCached实例*/
FunctionCached.builder()
/**
* 值存储指定弱引用(WeakReference)模式
* 参见上一篇博客:
* 《guava:LoadingCache缓存机制支持弱引用(WeakReference)》
* https://blog.csdn.net/10km/article/details/139060089
*/
.weakValues()
/**
* Key(Object[])执行深度比较
* 参见博客:
* 《guava:Cache中使用数组(Object[],int[]...)作为KEY》
* https://blog.csdn.net/10km/article/details/103072235
*/
.keyDeepEqual()
.getterFunction(new Function<Object[], LinkedHashSet<Class<?>>>() {
@SuppressWarnings("unchecked")
@Override
public LinkedHashSet<Class<?>> apply(Object[] input) {
/** 调用方法执行多参数计算 */
return findClasses0((Iterable<Class<?>>)input[0],
(Supplier<RangeFilter>)input[1],
(Iterable<Predicate<Class<?>>>)input[2]);
}
}).build();
private static LinkedHashSet<Class<?>> findClasses0(Iterable<Class<?>> classes,
Supplier<RangeFilter> finisher, Iterable<Predicate<Class<?>>> filters){
// 计算实现代码
}
参考资料
《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》
《guava:Cache中使用数组(Object[],int[]...)作为KEY》
《guava:LoadingCache缓存机制支持弱引用(WeakReference)》