Spingboot整合Redis,用注解(@Cacheable@CacheEvict@CachePut@Caching)管理缓存
Posted 北海派
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spingboot整合Redis,用注解(@Cacheable@CacheEvict@CachePut@Caching)管理缓存相关的知识,希望对你有一定的参考价值。
背景:项目从头开始,需结合Springboot和Redis
需求:用注解管理缓存
方法:
一、用Redis取代Springboot原有缓存
1、pom引入依赖
2、application.yml配置
3、启动类开启注解
4、RedisConfig配置
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.interceptor.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.cache.CacheManager; import org.springframework.data.redis.cache.RedisCacheManager; import javax.annotation.Resource; @Configuration public class RedisConfig extends CachingConfigurerSupport { @Resource private RedisConnectionFactory factory; /** * 自定义生成redis-key * * @return */ @Override @Bean public KeyGenerator keyGenerator() { return (o, method, objects) -> { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()).append("."); sb.replace(0,21,""); sb.append(method.getName()).append("."); for (Object obj : objects) { if (null!=obj){ sb.append(obj.toString()); } } System.out.println("keyGenerator=" + sb.toString()); return sb.toString(); }; } /* * 要启用spring缓存支持,需创建一个 CacheManager的 bean,CacheManager 接口有很多实现,这里Redis 的集成,用 * RedisCacheManager这个实现类 Redis 不是应用的共享内存,它只是一个内存服务器,就像 mysql 似的, * 我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 * RedisTemplate, 这些都是 Redis 缓存所必需的配置,把它们都放在自定义的 CachingConfigurerSupport 中 */ @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheManager rm = RedisCacheManager.create(connectionFactory); /*rm.setDefaultExpiration(30L);// 设置缓存时间*/ return rm; } /*@Bean @Override public CacheResolver cacheResolver() { return new SimpleCacheResolver(cacheManager()); }*/ @Bean @Override public CacheErrorHandler errorHandler() { // 用于捕获从Cache中进行CRUD时的异常的回调处理器。 return new SimpleCacheErrorHandler(); } // 1.项目启动时此方法先被注册成bean被spring管理,如果没有这个bean,则redis可视化工具中的中文内容(key或者value)都会以二进制存储,不易检查。 @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
二、用注解管理缓存(注意实体类序列化)
package com.hztech.framework.dict.ctl; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.plugins.Page; import com.hztech.framework.cache.DictCache; import com.hztech.framework.core.BaseController; import com.hztech.framework.dict.entity.Dict; import com.hztech.framework.dict.entity.DictItem; import com.hztech.framework.dict.service.DictItemService; import com.hztech.framework.dict.service.DictService; import com.hztech.framework.dict.vo.ItemListResp; import com.hztech.framework.dict.vo.ItemResp; import com.hztech.framework.util.CommonConst; import com.hztech.framework.util.RedisUtil; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * 字典及字典项管理接口 */ @RestController @RequestMapping(path = "接口路径") @Api(tags = "字典及字典项管理") public class DictItemCtl extends BaseController { public DictItemCtl() { setLog(DictItemCtl.class); cols.put("code", "c_code"); cols.put("name", "c_name"); cols.put("seq","n_seq"); } @Autowired private DictService dictService; @Autowired private DictItemService itemService; @Autowired private RedisUtil redisUtil; @ApiOperation("添加字典") @ApiImplicitParam(name = "dict",value = "字典",dataType = "Dict",paramType = "body",required = true) @PostMapping(name = "添加字典") @Transactional @Caching(evict = {@CacheEvict(value = "dict:list",allEntries = true)},put = {@CachePut(value = "dict",key = "#result")} ) public String add(@RequestBody @Valid Dict dict) { log.info("添加字典【{}】", dict.getName()); dictService.insert(dict); /*if (!redisUtil.set("dict:"+dict.getCode(),dict)){ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }*/ return dict.getId(); } @ApiOperation("删除字典") @ApiImplicitParam(name = "id",value = "字典id",dataType = "String",paramType = "path",required = true) @DeleteMapping(path = "{id}", name = "删除字典") @Transactional @Caching( evict = {@CacheEvict(value = "dict",key = "#id"),@CacheEvict(value = "dict:list",allEntries = true)} ) public void delete(@PathVariable(name = "id") String id) { Dict dict = dictService.selectById(id); if (dict == null) { return; } log.info("删除字典【{}】", dict.getName()); dictService.deleteById(id); EntityWrapper<DictItem> ew = new EntityWrapper<>(); ew.eq("c_dict_id", id); //需要同时删除下面包含的字典项 itemService.delete(ew); /*//采用事件通知方式更新缓存 redisUtil.delete("dict:"+dict.getCode()); redisUtil.deleteStart("item:"+dict.getId()+"#");*/ } @ApiOperation("更新字典") @ApiImplicitParams({ @ApiImplicitParam(name = "id",value = "字典id",dataType = "String",paramType = "path",required = true), @ApiImplicitParam(name = "dict",value = "字典",dataType = "Dict",paramType = "body",required = true) }) @Transactional @PutMapping(path = "{id}", name = "更新字典") @CachePut(value = "dict",key = "#id") @Caching( evict = {@CacheEvict(value = "dict:list",allEntries = true)}, put = {@CachePut(value = "dict",key = "#id")} ) public void update(@PathVariable String id, @RequestBody @Valid Dict dict) { Dict old = dictService.selectById(id); if (old == null) { return; } log.info("更新字典【{}】", old.getName()); dictService.updateById(dict); /*//采用事件通知方式更新缓存 if(!redisUtil.set("dict:"+dict.getCode(),dict)){ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }*/ } @ApiOperation("查询字典列表") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNo",value = "当前页(默认第一页)",dataType = "Integer",paramType = "header",required = true), @ApiImplicitParam(name = "pageSize",value = "每页显示条数(默认15条)",dataType = "Integer",paramType = "header",required = true), @ApiImplicitParam(name = "codeOrName",value = "编号或者名称",dataType = "String",paramType = "query"), @ApiImplicitParam(name = "sort",value = "排序方式",dataType = "String",paramType = "query"), @ApiImplicitParam(name = "asc",value = "升序(默认升序)",dataType = "boolean",paramType = "query"), }) @GetMapping(name = "查询字典列表") @Cacheable(value = "dict:list") public List<Dict> query(@RequestHeader(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestHeader(name = "pageSize", defaultValue = "15") Integer pageSize, @RequestParam(name = "codeOrName", required = false) String codeOrName, @RequestParam(name = "sort", defaultValue = "code", required = false) String sort, @RequestParam(name = "asc", defaultValue = "true", required = false) boolean asc) { log.info("查询字典列表"); EntityWrapper<Dict> ew = new EntityWrapper<>(); if (!StringUtils.isEmpty(codeOrName)) { ew.like("c_code", codeOrName); ew.or(); ew.like("c_name", codeOrName); } ew.orderBy(cols.get(sort), asc); Page<Dict> page = new Page<>(pageNo, pageSize); page = dictService.selectPage(page, ew); log.info("总数:{}", page.getTotal()); response.setIntHeader(CommonConst.RECORD_TOTAL, (int) page.getTotal()); return page.getRecords(); } @ApiOperation("添加字典项") @ApiImplicitParam(name = "item",value = "字典项",dataType = "DictItem",paramType = "body",required = true) @PostMapping(path = "items", name = "添加字典项") @Transactional @Caching( evict = {@CacheEvict(value = "item:list",allEntries = true)}, put = {@CachePut(value = "item",key = "#result")} ) public String addItem(@RequestBody @Valid DictItem item) { itemService.insert(item); //采用事件通知方式更新缓存 /*if(!redisUtil.set("item:"+item.getDictId()+"#"+item.getCode(),item)){ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }*/ return item.getId(); } @ApiOperation("删除字典项") @ApiImplicitParam(name = "itemId",value = "删除字典项",dataType = "String",paramType = "path",required = true) @DeleteMapping(path = "items/{itemId}", name = "删除字典项") @Transactional @Caching( evict = {@CacheEvict(value = "item",key = "#itemId"),@CacheEvict(value = "item:list",allEntries = true)} ) public void deleteItem(@PathVariable(name = "itemId") String itemId) { DictItem dictItem=itemService.selectById(itemId); itemService.deleteById(itemId); //采用事件通知方式更新缓存 /*redisUtil.delete("item:"+dictItem.getDictId()+"#"+dictItem.getCode());*/ } @ApiOperation("更新字典项") @ApiImplicitParams({ @ApiImplicitParam(name = "id",value = "字典项id",dataType = "String",paramType = "path",required = true), @ApiImplicitParam(name = "item",value = "字典项",dataType = "DictItem",paramType = "body",required = true) }) @PutMapping(path = "items/{id}", name = "更新字典项") @Transactional @Caching( evict = {@CacheEvict(value = "item:list",allEntries = true)}, put = {@CachePut(value = "item",key = "#id")} ) public String updateItem(@PathVariable(name = "id") String id,@RequestBody @Valid DictItem item) { item.setId(id); DictItem dictItem=itemService.selectById(id); itemService.updateById(item); //采用事件通知方式更新缓存 /*if(!redisUtil.set("item:"+dictItem.getDictId()+"#"+dictItem.getCode(),item)){ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }*/ return item.getId(); } @ApiOperation("查询字典项") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNo",value = "当前页(默认第一页)",dataType = "Integer",paramType = "header",required = true), @ApiImplicitParam(name = "pageSize",value = "每页显示条数(默认15条)",dataType = "Integer",paramType = "header",required = true), @ApiImplicitParam(name = "dictId",value = "字典项id",dataType = "String",paramType = "query",required = true), @ApiImplicitParam(name = "codeOrName",value = "编号或名称",dataType = "String",paramType = "query"), @ApiImplicitParam(name = "sort",value = "排序方式(默认seq)",dataType = "String",paramType = "query"), @ApiImplicitParam(name = "asc",value = "(默认升序)",dataType = "boolean",paramType = "query") }) @GetMapping(path = "items", name = "查询字典项") @Cacheable(value = "item:list") public List<DictItem> queryItem(@RequestHeader(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestHeader(name = "pageSize", defaultValue = "15") Integer pageSize, @RequestParam(name = "dictId") String dictId, @RequestParam(name = "codeOrName", required = false) String codeOrName, @RequestParam(name = "sort", defaultValue = "seq", required = false) String sort, @RequestParam(name = "asc", defaultValue = "true", required = false) boolean asc) { log.info("查询字典项列表"); EntityWrapper<DictItem> ew = new EntityWrapper<>(); ew.eq("c_dict_id",dictId); if (!StringUtils.isEmpty(codeOrName)) { ew.like("c_code", codeOrName); ew.or(); ew.like("c_name", codeOrName); } ew.orderBy(cols.get(sort), asc); Page<DictItem> page = new Page<>(pageNo, pageSize); page = itemService.selectPage(page, ew); log.info("总数:{}", page.getTotal()); response.setIntHeader(CommonConst.RECORD_TOTAL, (int) page.getTotal()); return page.getRecords(); } /** * 此方法用于页面填充字典项,其内容从缓存中提取。 * * @param dictCode * @return */ @ApiOperation("下拉列表获取字典项") @ApiImplicitParam(name = "dictCode",value = "字典项名称",dataType = "String",paramType = "path",required = true) @GetMapping(path = "common/items/{dictCode}",name = "下拉列表获取字典项") @Cacheable(value = "item:list") public ItemListResp getDictItems(@PathVariable String dictCode){ ItemListResp resp=new ItemListResp(); //List<DictItem> items=DictCache.getItems(dictCode); Set<String> set = redisUtil.keysStart("item:"+((Dict)redisUtil.get("dict:"+dictCode)).getId()+"#"); if(set.isEmpty()){ return resp; } for (String k:set){ DictItem item = (DictItem) redisUtil.get(k); ItemResp ir=new ItemResp(); ir.setCode(item.getCode()); ir.setName(item.getName()); resp.getItems().add(ir); if (item.getSelected() == 1 && StringUtils.isEmpty(resp.getDefaultValue())) { // 设置默认选中项 resp.setDefaultValue(item.getCode()); } } /*for(DictItem item : items){ ItemResp ir=new ItemResp(); ir.setCode(item.getCode()); ir.setName(item.getName()); resp.getItems().add(ir); if (item.getSelected() == 1 && StringUtils.isEmpty(resp.getDefaultValue())) { // 设置默认选中项 resp.setDefaultValue(item.getCode()); } }*/ return resp; } }
以上是关于Spingboot整合Redis,用注解(@Cacheable@CacheEvict@CachePut@Caching)管理缓存的主要内容,如果未能解决你的问题,请参考以下文章