使用AOP 实现Redis缓存注解,支持SPEL(转)

来源:互联网 发布:门窗cad制图软件 编辑:程序博客网 时间:2024/06/03 17:25

源文链接:http://www.cnblogs.com/DajiangDev/p/3770894.html

原来想自己实现一个,看到网上有一个和自己思路一样的实现,经试验,完美运行,感谢原作者的分享精神。

1.applicationContext.xml,配置JedisPool

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">        <property name="maxTotal" value="50" />        <property name="maxIdle" value="10" />        <property name="maxWaitMillis" value="1000" />        <property name="testOnBorrow" value="true" />    </bean>    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">        <constructor-arg index="0" ref="jedisPoolConfig" />        <constructor-arg index="1" value="127.0.0.1" />        <constructor-arg index="2" value="6379" />    </bean>

2.Redis的封装类,使用FastJSON进行JSON和Object的转化,这里只用到了hset,hget,hdel,其他省略了

@Component    public class RedisCacheBean {        @Resource        JedisPool jedisPool;        /**         * 把对象放入Hash中         */        public void hset(String key,String field,Object o){            Jedis jedis =jedisPool.getResource();            jedis.hset(key,field, JsonUtil.toJSONString(o));            jedisPool.returnResource(jedis);        }        /**         * 从Hash中获取对象         */        public String hget(String key,String field){            Jedis jedis =jedisPool.getResource();            String text=jedis.hget(key,field);            jedisPool.returnResource(jedis);            return text;        }        /**         * 从Hash中获取对象,转换成制定类型         */        public <T> T hget(String key,String field,Class<T> clazz){            String text=hget(key, field);            T result=JsonUtil.parseObject(text, clazz);            return result;        }        /**         * 从Hash中删除对象         */        public void hdel(String key,String ... field){            Jedis jedis =jedisPool.getResource();            Object result=jedis.hdel(key,field);            jedisPool.returnResource(jedis);        }            }


3.创建注解,其实大部分数据都是以hash形式存储的(使的key易于管理),所以,注解中定义了fieldKey,用作Hash的field。

/**     * 缓存注解     * @author liudajiang     *     */    @Target({ElementType.METHOD})    @Retention(RetentionPolicy.RUNTIME)    public @interface Cacheable {        String key();        String fieldKey() ;        int expireTime() default 3600;    }

4.定义切面,定义PointCut 表达式为注解

@Component    @Aspect    public class CacheAspect {        @Resource RedisCacheBean redis;                /**                  * 定义缓存逻辑                            */        @Around("@annotation(org.myshop.cache.annotation.Cacheable)")        public Object cache(ProceedingJoinPoint pjp ) {            Object result=null;            Boolean cacheEnable=SystemConfig.getInstance().getCacheEnabled();            //判断是否开启缓存            if(!cacheEnable){                try {                    result= pjp.proceed();                } catch (Throwable e) {                    e.printStackTrace();                }                return result;            }                        Method method=getMethod(pjp);            Cacheable cacheable=method.getAnnotation(org.myshop.cache.annotation.Cacheable.class);                        String fieldKey =parseKey(cacheable.fieldKey(),method,pjp.getArgs());                        //获取方法的返回类型,让缓存可以返回正确的类型            Class returnType=((MethodSignature)pjp.getSignature()).getReturnType();                        //使用redis 的hash进行存取,易于管理            result= redis.hget(cacheable.key(), fieldKey,returnType);                        if(result==null){                try {                    result=pjp.proceed();                    Assert.notNull(fieldKey);                    redis.hset(cacheable.key(),fieldKey, result);                } catch (Throwable e) {                    e.printStackTrace();                }            }            return result;        }        /**          * 定义清除缓存逻辑          */        @Around(value="@annotation(org.myshop.cache.annotation.CacheEvict)")        public Object evict(ProceedingJoinPoint pjp ){            //和cache类似,使用Jedis.hdel()删除缓存即可...        }                /**         *  获取被拦截方法对象         *           *  MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象         *    而缓存的注解在实现类的方法上         *  所以应该使用反射获取当前对象的方法对象         */        public Method getMethod(ProceedingJoinPoint pjp){            //获取参数的类型            Object [] args=pjp.getArgs();            Class [] argTypes=new Class[pjp.getArgs().length];            for(int i=0;i<args.length;i++){                argTypes[i]=args[i].getClass();            }            Method method=null;            try {                method=pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(),argTypes);            } catch (NoSuchMethodException e) {                e.printStackTrace();            } catch (SecurityException e) {                e.printStackTrace();            }            return method;                    }        /**         *    获取缓存的key          *    key 定义在注解上,支持SPEL表达式         * @param pjp         * @return         */        private String parseKey(String key,Method method,Object [] args){                                    //获取被拦截方法参数名列表(使用Spring支持类库)            LocalVariableTableParameterNameDiscoverer u =                   new LocalVariableTableParameterNameDiscoverer();              String [] paraNameArr=u.getParameterNames(method);                        //使用SPEL进行key的解析            ExpressionParser parser = new SpelExpressionParser();             //SPEL上下文            StandardEvaluationContext context = new StandardEvaluationContext();            //把方法参数放入SPEL上下文中            for(int i=0;i<paraNameArr.length;i++){                context.setVariable(paraNameArr[i], args[i]);            }            return parser.parseExpression(key).getValue(context,String.class);        }    }

5、使用

    @Transactional    @Cacheable(key="getAdminByName",fieldKey="#name")    public Admin getByName(String name) {        return adminDao.getByUsername(name);    }    @Transactional    @CacheEvict(key="getAdminByName",fieldKey="#admin.username")    public void update(Admin admin){        adminDao.update(admin);    }


效果:






0 0
原创粉丝点击