• 深入理解Spring Redis的使用 (六)、用Spring Aop 实现注解Dao层的自动Spring Redis缓存


    摘要: 主要针对Dao层的一些数据库查询的操作,数据实时性不强,直接加入缓存。当缓存中有的时候,就使用缓存中的数据。这样的方法,最终仅仅使用一个注解实现。对于之前的hibernate二级缓存使用,比较陌生。比如是否支持Redis或者可以自己开发支持。是否支持针对部分需要加入缓存的方法配置,而不是所有的hibernate实体都加入缓存。可能我这种方法对于二级缓存来说,抛开代码差距,也是殊途同归的东西。

    这几天工作中,突然遇到了对于有些个实体类,需要被缓存起来。但是这些个实体类数目庞大, 初始化加载的话,太耗费时间。所以初步的方案就是先查缓存,缓存没有就查询数据库,查完数据库再放入缓存。同时也方便设置过期时间。

    但是针对目前的项目来说,Dao是作为独立的Maven Module,Redis也是独立的Maven Module,相互耦合的话,代码变得难以维护,结构不清晰。所以引入了注解,然后在Redis项目中,针对注解做AOP,这样的话,没有用到缓存的项目,就可以忽略这样的注解。如果用到了,可以自动加入缓存。

    注解代码:

    package com.ns.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.util.concurrent.TimeUnit;
    /**
     * 只能注解dao里面对应的get方法,传递的参数作为hashkey,返回的值作为value
     * @author Han
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Cached {
        /**
         * redis key
         * @return
         */
        String key();
        /**
         * 过期时间,默认为0即永不过期
         * @return
         */
        long timeout() default 0L;
        /**
         * 时间单位,默认为秒
         * @return
         */
        TimeUnit timeunit() default TimeUnit.SECONDS;
    
    }

    Aop切面代码

    package com.ns.redis.aop;
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import org.apache.commons.collections.MapUtils;
    import org.apache.commons.lang.ArrayUtils;
    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.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.dao.DataAccessException;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.stereotype.Component;
    import org.springframework.util.Assert;
    
    import com.ns.annotation.Cached;
    import com.ns.redis.dao.base.BaseRedisDao;
    /**
     * 对dao的getbean的缓存处理
     * @author Han
     */
    @Aspect
    @Component
    public class AutoRedisCached extends BaseRedisDao<String, Object>{
        
        /*
         * 约束任意包下的包含Dao的类的任意方法,并且被cached注解
         */
        @Pointcut("execution(* *..*Dao*.*(*,..) && @annotation(com.ns.annotation.Cached))")
        private void cacheMethod(){}
        
        @Around("cacheMethod()")
        public Object doArround(ProceedingJoinPoint pjp) throws Throwable{
            Object[] args = pjp.getArgs();
            //定义序列化器
            final RedisSerializer<String> keySerializer = getKeySerializer();
            final RedisSerializer<Object> hashValueSerializer = getHashValueSerializer();
            final RedisSerializer<Object> hashKeySerializer = getHashKeySerializer();
            
            //序列化参数,作为hashkey
            byte [] hashkeyBytesTmp = null;
            if(args.length == 1){
                hashkeyBytesTmp = hashKeySerializer.serialize(args[0]);
            }else{
                hashkeyBytesTmp = new byte[0];
                for(Object arg : args){
                    hashkeyBytesTmp = ArrayUtils.addAll(hashkeyBytesTmp, hashKeySerializer.serialize(arg));
                }
            }
            
            final byte [] hashkeyBytes = hashkeyBytesTmp;
            
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            final Cached cacheinfo = method.getAnnotation(Cached.class);
            Object obj= null;
            
            obj = execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    byte [] tmp = connection.hGet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes);
                    return hashValueSerializer.deserialize(tmp);
                }
            });
            if(obj == null){
                final Object objReturn = pjp.proceed();
                if(objReturn != null){
                    execute(new RedisCallback<Boolean>() {
                        @Override
                        public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                            return connection.hSet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes,hashValueSerializer.serialize(objReturn));
                        }
                    });
                    
                    if(cacheinfo.timeout()>0){
                        expire(cacheinfo.key(), cacheinfo.timeout(), cacheinfo.timeunit());
                    }
                }
                obj = objReturn;
            }
            //从dao获取
            return obj;
        }
    }
  • 相关阅读:
    %和format的区别
    C++ 使用 curl 进行 http 请求(GET、POST、Download)的封装
    C++ log4cplus 类库的封装
    linux top 命令
    python使用urllib2 http 下载参数的try catch
    C 小白的 thrift 环境搭建
    pandas 必背函数操作
    flask + MySQL-python 创建 webapp 应用
    python 的 virtualenv 环境搭建及 sublime 手动创建运行环境
    nginx proxy_pass指令’/’注意事项
  • 原文地址:https://www.cnblogs.com/luochengqiuse/p/4657430.html
Copyright © 2020-2023  润新知