打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Springboot中Spring-cache与redis整合

也是在整合redis的时候偶然间发现spring-cache的。这也是一个不错的框架,与spring的事务使用类似,只要添加一些注解方法,就可以动态的去操作缓存了,减少代码的操作。如果这些注解不满足项目的需求,我们也可以参考spring-cache的实现思想,使用AOP代理+缓存操作来管理缓存的使用。
在这个例子中我使用的是redis,当然,因为spring-cache的存在,我们可以整合多样的缓存技术,例如Ecache、Mamercache等。
下面来看springcache的具体操作吧!
附上官方的文档:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

redis中整合spring-cache

我代码的工程还是接着上个项目来使用的, 所以可以结合上一篇博客来看
http://blog.csdn.net/u011521890/article/details/78070773
或者直接找github,欢迎star
https://github.com/hpulzl/book_recommend

缓存的配置如下

  • 在RedisCacheConfig上添加注解
@EnableCaching //加上这个注解是的支持缓存注解
  • 创建RedisCacheManager
 /**     * 设置RedisCacheManager     * 使用cache注解管理redis缓存     *     * @return     */    @Bean    public RedisCacheManager cacheManager() {        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());        return redisCacheManager;    }
  • 自定义缓存的key
 /**     * 自定义生成redis-key     *     * @return     */    @Override    public KeyGenerator keyGenerator() {        return new KeyGenerator() {            @Override            public Object generate(Object o, Method method, Object... objects) {                StringBuilder sb = new StringBuilder();                sb.append(o.getClass().getName()).append(".");                sb.append(method.getName()).append(".");                for (Object obj : objects) {                    sb.append(obj.toString());                }                System.out.println("keyGenerator=" + sb.toString());                return sb.toString();            }        };    }

在RedisCacheConfig中添加以上的代码,就可以使用springcache的注解了。下面介绍springcache的注解如何使用

spring cache与redis缓存结合

对springCache概念的了解

springCache支持透明的添加缓存到应用程序,类似事务处理一般,不需要复杂的代码支持。

缓存的主要使用方式包括以下两方面
1. 缓存的声明,需要根据项目需求来妥善的应用缓存
2. 缓存的配置方式,选择需要的缓存支持,例如Ecache、redis、memercache等

缓存的注解介绍

@Cacheable 触发缓存入口

@CacheEvict 触发移除缓存

@CacahePut 更新缓存

@Caching 将多种缓存操作分组

@CacheConfig 类级别的缓存注解,允许共享缓存名称

@CacheConfig

该注解是可以将缓存分类,它是类级别的注解方式。我们可以这么使用它。
这样的话,UseCacheRedisService的所有缓存注解例如@Cacheable的value值就都为user。

@CacheConfig(cacheNames = "user")@Servicepublic class UseCacheRedisService {}

在redis的缓存中显示如下

127.0.0.1:6379> keys *1) "user~keys"2) "user_1"127.0.0.1:6379> get user~keys(error) WRONGTYPE Operation against a key holding the wrong kind of value127.0.0.1:6379> type user~keyszset127.0.0.1:6379> zrange user~keys 0 101) "user_1"

我们注意到,生成的user~keys,它是一个zset类型的key,如果使用get会报WRONGTYPE Operation against a key holding the wrong kind of value。这个问题坑了我很久

@Cacheable

一般用于查询操作,根据key查询缓存.


  1. 如果key不存在,查询db,并将结果更新到缓存中。
  2. 如果key存在,直接查询缓存中的数据。


查询的例子,当第一查询的时候,redis中不存在key,会从db中查询数据,并将返回的结果插入到redis中。

@Cacheable    public List<User> selectAllUser(){        return userMapper.selectAll();    }

调用方式。

@Test    public void selectTest(){        System.out.println("===========第一次调用=======");        List<User> list = useCacheRedisService.selectAllUser();        System.out.println("===========第二次调用=======");        List<User> list2 = useCacheRedisService.selectAllUser();        for (User u : list2){            System.out.println("u = " + u);        }    }

打印结果,大家也可以试一试
只输出一次sql查询的语句,说明第二次查询是从redis中查到的。

===========第一次调用=======keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.DEBUG [main] - ==>  Preparing: SELECT id,name,sex,age,password,account FROM user DEBUG [main] - ==> Parameters: DEBUG [main] - <==      Total: 1===========第二次调用=======keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.u = User{id=1, name='fsdfds', sex='fdsfg', age=24, password='gfdsg', account='gfds'}

redis中的结果
我们可以看到redis中已经存在
com.lzl.redisService.UseCacheRedisService.selectAllUser.记录了。
这么长的一串字符key是根据自定义key值生成的。

127.0.0.1:6379> keys *1) "user~keys"2) "com.lzl.redisService.UseCacheRedisService.selectAllUser."3) "user_1"127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.selectAllUser."[\"java.util.ArrayList\",[[\"com.lzl.bean.User\",{\"id\":1,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]]]"

@CachePut

一般用于更新和插入操作,每次都会请求db
通过key去redis中进行操作。
1. 如果key存在,更新内容
2. 如果key不存在,插入内容。

 /**     * 单个user对象的插入操作,使用user+id     * @param user     * @return     */    @CachePut(key = "\"user_\" + #user.id")    public User saveUser(User user){        userMapper.insert(user);        return user;    }

redis中的结果
多了一条记录user_2

127.0.0.1:6379> keys *1) "user~keys"2) "user_2"3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."4) "user_1"127.0.0.1:6379> get user_2"[\"com.lzl.bean.User\",{\"id\":2,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]"

@CacheEvict

根据key删除缓存中的数据。allEntries=true表示删除缓存中的所有数据。

 @CacheEvict(key = "\"user_\" + #id")    public void deleteById(Integer id){        userMapper.deleteByPrimaryKey(id);    }

测试方法

 @Test    public void deleteTest(){        useCacheRedisService.deleteById(1);    }

redis中的结果
user_1已经移除掉。

127.0.0.1:6379> keys *1) "user~keys"2) "user_2"3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."

测试allEntries=true时的情形。

 @Test    public void deleteAllTest(){        useCacheRedisService.deleteAll();    } @CacheEvict(allEntries = true)    public void deleteAll(){        userMapper.deleteAll();    }

redis中的结果
redis中的数据已经全部清空

127.0.0.1:6379> keys *(empty list or set)

@Caching

通过注解的属性值可以看出来,这个注解将其他注解方式融合在一起了,我们可以根据需求来自定义注解,并将前面三个注解应用在一起

public @interface Caching {    Cacheable[] cacheable() default {};    CachePut[] put() default {};    CacheEvict[] evict() default {};}

使用例子如下

 @Caching(            put = {                    @CachePut(value = "user", key = "\"user_\" + #user.id"),                    @CachePut(value = "user", key = "#user.name"),                    @CachePut(value = "user", key = "#user.account")            }    )    public User saveUserByCaching(User user){        userMapper.insert(user);        return user;    }
 @Test    public void saveUserByCachingTest(){        User user = new User();        user.setName("dkjd");        user.setAccount("dsjkf");        useCacheRedisService.saveUserByCaching(user);    }

redis中的执行结果
一次添加三个key

127.0.0.1:6379> keys *1) "user~keys"2) "dsjkf"3) "dkjd"4) "user_3"

结合@Caching还可以设置自定义的注解

自定义注解

@Caching(        put = {                @CachePut(value = "user", key = "\"user_\" + #user.id"),                @CachePut(value = "user", key = "#user.name"),                @CachePut(value = "user", key = "#user.account")        })@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface SaveUserInfo {}

使用如下

@SaveUserInfo    public User saveUserByInfo(User user){        userMapper.insert(user);        return user;    }

测试

@Test    public void saveUserByInfoTest(){        User user = new User();        user.setName("haha");        user.setAccount("hhhcc");        useCacheRedisService.saveUserByInfo(user);    }

redis结果

127.0.0.1:6379> keys *1) "user_4"2) "dsjkf"3) "dkjd"4) "user~keys"5) "haha"6) "hhhcc"7) "user_3"

通过以上的例子基本可以了解springcache的使用了,当然还有更加复杂的操作,这里只是简单的介绍一下,运用到实际的项目中还是有所欠缺的。不过有这个基础应该不会太难。同时有时间可以再研究一下spring-cache的实现原理。是基于AOP的实现的,这也是我们在项目中学习的地方。

遇到的两个问题

WRONGTYPE Operation against a key holding the wrong kind of value

这个就是上面所说的类型不一致,使用redis命令不当造成的。所以在查找redis的value时候,需要知道key的类型。

type key

Invalid argument(s)

还是redis现实的错误,这个有些困惑,在get的时候,一定要加上”“(引号)才行。

127.0.0.1:6379> keys *1) "user_4"2) "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}"3) "dsjkf"4) "dkjd"5) "user~keys"6) "haha"7) "hhhcc"8) "user_3"127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}Invalid argument(s)127.0.0.1:6379> type com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}Invalid argument(s)127.0.0.1:6379> get "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}""[\"com.lzl.bean.User\",{\"id\":5,\"name\":\"fsdsg\",\"sex\":\"vcxvx\",\"age\":24,\"password\":\"vcxvcxc\",\"account\":\"vxcvxc\"}]"
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Spring 思维导图,让 Spring 不再难懂(cache篇)
spring boot整合redis,实现shiro的CacheManager
如何设计一个本地缓存
统一缓存帝国,实战 Spring Cache!
Spring Boot中,Redis缓存还能这么用!
springboot整合spring @Cache和Redis
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服