2023年11月29日发(作者:)
SpringBoot(九)Cache接⼝get函数⽆效问题
原本想基于Lettuce,⾃⼰写⼀个Redis的Cache,⾃定义Cache难度不⾼,但是在编码过程中,发现get(Object key, Class
函数从未被调⽤过,导致计划迟迟未完成。
⾃定义Cache的基本代码
package ;
import Const;
import ter;
import eption;
import ;
import ValueWrapper;
import p;
import ;
import le;
/**
*
*/
public class RedisCache implements Cache {
Mapnew HashMap<>();
/**
* 简单直⽩,就是获取Cache的名字
*/
@Override
public String getName() {
return _DEF;
}
/**
* 获取底层的缓存实现对象
*/
@Override
public Object getNativeCache() {
return _DEF;
}
/**
* 根据键获取值,把值包装在ValueWrapper⾥⾯,如果有必要可以附加额外信息
*/
@Override
public ValueWrapper get(Object key) {
n("ValueWrapper");
throw new BizException("test");
// return nsKey(key)?new SimpleValueWrapper((key)):null;
}
/**
* 从缓存中获取 key 对应的值,如果缓存没有命中,则添加缓存,
* 此时可异步地从 valueLoader 中获取对应的值(4.3版本新增)
* 与缓存标签中的sync属性有关
*/
@Override
public
try {
n("get");
return ();
} catch (Exception e) {
tackTrace();
}
return null;
}
/**
* 存放键值对
*/
@Override
public void put(Object key, Object value) {
n("put(Object key, Object value)");
(key, value);
}
/**
因此,Cache接⼝中,get(Object key, Class
(后⾯提到的get函数,均指此函数,重点分析此函数不被调⽤的原因)
@Override
public
try {
n("get(Object o, Class
return nsKey(key)?t((key), aClass):null;
} catch (Exception e) {
tackTrace();
}
return null;
}
分析
打印异常栈,分析源码,核⼼异常如下:
ionHandler(rvletRequest,.
HttpServletResponse,eption) throws ption
eption: test
at (:43)
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
public CacheInterceptor() {
}
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = hod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
return d();
} catch (Throwable var2) {
throw new ThrowableWrapper(var2);
}
};
try {
//只是负责调⽤起CacheAspectSupport,本⾝代码较少
return this.execute(aopAllianceInvoker, s(), method, uments());
} catch (ThrowableWrapper var5) {
throw ginal();
}
}
}
AbstractCacheInvoker
Cache处理器,对Cache包裹了⼀层代码,负责调⽤起Cache的相关函数,从这⾥可以看出⼀部分问题了,代码⾃始⾄终没调⽤过get函数
public abstract class AbstractCacheInvoker {
protected SingletonSupplier
protected AbstractCacheInvoker() {
this.errorHandler = (SimpleCacheErrorHandler::new);
}
protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
this.errorHandler = (errorHandler);
}
public void setErrorHandler(CacheErrorHandler errorHandler) {
this.errorHandler = (errorHandler);
}
public CacheErrorHandler getErrorHandler() {
return (CacheErrorHandler)this.();
}
@Nullable
protected ValueWrapper doGet(Cache cache, Object key) {
try {
return (key);
} catch (RuntimeException var4) {
this.getErrorHandler().handleCacheGetError(var4, cache, key);
return null;
}
}
protected void doPut(Cache cache, Object key, @Nullable Object result) {
try {
(key, result);
} catch (RuntimeException var5) {
this.getErrorHandler().handleCachePutError(var5, cache, key, result);
}
@Nullable
private Object execute(CacheOperationInvoker invoker, Method method, perationContexts contexts) {
if (hronized()) {
perationContext context =
(perationContext)(CacheableOperation.class).iterator().next();
if (this.isConditionPassing(context, _RESULT)) {
Object key = this.generateKey(context, _RESULT);
Cache cache = (Cache)hes().iterator().next();
try {
//同步调⽤,调⽤Cache有Callback的get函数
return this.wrapCacheValue(method, (key, () -> {
return this.unwrapReturnValue(this.invokeOperation(invoker));
}));
} catch (ValueRetrievalException var10) {
throw (ThrowableWrapper)se();
}
} else {
return this.invokeOperation(invoker);
}
} else {
this.processCacheEvicts((CacheEvictOperation.class), true, _RESULT);
//异步调⽤,调⽤get函数,返回ValueWrapper
ValueWrapper cacheHit = this.findCachedItem((CacheableOperation.class));
List
if (cacheHit == null) {
this.collectPutRequests((CacheableOperation.class), _RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && !this.hasCachePut(contexts)) {
//从缓存命中到Value
cacheValue = ();
//Value格式化
returnValue = this.wrapCacheValue(method, cacheValue);
} else {
//从缓存未命中到Value
returnValue = this.invokeOperation(invoker);
cacheValue = this.unwrapReturnValue(returnValue);
}
//put函数调⽤
this.collectPutRequests((CachePutOperation.class), cacheValue, cachePutRequests);
Iterator var8 = or();
while(t()) {
//put函数调⽤
utRequest cachePutRequest = (utRequest)();
(cacheValue);
}
//evict函数调⽤
this.processCacheEvicts((CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
}
其中最可疑的wrapCacheValue,作⽤是包装缓存值,合理的代码逻辑,会对返回值进⾏⼆次封装,然⽽,此处代码仅仅只是⽤Optional
进⾏了处理,并未做更有效的处理,应该是Java8下的代码调整。
@Nullable
private Object wrapCacheValue(Method method, @Nullable Object cacheValue) {
return urnType() != Optional.class || cacheValue != null && ss() == Optional.class ? cacheValue :
able(cacheValue);
}
推测
JDK⾃带的对象序列化技术,在对象转为byte[]之后,内容中已经包含了全类名,byte[]转为对象,可以⾃动转型,不需要Class作为参
数,。
Spring的Cache⼗分符合JDK的⽤法,传统的缓存EhCache,设计上就采⽤了原⽣的序列化。
JSON是Douglas Crockford在2001年开始推⼴使⽤的数据格式,在2005年-2006年正式成为主流的数据格式,
⽽Spring 框架最开始的部分是由Rod Johnson于2000年为伦敦⾦融界提供独⽴咨询业务时写出来的。
两者产⽣的时间差异,可能是这⼀现象的原因:
Spring重点对JDK原⽣代码做了⽀持,因为JDK不需要Class作为参数,随着第三⽅序列化的产⽣,尽管接⼝上已经设计好,但是并未改变
原先的做法。
序列化测试代码:
import .*;
/**
* @author
* @date 2019/12/24 14:38
*/
class A implements Serializable {
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// A a = new A();
// FileOutputStream is = new FileOutputStream("C:");
// try (ObjectOutputStream oos = new ObjectOutputStream(is)) {
// bject(a);
// }


发布评论