概念
当Spring Boot 结合Redis来作为缓存使用时,最简单的方式就是使用Spring Cache了,使用它我们无需知道Spring中对Redis的各种操作,仅仅通过它提供的@Cacheable 、@CachePut 、@CacheEvict 、@EnableCaching等注解就可以实现缓存功能。
常用注解
@EnableCaching
开启缓存功能,一般放在启动类上。
@Cacheable
使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。一般使用在查询方法上,可以设置如下属性:
value:缓存名称(必填),指定缓存的命名空间;
key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
unless:条件符合则不缓存;
condition:条件符合则缓存。
@CachePut
使用该注解的方法每次执行时都会把返回结果存入缓存中。一般使用在新增方法上,可以设置如下属性:
value:缓存名称(必填),指定缓存的命名空间;
key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
unless:条件符合则不缓存;
condition:条件符合则缓存。
@CacheEvict
使用该注解的方法执行时会清空指定的缓存。一般使用在更新或删除方法上,可以设置如下属性:
value:缓存名称(必填),指定缓存的命名空间;
key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
condition:条件符合则缓存。
使用
springboot默认使用的是ConcurrentMapCacheManager作为缓存技术。如果想使用redis作为缓存,操作如下:
1. 引入pom:
<!--redis依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redis连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 修改application.yml:
- 使用了redis连接池Lettuce。
- Lettuce是一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用NettyNIO框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
spring:
redis:
host: 127.0.0.1 # Redis服务器地址
database: 0 # Redis数据库索引(默认为0)
port: 6379 # Redis服务器连接端口
password: # Redis服务器连接密码(默认为空)
timeout: 3000ms # 连接超时时间
lettuce:
pool:
max-active: 8 # 连接池最大连接数
max-idle: 8 # 连接池最大空闲连接数
min-idle: 0 # 连接池最小空闲连接数
max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制
3. 添加RedisConfig配置,记得添加@Primary,@EnableCaching。
- 启动类上的@EnableCaching可以去掉。
- 因为注入了多个cacheManaager,需要在默认的管理器方法上加上@Primary注解。不然,会出现异常:
No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* redis数据库自定义key
*/
public static final String REDIS_KEY_DATABASE="mall";
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
//创建JSON序列化器
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
return serializer;
}
@Bean
@Primary
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置Redis缓存有效期为1天
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
}
这样就配置好了。
4. 测试代码使用:
Service:
public interface PmsBrandService {
int create(PmsBrand brand);
int update(Long id, PmsBrand brand);
int delete(Long id);
PmsBrand getItem(Long id);
List<PmsBrand> list(Integer pageNum, Integer pageSize);
List<PmsBrand> ListAll();
}
@Service
public class PmsBrandServiceImpl implements PmsBrandService {
@Autowired
private PmsBrandMapper brandMapper;
@Override
public int create(PmsBrand brand) {
return brandMapper.insertSelective(brand);
}
@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
@Override
public int update(Long id, PmsBrand brand) {
brand.setId(id);
return brandMapper.updateByPrimaryKeySelective(brand);
}
@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
@Override
public int delete(Long id) {
return brandMapper.deleteByPrimaryKey(id);
}
@Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")
@Override
public PmsBrand getItem(Long id) {
return brandMapper.selectByPrimaryKey(id);
}
@Override
public List<PmsBrand> list(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
return brandMapper.selectByExample(new PmsBrandExample());
}
@Override
public List<PmsBrand> ListAll() {
return brandMapper.selectByExample(new PmsBrandExample());
}
}
Controller:
Api(tags = "PmsBrandController", description = "商品品牌管理")
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService brandService;
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@ApiOperation("添加品牌")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@RequestBody PmsBrand pmsBrand) {
CommonResult commonResult;
int count = brandService.create(pmsBrand);
if (count == 1) {
commonResult = CommonResult.success(pmsBrand);
LOGGER.debug("create success:{}", pmsBrand);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("create failed:{}", pmsBrand);
}
return commonResult;
}
@ApiOperation("更新指定id品牌信息")
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
CommonResult commonResult;
int count = brandService.update(id, pmsBrandDto);
if (count == 1) {
commonResult = CommonResult.success(pmsBrandDto);
LOGGER.debug("update success:{}", pmsBrandDto);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("update failed:{}", pmsBrandDto);
}
return commonResult;
}
@ApiOperation("删除指定id的品牌")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult delete(@PathVariable("id") Long id) {
int count = brandService.delete(id);
if (count == 1) {
LOGGER.debug("delete success :id={}", id);
return CommonResult.success(null);
} else {
LOGGER.debug("delete failed :id={}", id);
return CommonResult.failed("操作失败");
}
}
@ApiOperation("获取指定id的品牌详情")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<PmsBrand> getItem(@PathVariable("id") Long id) {
return CommonResult.success(brandService.getItem(id));
}
@ApiOperation("分页查询品牌列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<PmsBrand>> list(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("页码") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每页数量") Integer pageSize) {
List<PmsBrand> brandList = brandService.list(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
@ApiOperation("获取所有品牌列表")
@RequestMapping(value = "listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult<List<PmsBrand>> getAll() {
return CommonResult.success(brandService.ListAll());
}
}
结果
如下:数据就缓存到了配置的redis里面。
我们在配置类中,默认给缓存1天的时间,自己可以按需求设置。