练习二级缓存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

就会发现我们由于没有清除缓存,后台是直接获取的缓存数据没有在执行这个方法,同时在项目中也会导致缓存与数据库双写不一致。


  1. ↩︎

posted @ 2021-01-28 00:48  qtyanan  阅读(100)  评论(0编辑  收藏  举报