SpringBoot缓存注解学习笔记之Redis

2017-02-20 15:28

之前用SpringBoot+MyBatisPlus+SpringMVC整合搭建了一个基础web开发框架,使用这三个框架搭建出来项目结构非常的清爽,没有过多的配置文件,各个模块之间有清晰的联系,非常适合敏捷开发。

最近学习了Redis这个基于内存的,Key-Value数据形式的高性能数据库,感觉学习了入门之后很简单,没有体会到它具体能干嘛,我就想着使用Redis这个数据库来整合之前搭建的框架,利用Spring中的缓存机制,将查询的信息缓存到Redis中。

安装Redis

Window 下安装 下载地址: https://github.com/MSOpenTech/redis/relea ses。 Redis 支持32 位和64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C盘的tools目录中,解压后,将文件夹重新命名为 redis。

SpringBoot缓存注解学习笔记之Redis0

下载Redis版本

打开一个 cmd 窗口 使用cd命令切换目录到 C:\tools\redis 运行 redis-server.exe redis.windows.conf 。

如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:

SpringBoot缓存注解学习笔记之Redis1

启动成功后

启动成功后不要关闭命令窗口,不然redis服务也会关闭。如果不想每次使用都一直开着这个命令窗口,可以将redis服务添加到windows的服务中:

安装命令:
redis-server.exe --service-install redis.windows.conf --loglevel verbose 

卸载命令:
redis-server --service-uninstall

安装成功后,可以在windows的服务管理中对redis进行管理,就不用每次都打开命令窗口来启动redis服务了,如下图:

SpringBoot缓存注解学习笔记之Redis2

在windows中管理redis服务

获取之前项目

环境我就直接在之前的整合框架上进行搭建,之前项目下载地址: https://git.oschina.net/z77z/springboot_mybatisplus

注意:之前搭建这个框架的时候我为了获取基础数据,在启动springboot的时候也启动了爬虫程序,如果不想每次启动都启动爬虫可以注释掉启动类中的run方法。

添加Redis依赖到pom.xml中

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

在application.properties中添加配置

# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0

我们需要做的配置到这里就已经完成了,Spring Boot会在侦测到存在Redis的依赖并且Redis的配置是可用的情况下,使用RedisCacheManager初始化CacheManager。也就是说要使用缓存的话,SpringBoot就会选择Redis来作为缓存的容器。

编写一个简单的redis读写测试列

/**
 * redis读写测试
 * @author z77z
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public classRedisCacheTest{
	@Autowired
	StringRedisTemplate stringRedisTemplate;
	
	@Test
	publicvoidredisTest()throwsException{

		//保存字符串
		stringRedisTemplate.opsForValue().set("aaa", "111");
		//读取字符串
		String aaa = stringRedisTemplate.opsForValue().get("aaa");
		System.out.println(aaa);
	}
}

打印结果:

SpringBoot缓存注解学习笔记之Redis3

redis读写测试结果

编写缓存测试列

不使用缓存:

/**
 * 获取数据,并且做缓存处理
 * @author z77z
 *
 */
@Component
public classRedisCache{
	
	@Autowired
	BeautifulPicturesService beautifulPicturesService;
	//@Cacheable(value = "BeautifulPictures")
	publicBeautifulPicturesgetBeautifulPicturesList(String id){
		return beautifulPicturesService.selectById(id);
	}
}
/**
 * 测试类
 * @author z77z
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public classRedisCacheTest{
	@Autowired
	BeautifulPicturesService beautifulPicturesService;

	@Autowired
	StringRedisTemplate stringRedisTemplate;
	
	@Autowired
	RedisCache redisCache;
	
	@Test
	publicvoidredisTest()throwsException{

		//保存字符串
		stringRedisTemplate.opsForValue().set("aaa", "111");
		//读取字符串
		String aaa = stringRedisTemplate.opsForValue().get("aaa");
		System.out.println(aaa);
	}
	
	@Test
	publicvoidCacheTest(){
		BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList("1011");
		System.out.println("第一次查询结果:");
		System.out.println(beautifulPicture);

		BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList("1011");
		System.out.println("第二次查询结果:");
		System.out.println(beautifulPicture1);
	}
}

执行结果:

SpringBoot缓存注解学习笔记之Redis4

可以从日志中看出两次查询都是执行了sql,也就是执行了 getBeautifulPicturesList 这个方法

使用缓存:

在getBeautifulPicturesList方法的上面添加@Cacheable(value = “BeautifulPictures”)注解,当请求这个方法时会先判断缓存中是否存在,存在就在缓存中获取,不会执行这个方法。不存在就正常执行这个方法获取返回值并且存如缓存中。添加注解后执行结果如下:

SpringBoot缓存注解学习笔记之Redis5

添加缓存注释后

从日志中可以看出,第一次查询的时候执行的sql,而第二次查询的时候没有执行sql,说明是从缓存中获取的数据。

缓存数据一致性保证

CRUD (Create 创建,Retrieve 读取,Update 更新,Delete 删除) 操作中,除了 R具备幂等性,其他三个发生的时候都可能会造成缓存结果和数据库不一致。为了保证缓存数据的一致性,在进行 CUD 操作的时候我们需要对可能影响到的缓存进行更新或者清除。如下:

/**
 * 获取数据,并且做缓存处理
 * @author z77z
 *
 */
@Component
public classRedisCache{
	
	@Autowired
	BeautifulPicturesService beautifulPicturesService;
	
	//查询
	@Cacheable(value = "beautifulPictures")
	publicBeautifulPicturesgetBeautifulPicturesList(String id){
		return beautifulPicturesService.selectById(id);
	}
	
	//修改
	@CachePut(value = "beautifulPictures")
	publicvoidupdateBeautifulPicture(String id){
		BeautifulPictures beautifulPictures = new BeautifulPictures();
		beautifulPictures.setTitle("Title被我修改了一下,哈哈");
		beautifulPictures.setId(id);
		beautifulPicturesService.updateById(beautifulPictures);
	}
}
/**
 * 测试类
 * @author z77z
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public classRedisCacheTest{
	@Autowired
	BeautifulPicturesService beautifulPicturesService;

	@Autowired
	StringRedisTemplate stringRedisTemplate;
	
	@Autowired
	RedisCache redisCache;
	
	@Test
	publicvoidredisTest()throwsException{

		//保存字符串
		stringRedisTemplate.opsForValue().set("aaa", "111");
		//读取字符串
		String aaa = stringRedisTemplate.opsForValue().get("aaa");
		System.out.println(aaa);
	}
	
	@Test
	publicvoidCacheTest(){
		String id = "1";
		BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList(id);
		System.out.println("第一次查询结果:");
		System.out.println(beautifulPicture);

		BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList(id);
		System.out.println("第二次查询结果:");
		System.out.println(beautifulPicture1);
		
		redisCache.updateBeautifulPicture(id);
		
		BeautifulPictures beautifulPicture2 = redisCache.getBeautifulPicturesList(id);
		System.out.println("第三次查询结果:");
		System.out.println(beautifulPicture2);
	}
}

保持缓存一致性测试结果:

SpringBoot缓存注解学习笔记之Redis6

保持缓存的一致性测试

在会导致数据发生改变的方法上添加 @CachePut(value = "beautifulPictures") 注解,添加后会更新缓存中的值,并且每次都会正常执行方法内容。

SpringBoot缓存注解详解

  • @Cacheable:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

主要参数说明:

  1. value :缓存的名称,在 spring 配置文件中定义,必须指定至少一个,例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}。
  2. key :缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,例如:@Cacheable(value=”testcache”,key=”#userName”)。
  3. condition :缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存,例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2。
  • @CachePut:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用

主要参数说明:

  1. value , key 和 condition 参数配置和@Cacheable一样。
  • @CacheEvict:作用是主要针对方法配置,能够根据一定的条件对缓存进行清空

主要参数说明:

  1. value , key 和 condition 参数配置和@Cacheable一样。
  2. allEntries :是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存,例如:@CachEvict(value=”testcache”,allEntries=true)。
  3. beforeInvocation :是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存,例如@CachEvict(value=”testcache”,beforeInvocation=true)。

另外说下: @cache(“something”);这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。