zoukankan      html  css  js  c++  java
  • 深入理解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;
        }
    }
  • 相关阅读:
    安全编码1
    VPP tips
    VPP概述汇总
    C语言安全编码摘录
    TCP-proxy
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.4. Matplotlib: plotting
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.3. NumPy: creating and manipulating numerical data
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.2. The Python language
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.1. Python scientific computing ecosystem
    25马5跑道,求最快的五匹马的需要比赛的次数
  • 原文地址:https://www.cnblogs.com/luochengqiuse/p/4657430.html
Copyright © 2011-2022 走看看