Demo entry 6656330

java

   

Submitted by anonymous on Oct 31, 2017 at 03:30
Language: Java. Code size: 8.9 kB.

@Aspect
@Component
public class RedisCacheAspect {

    public static final Logger infoLog = LoggerFactory.getLogger(RedisCacheAspect.class);

    /**
     * 是否使用集群配置
     */
    boolean isUseCluster = false;

    /**
     * 是否使用redis
     */
    boolean isUseRedis  = false;

    /**
     * 自动注入jedisTemplate,这里是单缓存服务器时可以使用的对象
     */
    @Autowired @Qualifier("jedisTemplate")
    private RedisTemplate<Serializable, Object> redisTemplate;

    /**
     * 自动注入,这里是集群缓存服务器时可以使用的对象
     */
    @Autowired @Qualifier("jedisCluster")
    private JedisCluster jedisCluster;

    /**
     *  cache切入点
     */
    @Pointcut("@annotation(com.ict.web.commons.redis.anotation.RedisCache)")
    public void mRedisCache() {
    }

    /**
     * clear cache 切入点
     */
    @Pointcut("@annotation(com.ict.web.commons.redis.anotation.RedisEvict)")
    public void clearCache(){
    }

    /**
     * 方法调用前,先查询缓存。如果存在缓存,则返回缓存数据,阻止方法调用;
     * 如果没有缓存,则调用业务方法,然后将结果放到缓存中
     * @param jp
     * @return
     * @throws Throwable
     */
    @Around("mRedisCache()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {

        //加载参数
        loadProperties();

        //不使用redis的配置下,直接返回
        if(!isUseRedis){
            return jp.proceed(jp.getArgs());
        }

        // 得到类名、方法名和参数
        String clazzName = jp.getTarget().getClass().getName();
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();

        // 根据类名,方法名和参数生成key
        String key = genKey(clazzName, methodName, args);
        if (infoLog.isDebugEnabled()) {
            infoLog.debug("生成key:{}", key);
        }

        // 得到被代理的方法
        Method me = ((MethodSignature) jp.getSignature()).getMethod();

        // 得到被代理的方法上的注解
        Class modelType = me.getAnnotation(RedisCache.class).type();

        // 检查redis中是否有缓存
        Object value = null;
        try {

            //如果清除缓存线程没有在执行,则重新打开一个线程,确保缓存栈中没有剩余缓存
            if (mQueueThread == null || !mQueueThread.isAlive()) {
                mThread();
            }

            //清除队列中存在该项,需要先清除
            if(queue.contains(modelType.getName())) {

                if(isUseCluster) {
                    //集群时的删除
                    jedisCluster.del(modelType.getName());
                }else {
                    //单机时的清除
                    redisTemplate.delete(modelType.getName());
                }

                queue.remove(modelType.getName());
            }

            //确保需要清除的队列中没有该key再往下执行
            if (!queue.contains(modelType.getName())) {

                if(isUseCluster) {
                    //集群时的取缓存的方法
                    value = jedisCluster.hget(modelType.getTypeName(), key);
                }else{
                    //单机时的取缓存方法
                    value = redisTemplate.opsForHash().get(modelType.getName(), key);
                }

                //取得的时候是字符串,需要反序列化
                value = SerializeUtil.unSerialize_jdk((String) value);

            }

        }catch (Exception e){
//            e.printStackTrace();
            if (infoLog.isDebugEnabled()) {
                infoLog.debug("缓存服务器连接发生错误,需要检查网络...");
            }
            return jp.proceed(args);
        }

        // result是方法的最终返回结果
        Object result;
        if (null == value) {
            // 缓存未命中
            if (infoLog.isDebugEnabled()) {
                infoLog.debug("缓存未命中");
            }

            //调用接下去需要执行的方法,取得返回数据
            result = jp.proceed(args);

            // 序列化查询结果为JSON
            //String json = serialize(result);

            /**
             *  序列化结果放入缓存,需要加一个标签modelType,方便统一管理同一个类别的缓存
             *  这里采用的是JDK自带的序列化对象保存,因为JSON反序列复杂对象的时候速度很不理想
             */
            new Thread(new Runnable() {
                public void run() {
                    try {


                        if(isUseCluster){//集群时的缓存
                            jedisCluster.hset(modelType.getName(), key, SerializeUtil.toSerialize_jdk(result));
                        }else {//单机时的缓存
                            redisTemplate.opsForHash().put(modelType.getName(), key, SerializeUtil.toSerialize_jdk(result));
                        }

                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();

        } else {
            // 缓存命中
            if (infoLog.isDebugEnabled()) {
                infoLog.debug("缓存命中, value = {}", value);
            }

            return value;

//因为从JSON反序列化的过程时间消耗很不理想,所以此处还是使用JDK原生提供的序列化
//如果稍候能够解决JSON反序列化的问题,这里可以考虑使用,毕竟JSON的内存占用比JDK序列化对象少得多
            // 得到被代理方法的返回值类型
//            Class returnType = ((MethodSignature) jp.getSignature()).getReturnType();

            // 反序列化从缓存中拿到的json
//            result = deserialize(value, returnType, modelType);

//            if (infoLog.isDebugEnabled()) {
//                infoLog.debug("反序列化结果 = {}", result);
//            }


        }

        return result;
    }

    /**
     * 在方法调用前清除缓存,然后调用业务方法
     * @param jp
     * @return
     * @throws Throwable
     */
    @Around("clearCache()")
    public Object evictCache(ProceedingJoinPoint jp) throws Throwable {

        //加载参数
        loadProperties();

        //不使用redis的配置下,直接返回
        if(!isUseRedis){
            return jp.proceed(jp.getArgs());
        }

        // 得到被代理的方法
        Method me = ((MethodSignature) jp.getSignature()).getMethod();

        // 得到被代理的方法上的注解,这里是定义为同一个标签的数据,全部需要清除
        Class[] modelTypes = me.getAnnotation(RedisEvict.class).type();
        if(modelTypes!=null && modelTypes.length>0){
            for(Class modelType : modelTypes){
                if (infoLog.isDebugEnabled()) {
                    infoLog.debug("清空缓存:{}", modelType.getName());
                }

                // 清除对应缓存
                try {
                    if(isUseCluster) { //集群时
                        jedisCluster.del(modelType.getName());
                    }else {//单机时
                        redisTemplate.delete(modelType.getName());
                    }
                }catch (Exception e){
                    infoLog.debug("缓存服务器连接发生错误,需要检查网络...加入清缓存队列...");
                    queue.add(modelType.getName()); //发生异常的时候把它放到队列中
                }
            }
        }

        // 调用业务方法
        return jp.proceed(jp.getArgs());
    }


    private static final String DELIMITER = "-";

    /**
     * 根据类名、方法名和参数生成key
     * @param clazzName
     * @param methodName
     * @param args 方法参数
     * @return
     */
    protected String genKey(String clazzName, String methodName, Object[] args) {
        StringBuilder sb = new StringBuilder(clazzName);
        sb.append(DELIMITER);
        sb.append(methodName);
        sb.append(DELIMITER);

        for (Object obj : args) {
            if(obj!=null) {
                sb.append(obj.toString());
                sb.append(DELIMITER);
            }
        }

        return sb.toString();
    }

    /**
     *
     */
    private Thread mQueueThread;

    /**
     * 需要清除的缓存队列
     */
    private Queue<String> queue=new LinkedList<>();

    /**
     * 需要清除缓存的队列线程
     * 一些原因下,运用服务器与缓存服务器联络不通畅,导致一些该清除的缓存没有被及时清除
     * 所以当每一次读取缓存数据的时候需要先检查清除缓存的队列,确保不会读取到垃圾数据
     */
    private void mThread(){
        mQueueThread = new Thread(new Runnable() {
        @Override
        public void run() {
            while(queue.size()>0){ //如果队列中有东西
                String key = queue.peek(); //先拿
                try {
                    if(isUseCluster) {
                        jedisCluster.del(key);
                    }else {
                        redisTemplate.delete(key); //删除缓存
                    }
                    queue.remove(key); //再队列中删除
                }catch (Exception e){
                    break; //发生异常则证明此时网络还不通,直接退出,等待下次重连即可
                }
            }
        }
       });
       mQueueThread.start();
    }

    /**
     * 加载配置,获取是否使用集群参数
     */
    private void loadProperties(){

        String mStr = Global.getConfig("redis.useCluster");
        try{
            if(mStr.equals("true")){
                isUseCluster = true;
            }else{
                isUseCluster = false;
            }
        }catch (Exception e){
            isUseCluster = false;
        }

        mStr = Global.getConfig("redis.useRedis");
        try{
            if(mStr.equals("true")){
                isUseRedis = true;
            }else{
                isUseRedis = false;;
            }
        }catch (Exception e){
            isUseRedis = false;
        }

    }

}

This snippet took 0.01 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).