SpringBoot使用Cacheable缓存引用类型取出为null

SpringBoot整合redis引用类型取出为null


原因 整合mybatis的时候配置了懒加载.注释掉就好了
后文给出了一个简单的Cacheable 的实现,但是没有写完.有兴趣的可以试试
其实有些东西出问题了还是把底层的源码替换掉比较好.无论这是一款多么强大的框架


总览 : Redis缓存

这几天一直在写项目,本来不想这么快的,想等课设写个分布式练练手
我负责了一个模块然后想使用@Cacheable注解使用Redis缓存数据
我觉得自带的JDK序列化器比较慢 所以想换一下protostuff序列化器
但是我觉得应该先完成功能再去实现,结果在一开始使用的时候就出现了问题
我就去网上找答案,但是我发现谁都没有和我一样的.
然后我手动用RedisTemplate试了一下是可以的
但是我觉得RedisTemplate不太好用,我就想用装饰者模式增强一下
然后发现整合protostuff有点问题
而且Service还要手写,以后换了还需要重新编写代码
然后我就想到了AOP, 我想实现一个Cacheable 然后使用RedisTemplate
首先是实现了一个注解
我把所有的代码也放出来, 因为后面有个东西没有去写完

SpringBoot使用Cacheable缓存引用类型取出为null
代码

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(要序列化的类型) 还有自己写的一个枚举
SpringBoot使用Cacheable缓存引用类型取出为null
AOP类

SpringBoot使用Cacheable缓存引用类型取出为null

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 注解简单的功能可以复制过去试试