深入理解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接口是为了保证此代码优先于其他切面执行。

时间: 2024-12-24 19:54:24

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

Redis实战(八)Redis的持久化

Redis相比Memcached的很大一个优势是支持数据的持久化, 通常持久化的场景一个是做数据库使用,另一个是Redis在做缓存服务器时,防止缓存失效. Redis的持久化主要有快照Snapshotting和AOF日志文件两种方式. 前者会根据配置的规则定时将内存中的数据持久化到硬盘上, 后者则是在每次执行写命令之后将命令记录下来. >>RDB方式 Redis是会以快照的形式将数据持久化到磁盘上. 默认会将快照文件存储在Redis当前进程的工作目录的dump.rdb文件中, 可以通过配置文件

Redis初学及与Spring集成小结

Redis初学及与Spring集成小结 1.redis是干什么的? redis是一种key-value内存数据库.在技术日新月异的发展环境下,客户的访问需求也在逐渐增长,物理数据库的压力也越来越大,由此redis也应运而生. redis常用的方式是将redis作为缓存数据库,减小物理数据库的访问压力: 2.redis常用数据结构: a.strings: set key value;设置key-value值 get key;获取key的value值 redis...method(redis中的一些

Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis

Redis真是好,其中的键值用起来真心强大啊有木有, 之前的文章讲过搭建了redis集群 那么咋们该如何调用单机版的redis以及集群版的redis来使用缓存服务呢? 先讲讲单机版的,单机版redis安装非常简单,不多说了,直接使用命令: 1 [[email protected] bin]# ./redis-server redis.conf 启动就行 在sprig文件中配置如下 1 <!-- 2 TODO: 3 开发环境使用单机版 4 生产环境务必切换成集群 5 --> 6 <!--

Spring系列之谈谈对Spring IOC的理解

学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IOC .DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解. 一.分享Iteye的开涛对Ioc的精彩讲解首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文 1.1.IoC是什么 Ioc—Inversi

深入探索spring技术内幕(八): Spring +JDBC组合开发和事务控制

一. 配置数据源 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/> <property name="

个人理解去搭建SSH三大框架spring管理配置文件(初学第一次接触SSH)

<bean id="dataSuorces" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.OracleDirver"/>(oracle数据库) <property name="url"

Spring(八)编码剖析@Resource注解的实现原理

beans2.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframew

Spring.Net学习笔记(八)-设置配置文件参数

一.开发环境 VS2013 .netframework4.5 spring.net1.3.1 二.项目结构 三.开发过程 1.编写Person类 1 namespace SpringNetConfigArg 2 { 3 public class Person 4 { 5 public string UserName { get; set; } 6 } 7 } 2.配置App.config文件 1 <?xml version="1.0" encoding="utf-8&q

关于快速创建一个spring-boot项目的操作,简单的spring运行方式的总结,spring注解的简单理解。

作为一个开发者我们都是通过开发工具进行创建工程通常我们都是采用(如:eclipse.intellij idea)来快速生成项目结构)但是sprig-boot项目我们不需要依赖开发工具进行 我们可以通过spring提供的便捷途径进行创建项目: 下面是sprig-boot项目快速创建的地址: 地址:http://start.spring.io/ 进入这个地址之后我们可以看到相关的按钮:图形化的界面多点点就会知道什么意思了 如上上面图中的相关的设置.自己可以进行相关的设置.最后会生成一个zip 包.然