缓存的好处不言而喻,比如查询商品的价格,如果可以放到缓存中,而不用每次都到数据库中查询,这将会大大提升系统性能,因为和缓存交互比访问数据库要快很多。或者在缓存中存放临时数据,而不用放到数据库中。
在学习Spring Boot中的数据的时候,我们需要先来了解一下几个非常重要的概念:
- Cache: 缓存接口,定义缓存操作,具体实现这些操作的有:RedisCache、EhCacheCache、ConcurrentMapCache等。
- CacheManager:缓存管理器,管理各种缓存(Cache)组件
- @Cacheable:主要针对方法配置,能够根据方法的请求参数对方法返回的结果进行缓存。
- @CacheEvict:清除缓存,比如一个用户退出了,我们需要清除这个用户的所有临时数据缓存。
- @CachePut:保证方法被调用,又希望结果被缓存,主要可以用来更新缓存。
- @EnableCaching:开启基于注解的缓存
- keyGenerator:缓存数据时key的生成策略
- serialize:缓存数据时value序列化策略
以数据库查询缓存为例,缓存结构如下:
如果要使用缓存的话,这里就不给出具体的例子了,在上面重要的概念的讲解中,我们已经讲解了几个注解的作用,在编程过程中使用这些注解即可。关于这些注解具体的使用规则,可以参考:https://blog.youkuaiyun.com/xm393392625/article/details/88639082
我们来看看Spring Boot底层是如何实现缓存配置的,依照之前的经验我们可以想象存在一个CacheAutoConfiguration,这个类将会完成我们的缓存配置,实际上这个类也存在,这个配置类导入了CacheConfigurationImportSelector这个类,他将会选择出下面这些缓存配置类,默认SimpleCacheConfiguration生效:
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
SimpleCacheConfiguration配置类将给容器中注册一个CacheManager:ConcurrentMapCacheManager,它可以获取和创建ConcurrentMapCache类型的缓存组件,它的作用是将数据保存在ConcurrentMap中;
以@Cacheable注解为例讲解获取缓存的流程:
- 方法运行之前,先去CacheManager中按照cacheNames指定的名字得到Cache(缓存组件),如果没有Cache组件则会自动创建。结合之前的图,Emp就是一个Cache组件。
- 之后在找到的Cache组件中查找缓存的内容,使用一个key,默认就是使用方法的参数生成这个key,比如在Emp这个Cache组件中查找具体的Value。
- 没有查到缓存就调用目标方法;
- 之后将目标方法返回的结果,放进缓存中;
以上就是Spring Boot中的缓存原理,最后来说说使用其他的CacheManager,比如常用的radis。
这里我使用的环境是 ubuntu + docker + redis来启动,在docker pull redis已经完成的情况下,运行下列内容:
然后在项目中的pow.xml文件中引入redis:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐redis</artifactId>
</dependency>
<!--或者-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
在RedisAutoConfiguration.java中Spring Boot已经帮我们做好的配置,如下:
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Christian Dupuis
* @author Christoph Strobl
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Marco Aust
* @author Mark Paluch
* @since 1.0.0
*/
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
可以看到它帮我们在容器中注册了StringRedisTemplate (K-V都是字符串)和RedisTemplate(K-V都是Object)两个组件,他们就和JdbcTemplate一样,帮助我们操作redis,不过我们可以直接使用缓存注解,在底层会自动调用这些方法,RedisTemplate 保存对象时是保存序列化后的数据,所以务必确认保存的对象可以序列化。不过大多数的时候,我们只是保存一个String和一个JSON串,所有StringRedisTemplate用的可能更多一点。如果要使用StringRedisTemplate 和RedisTemplate的话直接注入即可。不过在使用之前,需要在配置文件中写好redis主机地址:
spring.redis.host=192.168.31.246
引入redis的依赖之后,容器中保存的是RedisCacheConfiguration为我们添加的RedisCacheManager,而不使用之前所说的SimpleCacheConfiguration为我们添加的CacheManager。这是因为在SimpleCacheConfiguration类之上有一个@ConditionalOnMissingBean(CacheManager.class)这样的注解。默认创建的 RedisCacheManager 操作redis的时候使用的是 RedisTemplate<Object, Object>,RedisTemplate<Object, Object> 是默认使用jdk的序列化机制。如果你想保存JSON数据的话,请自行查找其他参考内容。