Redis系列三
来源:互联网 发布:java empty array 编辑:程序博客网 时间:2024/06/05 15:38
Redis系列三 - Spring boot如何使用redis做缓存及缓存注解的用法总结
目录(?)[+]
1. 概述
本文介绍Spring boot 如何使用redis做缓存,如何对redis缓存进行定制化配置(如key的有效期)以及spring boot 如何初始化redis做缓存。使用具体的代码介绍了@Cacheable,@CacheEvict,@CachePut,@CacheConfig等注解及其属性的用法。
2. spring boot集成redis
2.1. application.properties
配置application.properties,包含如下信息:
- 指定缓存的类型
- 配置redis的服务器信息
- 请不要配置spring.cache.cache-names值,原因后面再说
## 缓存# spring.cache.cache-names=book1,book2spring.cache.type=REDIS# REDIS (RedisProperties) spring.redis.database=0spring.redis.host=192.168.188.7spring.redis.password=spring.redis.port=6379spring.redis.pool.max-idle=8spring.redis.pool.min-idle=0 spring.redis.pool.max-active=100 spring.redis.pool.max-wait=-1
2.2. 配置启动类
- @EnableCaching: 启动缓存
- 重新配置RedisCacheManager,使用新的配置的值
@SpringBootApplication@EnableCaching // 启动缓存public class CacheApplication { private static final Logger log = LoggerFactory.getLogger(CacheApplication.class); public static void main(String[] args) { log.info("Start CacheApplication.. "); SpringApplication.run(CacheApplication.class, args); } /** * 重新配置RedisCacheManager * @param rd */ @Autowired public void configRedisCacheManger(RedisCacheManager rd){ rd.setDefaultExpiration(100L); }}
经过以上配置后,redis缓存管理对象已经生成。下面简单介绍spring boot如何初始化redis缓存。
2.3. spring boot 如何初始化redis做缓存
缓存管理接口org.springframework.cache.CacheManager,spring boot就是通过此类实现缓存的管理。redis对应此接口的实现类是org.springframework.data.redis.cache.RedisCacheManager。下面介绍此类如何生成。
首先我们配置application.properties的spring.redis.* 属性后@EnableCaching后,spring会执行RedisAutoConfiguration,初始化RedisTemplate和StringRedisTemplate
@Configuration@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })@EnableConfigurationProperties(RedisProperties.class)public class RedisAutoConfiguration {/** * Standard Redis configuration. */@Configurationprotected static class RedisConfiguration { …. @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean(StringRedisTemplate.class) public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }}}
然后RedisCacheConfiguration会将RedisAutoConfiguration生成的RedisTemplate注入方法生成RedisCacheManager 后。
@Configuration@AutoConfigureAfter(RedisAutoConfiguration.class)@ConditionalOnBean(RedisTemplate.class)@ConditionalOnMissingBean(CacheManager.class)@Conditional(CacheCondition.class)class RedisCacheConfiguration { private final CacheProperties cacheProperties; private final CacheManagerCustomizers customizerInvoker; RedisCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) { this.cacheProperties = cacheProperties; this.customizerInvoker = customizerInvoker; } @Bean public RedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); cacheManager.setUsePrefix(true); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return this.customizerInvoker.customize(cacheManager); }}
根据以上的分析,我们知道在spring已经帮我们生成一个RedisCacheManager并进行了配置。
最后我们再可以对这个RedisCacheManager进行二次配置,这里只列出配置key的有效期
/** * 重新配置RedisCacheManager * @param rd */ @Autowired public void configRedisCacheManger(RedisCacheManager rd){ rd.setDefaultExpiration(100L); }
注意:
请不要在applicaion.properties中配置: spring.cache.cache-names=book1,book2,否则会导致我们新的配置无法作用到这些配置的cache上。这是因为RedisCacheConfiguration 初始化RedisCacheManager后,会立即调用RedisCacheConfiguration 的初始化cache,而此时configRedisCacheManger还没有执行此方法,使得我们的配置无法启作用。反之,如果不配置,则后创建cache,会使用我们的配置。
3. spring缓存注解的用法
上节已经介绍如何配置缓存,这节介绍如何使用缓存。
3.1 辅助类
下方会使用到的辅助类
Book:
public class Book implements Serializable { private static final long serialVersionUID = 2629983876059197650L; private String id; private String name; // 书名 private Integer price; // 价格 private Date update; // public Book(String id, String name, Integer price, Date update) { super(); this.id = id; this.name = name; this.price = price; this.update = update; } // set/get略}
BookQry : 封装请求类
public class BookQry { private String id; private String name; // 书名 // set/get略}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
AbstractService
抽象类:初始化repositoryBook 值,模拟数据库数据。BookService 和BookService2都是继承此类
public abstract class AbstractService { protected static Map<String, Book> repositoryBook = new HashMap<>(); public AbstractService() { super(); } @PostConstruct public void init() { // 1 Book book1 = new Book("1", "name_1", 11, new Date()); repositoryBook.put(book1.getId(), book1); // 2 Book book2 = new Book("2", "name_2", 11, new Date()); repositoryBook.put(book2.getId(), book2); // 3 Book book3 = new Book("3", "name_3", 11, new Date()); repositoryBook.put(book3.getId(), book3); // 4 Book book4 = new Book("4", "name_4", 11, new Date()); repositoryBook.put(book4.getId(), book4); }}
3.2. @Cacheable
@Cacheable的属性的意义
- cacheNames:指定缓存的名称
- key:定义组成的key值,如果不定义,则使用全部的参数计算一个key值。可以使用spring El表达式
/** * cacheNames 设置缓存的值 * key:指定缓存的key,这是指参数id值。 key可以使用spEl表达式 * @param id * @return */ @Cacheable(cacheNames="book1", key="#id") public Book queryBookCacheable(String id){ logger.info("queryBookCacheable,id={}",id); return repositoryBook.get(id); } /** * 这里使用另一个缓存存储缓存 * * @param id * @return */ @Cacheable(cacheNames="book2", key="#id") public Book queryBookCacheable_2(String id){ logger.info("queryBookCacheable_2,id={}",id); return repositoryBook.get(id); } /** * 缓存的key也可以指定对象的成员变量 * @param qry * @return */ @Cacheable(cacheNames="book1", key="#qry.id") public Book queryBookCacheableByBookQry(BookQry qry){ logger.info("queryBookCacheableByBookQry,qry={}",qry); String id = qry.getId(); Assert.notNull(id, "id can't be null!"); String name = qry.getName(); Book book = null; if(id != null){ book = repositoryBook.get(id); if(book != null && !(name != null && book.getName().equals(name))){ book = null; } } return book; }
- keyGenerator:定义key生成的类,和key的不能同时存在
/** * 以上我们使用默认的keyGenerator,对应spring的SimpleKeyGenerator * 如果你的使用很复杂,我们也可以自定义myKeyGenerator的生成key * * key和keyGenerator是互斥,如果同时制定会出异常 * The key and keyGenerator parameters are mutually exclusive and an operation specifying both will result in an exception. * * @param id * @return */ @Cacheable(cacheNames="book3", keyGenerator="myKeyGenerator") public Book queryBookCacheableUseMyKeyGenerator(String id){ logger.info("queryBookCacheableUseMyKeyGenerator,id={}",id); return repositoryBook.get(id); }// 自定义缓存key的生成类实现如下:@Componentpublic class MyKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { System.out.println("自定义缓存,使用第一参数作为缓存key. params = " + Arrays.toString(params)); // 仅仅用于测试,实际不可能这么写 return params[0] + "0"; }}
- sync:如果设置sync=true:a. 如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待; b. 如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据
/*** * 如果设置sync=true, * 如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待 * 如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据 * @param id * @return */ @Cacheable(cacheNames="book3", sync=true) public Book queryBookCacheableWithSync(String id) { logger.info("begin ... queryBookCacheableByBookQry,id={}",id); try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { } logger.info("end ... queryBookCacheableByBookQry,id={}",id); return repositoryBook.get(id); }
- condition和unless 只满足特定条件才进行缓存:
- condition: 在执行方法前,condition的值为true,则缓存数据
- unless :在执行方法后,判断unless ,如果值为true,则不缓存数据
- conditon和unless可以同时使用,则此时只缓存同时满足两者的记录
/** * 条件缓存: * 只有满足condition的请求才可以进行缓存,如果不满足条件,则跟方法没有@Cacheable注解的方法一样 * 如下面只有id < 3才进行缓存 * */ @Cacheable(cacheNames="book11", condition="T(java.lang.Integer).parseInt(#id) < 3 ") public Book queryBookCacheableWithCondition(String id) { logger.info("queryBookCacheableByBookQry,id={}",id); return repositoryBook.get(id); } /** * 条件缓存: * 对不满足unless的记录,才进行缓存 * "unless expressions" are evaluated after the method has been called * 如下面:只对不满足返回 'T(java.lang.Integer).parseInt(#result.id) <3 ' 的记录进行缓存 * @param id * @return */ @Cacheable(cacheNames="book22", unless = "T(java.lang.Integer).parseInt(#result.id) <3 ") public Book queryBookCacheableWithUnless(String id) { logger.info("queryBookCacheableByBookQry,id={}",id); return repositoryBook.get(id); }
3.3. @CacheEvict
删除缓存
- allEntries = true: 清空缓存book1里的所有值
- allEntries = false: 默认值,此时只删除key对应的值
/** * allEntries = true: 清空book1里的所有缓存 */ @CacheEvict(cacheNames="book1", allEntries=true) public void clearBook1All(){ logger.info("clearAll"); } /** * 对符合key条件的记录从缓存中book1移除 */ @CacheEvict(cacheNames="book1", key="#id") public void updateBook(String id, String name){ logger.info("updateBook"); Book book = repositoryBook.get(id); if(book != null){ book.setName(name); book.setUpdate(new Date()); } }
3.4. @CachePut
每次执行都会执行方法,无论缓存里是否有值,同时使用新的返回值的替换缓存中的值。这里不同于@Cacheable:@Cacheable如果缓存没有值,从则执行方法并缓存数据,如果缓存有值,则从缓存中获取值
@CachePut(cacheNames="book1", key="#id") public Book queryBookCachePut(String id){ logger.info("queryBookCachePut,id={}",id); return repositoryBook.get(id); }
3.5. @CacheConfig
@CacheConfig: 类级别的注解:如果我们在此注解中定义cacheNames,则此类中的所有方法上 @Cacheable的cacheNames默认都是此值。当然@Cacheable也可以重定义cacheNames的值
@Component@CacheConfig(cacheNames="booksAll") public class BookService2 extends AbstractService { private static final Logger logger = LoggerFactory.getLogger(BookService2.class); /** * 此方法的@Cacheable没有定义cacheNames,则使用类上的注解@CacheConfig里的值 cacheNames * @param id * @return */ @Cacheable(key="#id") public Book queryBookCacheable(String id){ logger.info("queryBookCacheable,id={}",id); return repositoryBook.get(id); } /** * 此方法的@Cacheable有定义cacheNames,则使用此值覆盖类注解@CacheConfig里的值cacheNames * * @param id * @return */ @Cacheable(cacheNames="books_custom", key="#id") public Book queryBookCacheable2(String id){ logger.info("queryBookCacheable2,id={}",id); return repositoryBook.get(id); }}
4. 测试
4.1. 准备 redis
可以通过docker安装redis,非常方便。docker的用法见docker 安装和常用命令
4.2. 测试类
如果要验证以上各个方法,可以下载工程,并执行测试类CacheTest。由于比较简单,这里不在演示。
5. 代码
本文的详细代码Github
- C# redis 系列三
- Redis系列三
- Redis系列三
- Redis系列~数据类型(三)
- Redis系列(三)Redis数据结构
- Redis系列之Redis集群(三)
- redis系列(三)redis的安装
- Redis系列(三)--过期策略
- Redis系列之持久化(三)
- Redis数据库系列(三)------Redis字符串类型的操作
- Redis数据类型之散列类型--Redis系列三
- Redis系列(三):Redis发布订阅及客户端编程
- Redis系列(三)—— Redis和消息中间件
- Redis系列(三)-Redis发布订阅及客户端编程
- PHP框架Yii系列教程(三):集成Redis
- Linux 系列(三)——Redis 分布式集群搭建
- Redis主从复制和集群配置系列之三
- Redis系列(三)——主从介绍和配置
- 从数据库反向生成代码
- RecyclerView.Adapter通用性
- 通过自定义view来做一个五子棋
- 项目总结
- UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 205: illegal multibyte sequence
- Redis系列三
- 数据结构上机2——链表
- SSM框架——使用MyBatis Generator自动创建代码
- Python脚本报错AttributeError: ‘module’ object has no attribute’xxx’解决方法
- GameEntity(三)—— EntityStrategyHelper
- 如何在Web页面里使用高拍仪扫描上传图像
- Linux 环境下tomcat 开机启动设置
- Spring Boot打包war jar 部署tomcat
- win10下caffe+gpu使用问题总结