Spring从3.1开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache和 org.springframework.cache.CacheManager接口来统一不同的缓存技术。并支持使用JCacheJSR-107)注解简化我们的开发。
1.Springboot常用缓存注解
| 注解 | 说明 |
|---|---|
| @EnableCaching | 开启注解,表示允许使用注解的方式进行缓存操作 |
| @Cacheable | 可用于类或方法上;在目标方法执行前,会根据key先去缓存中查询看是否有数据,有就直接返回缓存中的key对应的value值。不再执行目标方法;无则执行目标方法,并将方法的返回值作为value,并以键值对的形式存入缓存。 |
| @CachePut | 可用于类或方法上;在执行完目标方法后,并将方法的返回值作为value,并以键值对的形式存入缓存中。注意:该注解每次都会执行方法。 |
| @CacheEvict | 可用于类或方法上;在执行完目标方法后,清除缓存中对应key的数据。 |
| @Caching | 此注解即可作为@Cacheable、@CacheEvict、@CachePut三种注解中的的任何一种或几种来使用。(很少使用) |
2.实例
1)在pom.xml添加以下依赖。
<properties>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<jackson.version>2.13.2</jackson.version>
<commons-pool2.version>2.11.1</commons-pool2.version>
</properties>
<dependencies>
<!--springboot2.x以后默认使用lettuce实现springboot与redis整合-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
</dependencies>
2)在application.yml添加缓存配置
#数据源配置
spring:
#配置数据源
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#Springboot2.0 的hikari配置
hikari:
minimum-idle: 5
#空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
#连接池最大连接数,默认是10
maximum-pool-size: 10
#此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
#连接池名称
pool-name: MyHikariCP
#此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
max-lifetime: 1800000
#数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
connection-test-query: SELECT 1
# redis配置
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
# 连接超时时间(毫秒)
timeout: 1000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
cache:
type: redis
3)在启动类开启缓存支持。
@SpringBootApplication
@MapperScan(basePackages = {"com.myproject.front.mapper"})
@EnableCaching //开启缓存支持
public class FrontApplication {
public static void main(String[] args) {
SpringApplication.run(FrontApplication.class, args);
}
}
4).添加Redis配置类。
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* * 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的 配置注入并且此类是一个配置类可以更多程度的自定义配置
* *
* * @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* *
* * 缓存配置管理器
*/
@Bean("redisCacheManager")
@Primary
@DependsOn("customRedisTemplate")
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisTemplate.getConnectionFactory())
// 设置缓存默认永不过期
.cacheDefaults(
RedisCacheConfiguration.defaultCacheConfig()
// 不缓存null(需要与unless = "#result == null"共同使用)
.disableCachingNullValues()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())))
// 配置同步修改或删除 put/evict
.transactionAware()
.build();
}
@Bean("customRedisTemplate")
//当自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Primary
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式。
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
5)设计RedisUtil工具类。
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
@Component
public class RedisUtil {
@Resource(name = "customRedisTemplate")
private RedisTemplate<String, Object> redisTemplate; //redisTemplate
/**
* 往redis中缓存数据
*
* @param key
* @param object
* @return
*/
public boolean set(String key, Object object) {
ValueOperations<String, Object> vo = redisTemplate.opsForValue();
vo.set(key, object);
return true;
}
/**
* 根据key从redis服务器中获取value值
*
* @param key
* @return
*/
public Object get(String key) {
ValueOperations<String, Object> vo = redisTemplate.opsForValue();
return vo.get(key);
}
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
/**
* 根据key从Redis中删除value值
*/
public void remove(String key) {
redisTemplate.delete(key);
}
/*
* 根据前缀模糊删除key的集合
* */
public void removeByPrex(String prex) {
Set<String> keys = redisTemplate.keys(prex+"*");
if (CollectionUtils.isNotEmpty(keys)) {
redisTemplate.delete(keys);
}
}
}
6)设计Controller基类
import com.myproject.front.util.RedisUtil;
import javax.annotation.Resource;
public class BaseController {
@Resource
protected RedisUtil redisUtil;
}
7)设计测试接口。 分别实现查询Redis缓存,更新Redis缓存和删除Redis缓存。
注意:响应结果封装类R,必须实现Serializable接口。同时在pom.xml中移除spring-boot-devtools热部署依赖。
import com.myproject.front.entity.Catalog;
import com.myproject.front.service.CatalogService;
import com.myproject.front.util.R;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/test")
public class TestRedisCacheController extends BaseController {
@Resource
private CatalogService catalogService;
@GetMapping("catalog")
@Cacheable(cacheNames="allCatalogList",key = "#root.method.name")
public R getAllCatalogList(){
System.out.println("TestRedisCacheController=>getAllCatalogList()....");
List<Catalog> catalogList = new ArrayList<Catalog>();
try{
catalogList = catalogService.queryAllCatalogList();
redisUtil.set("allCatalogList",catalogList);
return R.ok("查询板块列表缓存成功!",catalogList);
}catch(Exception ex){
ex.printStackTrace();
return R.error("查询板块列表缓存出现异常!");
}
}
@PutMapping("catalog")
@CachePut(cacheNames="allCatalogList",key = "#root.method.name")
public R updateAllCatalogList(){
System.out.println("TestRedisCacheController=>updateAllCatalogList()....");
List<Catalog> catalogList = new ArrayList<Catalog>();
try{
catalogList = catalogService.queryAllCatalogList();
redisUtil.set("allCatalogList",catalogList);
return R.ok("更新板块列表缓存成功!",catalogList);
}catch(Exception ex){
ex.printStackTrace();
return R.error("更新板块列表缓存出现异常!");
}
}
@DeleteMapping("catalog")
@CacheEvict(cacheNames="allCatalogList",key = "'getAllCatalogList'")
public R deleteAllCatalogList(){
System.out.println("TestRedisCacheController=>deleteAllCatalogList()....");
try{
redisUtil.remove("allCatalogList");
return R.ok("删除板块列表缓存成功!");
}catch(Exception ex){
ex.printStackTrace();
return R.error("删除板块列表缓存出现异常!");
}
}
}