SpringBoot使用Cacheable缓存引用类型取出为null
SpringBoot整合redis引用类型取出为null
原因 整合mybatis的时候配置了懒加载.注释掉就好了
后文给出了一个简单的Cacheable 的实现,但是没有写完.有兴趣的可以试试
其实有些东西出问题了还是把底层的源码替换掉比较好.无论这是一款多么强大的框架
总览 : Redis缓存
这几天一直在写项目,本来不想这么快的,想等课设写个分布式练练手
我负责了一个模块然后想使用@Cacheable注解使用Redis缓存数据
我觉得自带的JDK序列化器比较慢 所以想换一下protostuff序列化器
但是我觉得应该先完成功能再去实现,结果在一开始使用的时候就出现了问题
我就去网上找答案,但是我发现谁都没有和我一样的.
然后我手动用RedisTemplate试了一下是可以的
但是我觉得RedisTemplate不太好用,我就想用装饰者模式增强一下
然后发现整合protostuff有点问题
而且Service还要手写,以后换了还需要重新编写代码
然后我就想到了AOP, 我想实现一个Cacheable 然后使用RedisTemplate
首先是实现了一个注解
我把所有的代码也放出来, 因为后面有个东西没有去写完
代码
package com.in.market.commons.rediscache;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisCacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
boolean isCache() default true;
Class type();
RedisMethodEnum method();
}
有很多都是复制Cacheable 加了一些东西 比如type(要序列化的类型) 还有自己写的一个枚举
AOP类
AOP类是没有实现完的
而且里面还有一些测试的东西也没有删除, 不过大致的逻辑是完了
package com.in.market.commons.rediscache;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Aspect
@Component
public class RedisCacheAspect {
private static class CacheHolder {
private final static Map<String, RuntimeSchema> cacheMap = new ConcurrentHashMap<>();
//缓存保存序列化工具
}
//原生的RedisTemplate
@Autowired
private RedisTemplate redisTemplate = null;
@Pointcut("execution(public * com.in.market.service.*.*(..))") //切入点
public void point(){}
//环绕通知
@Around("point()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Exception {
Object[] args = proceedingJoinPoint.getArgs();//获取参数
Class<?>[] argTypes = new Class[args.length];//获取参数类型
for(int i = 0; i < args.length; i++) {
argTypes[i] = args[i].getClass();
}
//获取目标方法
Method method = proceedingJoinPoint
.getTarget().getClass().getMethod(proceedingJoinPoint.getSignature().getName(), argTypes);
//获取参数的名字
DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
String[] arr = discover.getParameterNames(method);
RedisCacheable annotation = method.getAnnotation(RedisCacheable.class);//获取这个注解
String key = annotation.key();
argeForKey(key, arr, args);//这里没有写完 因为想要处理一下保存的Redis保存的动态保存的key
//例如原本是 key="'cache_cart_' + #id" 我也想做到这个效果没写完我就发现了BUG
if(annotation != null) {//如果这个注解不是空
if(RedisMethodEnum.GETANDFIND.equals(annotation.method())) {//这里只模拟了Cacheable的功能
Object object = findObjectForCache(annotation); //先去缓存里查找
if(object == null) {//如果是空的话
object = proceed(proceedingJoinPoint);//从数据库里拿
saveObjectForCache(annotation,object);//保存这个对象
}
return object;
}
}
//否则的话也就是没有标注缓存注解 直接返回
return proceed(proceedingJoinPoint);
}
private String argeForKey(String key,String[] argsName, Object[] args) {
String[] keys = key.split("#");
StringBuilder stringBuilder = new StringBuilder(keys[0]);
System.out.println("这里进来了 " + key + " " + keys.length);
if(keys.length == 2) {
getKeyArge(keys[1],argsName,args);
}
return null;
}
//这里可能写的一些错误, 就是想动态获取key的值
private String getKeyArge(String key,String[] argsName, Object[] args) {
int n = argsName.length;
if(key.contains(".")) {
System.out.println("包含." + key);
String[] s = key.split("\\.");
System.out.println(s[0]);
for(int i = 0; i < n; i++) {
if(s[0].equals(argsName[i])) {
try {
Field field = args[i].getClass().getField(s[1]);
System.out.println(field.get(args[i]));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
break;
}
}
} else {
System.out.println("不包含." + " " + n);
for(int i = 0; i < n; i++) {
System.out.println(key + " ---- " + argsName[i]);
if(key.equals(argsName[i])) {
System.out.println(args[i]);
}
}
}
return null;
}
//序列化对象
private <T> void saveObjectForCache(RedisCacheable annotation,T t) {
Class clazz = annotation.type();
RuntimeSchema runtimeSchema = getCacheRuntimeSchema(clazz.getName(), clazz);
String[] cacheNames = annotation.cacheNames();
String key = annotation.key();
byte[] bytes = ProtostuffIOUtil.toByteArray(t, runtimeSchema,
LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
for(String cacheName : cacheNames) {
redisTemplate.opsForHash().put(cacheName,key,bytes);
}
}
//原方法 没有很好的处理异常
private Object proceed(ProceedingJoinPoint proceedingJoinPoint) {
try {
return proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
//从缓存中查找
private Object findObjectForCache(RedisCacheable redisCacheable) {
String[] cacheNames = redisCacheable.cacheNames();
String key = redisCacheable.key();
for(String cacheName : cacheNames) {
if (redisTemplate.opsForHash().hasKey(cacheName, key)) {
RuntimeSchema runtimeSchema = getCacheRuntimeSchema(redisCacheable.type().getName(), redisCacheable.type());
Object object = runtimeSchema.newMessage();
System.out.println(123123123);
byte[] bytes = (byte[]) redisTemplate.opsForHash().get(cacheName, key);
ProtostuffIOUtil.mergeFrom(bytes, object, runtimeSchema);
System.out.println("返回缓存" + object);
return object;
}
}
System.out.println("返回null");
return null;
}
//得到序列化要用的工具
private <T> RuntimeSchema getCacheRuntimeSchema(String key, Class<T> clazz) {
if(!CacheHolder.cacheMap.containsKey(key)) initRuntimeSchema(key,clazz);
return CacheHolder.cacheMap.get(key);
}
private <T> void initRuntimeSchema(String key, Class<T> clazz) {
CacheHolder.cacheMap.put(key, RuntimeSchema.createFrom(clazz));
}
}
部分功能没有实现, 而且我觉得也不够优化.
想实现@Cacheable 注解简单的功能可以复制过去试试