练习二级缓存Redis
练习二级缓存Redis
1.创建一个maven项目
配置基础pom.xml
<!-- 父级项目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 属性设置 -->
<properties>
<!-- jdK版本 -->
<java.version>1.8</java.version>
</properties>
<!-- 依赖关系 -->
<dependencies>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- springmvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- 编译 -->
<build>
<!-- 插件 -->
<plugins>
<!-- maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.使用简单的缓存
1.同时引入支持二级缓存的jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.在启动类上加上@EnableCaching 开启二级缓存注解
@SpringBootApplication
@EnableCaching//打开二级缓存
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.在service实现层中
@Override
//主要针对方法配置,能够根据方法的请求参数对其进行缓存
@Cacheable(value="users",key="methodName + targetClass")
public String show() {
System.out.println("show");
return "进入showc";
}
@Override
//清空缓存
@CacheEvict(value="users",allEntries=true)
public String evict() {
System.out.println("清空缓存");
return "清空缓存";
}
缓存注解解释
名称 | 解释 |
---|---|
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。与@Cacheable区别在于是否每次都调用方法,常用于更新 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@CacheConfig | 统一配置本类的缓存注解的属性 |
@Cacheable/@CachePut/@CacheEvict 主要的参数
名称 | 解释 |
---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:@Cacheable(value=”mycache”) 或者@Cacheable(value= |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合例如:@Cacheable(value=”testcache”,key=”#id”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
unless | 否定缓存。当条件结果为TRUE时,就不会缓存。@Cacheable(value=”testcache”,unless=”#userName.length()>2”) |
allEntries(@CacheEvict ) | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation(@CacheEvict) | 是否在方法执行前就清空,缺省为 false,如果指定为 true, |
则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法
执行抛出异常,则不会清空缓存 例如:@CachEvict(value=”testcache”,beforeInvocation=true)
3.SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意:
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如 :
@Cacheable(key = "targetClass + methodName +#p0")
2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],[1],$[…] |
二级缓存作用: 当访问到开启二级缓存方法时,第一次访问配置了二级缓存的方法的返回值存在二级缓存之中(内存中),后面在访问这个方法时就会直接返回结果,不在执行方法
4.项目配置Redis
1.导入依赖
就只需要这一个依赖!不需要spring-boot-starter-cache
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
当你导入这一个依赖时,SpringBoot的CacheManager就会使用RedisCache。
2.配置springboot的关于redis文件application.properties
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
3.配置Redis的缓存配置和序列化 CacheConfig类(要放在项目可以扫描到的包下面)
@Configuration
@EnableCaching
public class CacheConfig{
/**
* 配置缓存管理器RedisCacheManager
* Redis采用key-value存储的存储方式,对key和value需要序列化
* Redis的序列化方式由StringRedisSerializer,JdkSerializationRedisSerializer,GenericJackson2JsonRedisSerializer,GenericJackson2JsonRedisSerializer等
* RedisCacheManager默认采用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value
* @param redisConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// StringRedisSerializer序列器:
// 优点:开发者友好,轻量级,效率也比较高
// 缺点:只能序列化String类型,如果key使用该序列化器,则key必须为String类型
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
// jdkSerializationRedisSerializer序列化器:RestTemplate类默认的序列化方式,如果指定序列化器,则为它
// 优点:反序列化时不需要提供类型信息(class)
// 缺点:1、首先它要求存储的对象都必须实现java.io.Serializable接口,比较笨重
// 2、其次,他存储的为二进制数据,这对开发者是不友好的
// 3、序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。
// ClassLoader loader = this.getClass().getClassLoader();
// JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(loader);
// Jackson2JsonRedisSerializer序列化器:把一个对象以Json的形式存储,效率高且对调用者友好
// 优点:速度快,序列化后的字符串短小精悍,不需要存储对象实现java.io.Serializable接口
// 缺点:那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象),反序列化用到了该类型信息
// Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// ObjectMapper objectMapper = new ObjectMapper();
// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// GenericJackson2JsonRedisSerializer序列化器:基本和上面的Jackson2JsonRedisSerializer功能差不多,使用方式也差不多,
// 但不需要提供类型信息,推荐使用
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) //设置失效时间
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));
RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
return redisCacheManager;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(genericJackson2JsonRedisSerializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
配置完以上步骤,cache的缓存在本机的数据就可以存储在Redis上
例1:
//servlet实现层
//主要针对方法配置,能够根据方法的请求参数对其进行缓存
@Cacheable(value="users",key="methodName + targetClass + #p0") // "#p0"表示方法参数列表的第一个参数
public String show(String id) {
System.out.println("进入show");
return "进入showid:"+id;
}
//controller层
@RequestMapping("show")
@ResponseBody
public String show(String id) {
return testServlet.show(id);
}
在浏览器请求接口为:http://127.0.0.1:8080/show?id=0
在查看Redis中
同时修改value的值
再次请求接口http://127.0.0.1:8080/show?id=0
就会发现我们由于没有清除缓存,后台是直接获取的缓存数据没有在执行这个方法,同时在项目中也会导致缓存与数据库双写不一致。
… ↩︎