SpringBoot基础 —— 缓存使用,内涵 redis整合,序列化修改

版权声明: https://blog.csdn.net/qq_36786991/article/details/86710522

参考链接

Spring缓存抽象

Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;

  • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
  • Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;
  • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
  • 使用Spring缓存抽象时我们需要关注以下两点;
    1. 确定方法需要被缓存以及他们的缓存策略
    2. 从缓存中读取之前缓存存储的数据
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

如果不用的第三方的话,默认情况使用 ConcurrentMap缓存

重点知识点

名称 解释
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={”cache1”,”cache2”}
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)
  • 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
其他类型 ?.,?[…],![…],$[…]

原理

@Cacheable

  1. 方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取,CacheManage获取相应的缓存,第一次获取缓存如果没有cache组件会自动创建
  2. 去cache中查找缓存的内容,使用一个key 默认方法的参数,key是按照某种策略生成的,默认是KeyGenerator生成的,默认是使用SimpleKeyGenerator
  3. 没有查到缓存叫调用目标方法
  4. 将目标方法的结果,放进缓存

整合Redis

Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性

整合

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

当你导入这一个依赖时,SpringBootCacheManager就会使用RedisCache

如果你的Redis使用默认配置,这时候已经可以启动程序了。

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

原理

springBoot加载缓存组件

  • CacheManager === Cache缓存组件来实际给缓存中存取数据
  • 引入redis的starter,容器中保存的是RedisCacheManager
@Configuration
@ConditionalOnClass({RedisConnectionFactory.class})
@AutoConfigureAfter({RedisAutoConfiguration.class}) 
@ConditionalOnBean({RedisConnectionFactory.class})
@ConditionalOnMissingBean({CacheManager.class}) //
@Conditional({CacheCondition.class})
class RedisCacheConfiguration {
    private final CacheProperties cacheProperties;
    private final CacheManagerCustomizers customizerInvoker;
    private final org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration;
  • RedisCacheManager 帮我们创建RedisCache来作为缓存组件,RedisCache通过操作缓存数据的
  • 默认保存数据是 k-v都是Object;利用序列化保存的

自定义序列化方式

  • 如何保存为JSON?
修改RedisTemplate序列化方式

Spring boot集成 Redis 客户端jedis(不同版本spring-boot-starter-data-redis使用的策略不一样,高版本也有用lettuce的)。

此段代码可copy RedisAutoConfiguration.class 文件下的代码片段,然后自己添加序列化代码即可

@Bean
public RedisTemplate<Object, Stu> stuRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    RedisTemplate<Object, Stu> template = new RedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Stu>(Stu.class));
    return template;
}
@Autowired
StuService stuService;

@Autowired
StringRedisTemplate stringRedisTemplate;

@Autowired
RedisTemplate<Object,Stu> stuRedisTemplate;

@Autowired
RedisTemplate redisTemplate;

/**
 * redis操作
 *
 * 1,string
 * 2,list
 * 3,set
 * 4,hash
 * 5,zset
 */
@Test
public void test01(){
    stringRedisTemplate.opsForValue().set("string","string");
    stringRedisTemplate.opsForList().leftPush("list","1");
    stringRedisTemplate.opsForSet().add("set","dsadsa");
    stringRedisTemplate.opsForSet().add("set","dsadsa","2321","ewq");
}

@Test
public void test02(){
    //默认如果保存对象,使用jdk序列化机制,序列化后的数据存在redis里
    redisTemplate.opsForValue().set("ob", new Stu(1, "hcy", 20, null, null));
    //1、将数据以json的方式保存
    // 01、自己将对象转为json
    // 02、redisTemplate默认序列化规则
    Object ob = redisTemplate.opsForValue().get("ob");
    Stu stu = (Stu) ob;
    System.out.println(stu.getStuName());
}

@Test
public void test03(){
    //默认如果保存对象,使用jdk序列化机制,序列化后的数据存在redis里
    stuRedisTemplate.opsForValue().set("objjjj", new Stu(1, "hcy", 20, null, null));
    //1、将数据以json的方式保存
    // 01、自己将对象转为json
    // 02、redisTemplate默认序列化规则 修改默认规则
    Object ob = redisTemplate.opsForValue().get("ob");
    Stu stu = (Stu) ob;
    System.out.println(stu.getStuName());
}
自定义RedisCacheManage

这里说一下博主的一个想法,RedisCacheManager之前的版本应该是有一个构造方法直接传入RedisTemplate的,只要重写一下RedisCacheConfiguration里的bean就行;

我使用的这个版本是不可以的,需要传入RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation,后来也没那么做

我的版本是

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
            .disableCachingNullValues();

    RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .transactionAware()
            .build();

    return redisCacheManager;
}

private RedisSerializer<String> keySerializer() {
    return new StringRedisSerializer();
}

private RedisSerializer<Object> valueSerializer() {
    return new GenericJackson2JsonRedisSerializer();
}

猜你喜欢

转载自blog.csdn.net/qq_36786991/article/details/86710522