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


    项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段if else的代码,导致耦合非常大,不方便后期的修改。

    思来想去,决定使用自动注解+Spring 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;
    /**
     * 
     * ---------
     * @author Han
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RedisCached {
        /**
         * redis key
         * @return
         */
        String value();
        /**
         * 过期时间,默认为1分钟,如果要设置永不过期,请设置小于等于0的值
         * @return
         */
        long timeout() default 1;
        /**
         * 时间单位,默认为分钟
         * @return
         */
        TimeUnit timeunit() default TimeUnit.MINUTES;
        /**
         * 额外定义一个空方法,调用该方法来对之前的缓存进行更新
         * @return
         */
        boolean forDelete() default false;
    }
    复制代码

    这个注解方便我们标志那个方法需要作为AOP的切入点。

    AOP实现:

    复制代码
    package com.ns.redis.aop;
    
    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;
    
    import org.apache.commons.codec.digest.DigestUtils;
    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.hibernate.metamodel.binding.Caching;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    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.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.stereotype.Component;
    
    import com.ns.annotation.RedisCached;
    import com.ns.redis.dao.base.BaseRedisDao;
    /**
     * 对dao的getbean的缓存处理
     * order保证优先执行此切面
     * @author Han
     */
    @Aspect
    public class AutoRedisCached extends BaseRedisDao<Object, Object> implements Ordered{
        private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class);
        /*
         * 约束任意包下的包含Dao的类的任意方法,并且被cached注解
         */
        @Pointcut("execution(* *..*(..)) && @annotation(com.ns.annotation.RedisCached)")
        private void cacheMethod(){}
        
        @Around("cacheMethod()")
        public Object doArround(ProceedingJoinPoint pjp) throws Throwable{
            Object[] args = pjp.getArgs();
            
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            final RedisCached cacheinfo = method.getAnnotation(RedisCached.class);
            
            //定义序列化器
            final RedisSerializer<String> keySerializer = getStringSerializer();
            final RedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(method.getReturnType());
            
            //序列化参数,作为hashkey
            byte [] keyBytesTemp = keySerializer.serialize(cacheinfo.value());
            for(Object arg : args){
                keyBytesTemp = ArrayUtils.addAll(keyBytesTemp, getDefaultSerializer().serialize(arg));
            }
            //取md5后key
            final byte [] keyBytes = keySerializer.serialize(DigestUtils.md5Hex(keyBytesTemp));
            
            //是删除方法
            if(cacheinfo.forDelete()){
                execute(new RedisCallback<Object>() {
                    @Override
                    public Object doInRedis(RedisConnection connection) throws DataAccessException {
                        return connection.del(keyBytes);
                    }
                });
                return null;
            }
            
            Object obj= null;
            log.info("方法"+method.getName()+"切面,尝试从缓存获取...");
            obj = execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    byte [] tmp = connection.get(keyBytes);
                    return valueSerializer.deserialize(tmp);
                }
            });
            if(obj == null){
                log.info("方法"+method.getName()+"切面,缓存未找到...");
                final Object objReturn = pjp.proceed();
                if(objReturn != null){
                    execute(new RedisCallback<Boolean>() {
                        @Override
                        public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                            if(cacheinfo.timeout() > 0){
                                connection.setEx(keyBytes, TimeUnit.SECONDS.convert(cacheinfo.timeout(), cacheinfo.timeunit()), valueSerializer.serialize(objReturn));
                            }else{
                                connection.set(keyBytes,valueSerializer.serialize(objReturn));
                            }
                            return true;
                        }
                    });
                }
                obj = objReturn;
            }else{
                log.info("方法"+method.getName()+"切面,缓存命中...");
            }
            //从dao获取
            return obj;
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }
    复制代码

    注:Orderd接口是为了保证此代码优先于其他切面执行

  • 相关阅读:
    使用CSS3实现超炫的Loading(加载)动画效果
    三种简洁的经典高效的DIV+CSS制作的Tab导航简析
    Span和Div的区别
    [总结]Jquery api 快速参考
    25个可遇不可求的jQuery插件
    基于单个 div 的 CSS 绘图
    一款基于jQuery的图片场景标注提示弹窗特效
    HTML5手机开发——滚动和惯性缓动
    发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser
    CSS框架BluePrint
  • 原文地址:https://www.cnblogs.com/barrywxx/p/8525878.html
Copyright © 2020-2023  润新知