Redis/Spring Cache

在pom.xml加入:

<!--redis-->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>${spring.redis.version}</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${redis.version}</version>
</dependency>

在applicationContext.xml中加入

<!-- 属性文件读入 -->
<context:property-placeholder location="classpath*:system.properties,classpath*:redis.properties,classpath*:db.properties"/>
<!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
<cache:annotation-driven cache-manager="cacheManager"/>
<!-- =======================================Redis======================================== -->
<!-- 配置redis池,最大空闲实例数,(创建实例时)最大等待时间,(创建实例时)是否验证 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}"/>
    <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
    <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>

<!-- redis连接配置,依次为主机ip,端口,是否使用池,(usePool=true时)redis的池配置 -->
<bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redis.host}"/>
    <property name="port" value="${redis.port}"/>
    <property name="password" value="${redis.password}"/>
    <property name="usePool" value="true"/>
    <property name="poolConfig" ref="jedisPoolConfig"/>
</bean>

<!-- redis模板配置 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisFactory"/>
    <property name="keySerializer" >
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    </property>
    //序列化
    <property name="valueSerializer" >
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    </property>
</bean>

<!-- 声明 CacheManager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
    <constructor-arg ref="redisTemplate"/>
</bean>

RedisTemplate中需要声明4种serializer,默认为JdkSerializationRedisSerializer:

  1. keySerializer :对于普通K-V操作时,key采取的序列化策略
  2. valueSerializer:value采取的序列化策略
  3. hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
  4. hashValueSerializer:hash-value的序列化策略
    无论如何,建议key/hashKey采用StringRedisSerializer。
  • JdkSerializationRedisSerializer
    用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。
    速度快,但以字节流存储总长度长,且不容易阅读。
  • StringRedisSerializer
    一般如果key-value都是string的话,使用StringRedisSerializer就可以了
  • JacksonJsonRedisSerializer
    如果需要保存对象为json的话推荐使用JacksonJsonRedisSerializer,它不仅可以将对象序列化为json字符串并保存到redis中,但需要和jackson配合一起使用。
    被序列化的对象不用实现Serializable接口,Jackson序列化的结果清晰,容易阅读,而且存储字节少,速度较快。
  • OxmSerializer
    以xml形式的字符串存储。

其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,JacksonJsonRedisSerializer与OxmSerializer都是基于stirng存储,因此它们本质上还是String(最终还是使用string解析以及构建java对象)。

配置文件redis.properties:

#redis中心
redis.host=107.170.248.158
redis.port=6379
redis.password=123
redis.maxIdle=100
redis.maxActive=300
redis.maxWaitMillis=1000
redis.testOnBorrow=true
redis.timeout=100000

# 不需要加入缓存的类
targetNames=
# 不需要缓存的方法
methodNames=

#设置缓存失效时间
com.service.impl.xxxRecordManager= 60
com.service.impl.xxxSetRecordManager= 60
defaultCacheExpireTime=3600

fep.local.cache.capacity =10000

Controller:
要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis。

@Autowired
private TestService testService;

@RequestMapping(value="/testredis")
public String testRedis(String key,Model model){
    String value = testService.testRedis(key).toString();
    System.out.println("get from redis key : "+key+"; value : "+value+";");
    return "test";
}

@RequestMapping(value="/testrediscaching")
public String testRedisCaching(String key,Model model){
    String value = testService.testRedisCaching(key).toString();
    System.out.println("get from redis if cache exist. key : "+key+"; value : "+value+";");
    return "test";
}

@RequestMapping(value="/testrediscachingupdate")
public String testRedisCachingUpdate(String key,Model model){
    String value = testService.testRedisCachingUpdate(key).toString();
    System.out.println("update the redis. key : "+key+"; value : "+value+";");
    return "test";
}

@RequestMapping(value="/testrediscachingdelete")
public String testRedisCachingDelete(String key,Model model){
    testService.testRedisCachingDelete(key);
    System.out.println("delete from the redis. key : "+key+";");
    return "test";
}

Service:

public interface TestService {
    Object testRedis(String s);

    Object testRedisCaching(String s);

    Object testRedisCachingEntity(String s);

    Object testRedisCachingUpdate(String s);

    Object testRedisCachingDelete(String s);
}

ServiceImpl:

import com.zzhblh.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.io.Serializable;

/**
 * Created by chen on 2016/8/25.
 */
@Service("testService")
public class TestServiceImpl implements TestService{

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Object testRedis(String key) {
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        return operations.get(key);
    }

    @Override
    @Cacheable(value="testCaching")
    public Object testRedisCaching(String key) {
        System.out.println("没有用到redis缓存");
        //从数据库取出
        Object value = "World";
        return value;
    }

    @Override
    @Cacheable(value="testCaching")
    public Object testRedisCachingEntity(String key) {
        System.out.println("没有用到redis缓存");
        //从数据库取出
        //User必须要继承Serializable接口
        User u = new User("223","abc");
        return u;
    }
    @Override
    @CachePut(value="testCaching")
    public Object testRedisCachingUpdate(String key) {
        System.out.println("更新redis缓存");
        //更新从数据库取出,放入缓存
        Object value = "New World";
        return value;
    }

    @Override
    @CacheEvict(value="testCaching")
    public Object testRedisCachingDelete(String key) {
        System.out.println("删除redis缓存");
        //删除缓存
        return null;
    }
}

上面的方法使用了Spring Cache注解:
@Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。

属性名称 描述 示例
value 缓存的名称,在spring配置文件中定义,必须指定至少一个。 @Cacheable(value=”mycache”) 或者 @Cacheable(value={"cache1","cache2"}
key 缓存的 key,可以为空,如果指定要按照SpEL表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @Cacheable(value="testcache”,key="#userName")
condition 缓存的条件,可以为空,使用SpEL编写,返回true或者false,只有为true才进行缓存 @Cacheable(value=”testcache”,condition="#userName.length()>2")

@CachEvict主要针对方法配置,能够根据一定的条件对缓存进行清空,除了@Cacheable的三种配置参数,另有:

属性名称 描述 示例
allEntries 是否清空所有缓存内容,缺省为false,如果指定为true,则方法调用后将立即清空所有缓存 @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 @CachEvict(value=”testcache”,beforeInvocation=true)

@CachePut主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法。他的配置参数和@Cacheable相同

缓存的 key 生成策略:
对于使用 @Cacheable 注解的方法,每个缓存的key生成策略默认使用的是参数名+参数值,比如以下方法:

    @Cacheable("users")  
    public User findByUsername(String username)  

这个方法的缓存将保存于key为users~keys的缓存下,对于username取值为"abc"的缓存,key为"username-abc"。

    @Cacheable("users")  
    public Integer getLoginCountByUsername(String username)  

这个方法的缓存也将保存于key为users~keys的缓存下。对于username取值为"abc"的缓存,key也为"username-abc",将前一个方法的缓存覆盖掉。

可以直接在注解中使用springEL表达式,比如以下方法:

@Cacheable(value="users", key="#userid.toString() + #username.toString()")  
public User findByUsername(String username, String userid)  

@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(value="users", key="#p0")
public User find(Integer id) {
    returnnull;
}

@Cacheable(value="users", key="#p0.id")
public User find(User user) {
    returnnull;
}
属性名称 描述 示例
methodName 当前方法名 #root.methodName
method 当前方法 #root.method.name
target 当前被调用的对象 #root.target
targetClass 当前被调用的对象的class #root.targetClass
params 调用当前方法的参数 #root.params[0]
parameter name 调用当前方法的参数 #p0
caches 当前被调用的方法使用的Cache #root.caches[0].name

或使用KeyGenerator
在配置文件中添加:

//在application.xml中
<bean id="myKeyGenerator" class="com.zzhblh.utils.MyKeyGenerator" />
<cache:annotation-driven cache-manager="cacheManager" key-generator="myKeyGenerator"/>

MyKeyGenerator类:

//KeyGenerator
import org.springframework.cache.interceptor.KeyGenerator;
@Override
public class MyKeyGenerator implements KeyGenerator {
    public Object generate(Object o, Method method, Object... objects) {  
        StringBuilder sb = new StringBuilder();  
        sb.append(o.getClass().getName());  
        sb.append(method.getName());  
        for (Object obj : objects) {  
            sb.append(obj.toString());  
        }  
        return sb.toString();  
    }
}

这时请求 /testrediscaching?key=Hello,
redis中的key为 com.zzhblh.service.impl.TestServiceImpltestRedisCachingHello,同时也会有一个叫testCaching~keys的zset存储着testCaching的所有key

但如果我们请求/testrediscachingupdate?key=Hello,
我们发现redis中新增加了一个com.zzhblh.service.impl.TestServiceImpltestRedisCachingUpdateHello的key,请求/testrediscachingdelete?key=Hello同理。虽然这是符合myKeyGenerator中key的生成规则的,但却不符合实际的需要。可以修改MyKeyGenerator

sb.append(method.getName().replaceAll("Update","")).replaceAll("Delete",""));

无论使用什么方法,满足需求即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • 作家格拉德威尔在《异类》一书中指出:“人们眼中的天才之所以卓越非凡,并非天资超人一等,而是付出了持续不断的努力。1...
    夏夏手记阅读 492评论 0 2
  • 我还好 你呢
    北港阅读 152评论 0 0
  • 咿咿呀呀 爬爬滚滚 胖嘟嘟的小手 45°朝上 抓住你 眨巴眨巴 那硕大的眼睛 似乎 诉说着 呓语着 你努力辨析 最...
    胖大达阅读 291评论 0 0