guava:支持数组(Object[])为Key的缓存实现

以前写过一篇博客《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){
		// 计算实现代码
	}

完整代码参见码云仓库:https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/utils/FunctionCached.java

参考资料

《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》
《guava:Cache中使用数组(Object[],int[]...)作为KEY》
《guava:LoadingCache缓存机制支持弱引用(WeakReference)》

相关推荐
InterestingFigure19 分钟前
redis的时延监控
数据库·redis·缓存
续亮~24 分钟前
6、Redis系统-数据结构-05-整数
java·前端·数据结构·redis·算法
ningbaidexia1 小时前
java数据结构集合复习之ArrayList与顺序表
java·数据结构·windows
Ggggggtm2 小时前
Redis常用命令——Set、Zset篇
数据库·redis·缓存
程序员云翼2 小时前
7-理财平台
java·vue.js·spring boot·后端·毕设
舞者H3 小时前
源码层面学习动态代理
java·学习
焱行软件科技计算机毕设3 小时前
【java计算机毕设】线上花店销售商城系统java MySQL ssm JSP maven项目代码源码+文档ppt
java·mysql·课程设计
专注成就自我3 小时前
java使用easypoi模版导出word详细步骤
java·开发语言·word
多多*3 小时前
SpringBoot 启动流程六
java·开发语言·spring boot·后端·spring
极乐码农3 小时前
Spring学习03-[Spring容器核心技术IOC学习进阶]
java·学习·spring