@Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。
基本的概念可以查看这篇文档,本次是因为用到了Cacheable注解,记录一下使用过程
开启基于注解的缓存,使用 @EnableCaching 标注在 springboot 主启动类上
package com.insigma;import com.insigma.common.security.annotation.EnableCustomConfig;import com.insigma.common.security.annotation.EnableRyFeignClients;import com.spring4all.swagger.EnableSwagger2Doc;import org.springframework.boot.SpringApplication;import org.springframework.cloud.client.SpringCloudApplication;@SpringCloudApplication@EnableSwagger2Doc@EnableRyFeignClients@EnableCustomConfigpublic class AppSysDict {public static void main(String[] args) {SpringApplication.run(AppSysDict.class,args);System.out.println("字典服务启动成功");}}
package com.insigma.common.security.annotation;import com.insigma.common.security.config.ApplicationConfig;import com.insigma.common.security.feign.FeignAutoConfiguration;import org.mybatis.spring.annotation.MapperScan;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.EnableAspectJAutoProxy;import org.springframework.context.annotation.Import;import org.springframework.scheduling.annotation.EnableAsync;import java.lang.annotation.*;/*** @author Jiangchao* @description: TODO* @date 2021/9/29 14:43*/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited// 表示通过aop框架暴露该代理对象,AopContext能够访问@EnableAspectJAutoProxy(exposeProxy = true)// 指定要扫描的Mapper类的包的路径@MapperScan("com.insigma.**.mapper")// 开启线程异步执行@EnableAsync//开启缓存@EnableCaching// 自动加载类@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })public @interface EnableCustomConfig {}
标注缓存注解
最好标注在实现类上,标注在接口上会有些问题,具体问题没细究,测试的时候好像会跟其他注解发送冲突,可以看看这篇文章
@Override
@Cacheable(value = DictConstant.SYS_DICT_CACHE, key = "#sysDictInDTO.dictCode+':'+#sysDictInDTO.dictValue", unless = "#result == null")
public SysDictOutDTO queryDictByCodeAndValue(SysDictInDTO sysDictInDTO) {
SysDictOutDTO sysDictOutDTO = sysDictMapper.queryDictByCodeAndValue(sysDictInDTO);
if (sysDictOutDTO == null){
return null;
}
sysDictOutDTO.setDictCode(sysDictInDTO.getDictCode());
return sysDictOutDTO;
}


还要声明一个cacheManager,数据缓存在哪
package com.insigma.common.redis.configure;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
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.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
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.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes", "deprecation" })
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
RedisCacheConfiguration config =
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(strSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(jacksonSeial))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager
.builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}
}
注意点
由于注解是由spring aop实现的,所以在类本身内部调用的时候,如果直接调用方法,Cacheable注解是不生效的,需要先把自己注入进来
package com.insigma.sysdict.service.api.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.insigma.common.core.constants.DictConstant;
import com.insigma.common.core.domain.BaseResponse;
import com.insigma.common.core.utils.StringUtils;
import com.insigma.common.core.utils.bean.BeanUtils;
import com.insigma.common.redis.service.RedisService;
import com.insigma.sysdict.dto.input.SysDictInDTO;
import com.insigma.sysdict.dto.output.DictOutDTO;
import com.insigma.sysdict.dto.output.SysDictOutDTO;
import com.insigma.sysdict.entity.SysDict;
import com.insigma.sysdict.entity.SysDictItem;
import com.insigma.sysdict.mapper.SysDictItemMapper;
import com.insigma.sysdict.mapper.SysDictMapper;
import com.insigma.sysdict.service.api.SysDictService;
import com.insigma.sysdict.strategy.context.SysDictStrategyContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class SysDictServiceImpl implements SysDictService {
@Resource
private SysDictMapper sysDictMapper;
@Resource
private SysDictService sysDictService;
@Override
@Cacheable(value = DictConstant.SYS_DICT_CACHE, key = "#sysDictInDTO.dictCode+':'+#sysDictInDTO.dictValue", unless = "#result == null")
public SysDictOutDTO queryDictByCodeAndValue(SysDictInDTO sysDictInDTO) {
SysDictOutDTO sysDictOutDTO = sysDictMapper.queryDictByCodeAndValue(sysDictInDTO);
if (sysDictOutDTO == null){
return null;
}
sysDictOutDTO.setDictCode(sysDictInDTO.getDictCode());
return sysDictOutDTO;
}
@Override
public List<SysDictOutDTO> queryDictListByCodeAndValue(List<SysDictInDTO> sysDictInDTOList) {
if (CollectionUtils.isEmpty(sysDictInDTOList)){
return null;
}
List<SysDictOutDTO> sysDictOutDTOList = new ArrayList<>();
for (SysDictInDTO sysDictInDTO : sysDictInDTOList) {
// 先查询redis
String key = DictConstant.SYS_DICT_CACHE + "::" + sysDictInDTO.getDictCode() + ":" + sysDictInDTO.getDictValue();
Object cacheObject = redisService.getCacheObject(key);
SysDictOutDTO sysDictOutDTO;
List list = new ArrayList();
if (cacheObject == null){
// 查询数据库
// 调用自身方法,并且Cacheable注解还能生效
sysDictOutDTO = sysDictService.queryDictByCodeAndValue(sysDictInDTO);
if (sysDictOutDTO == null){
continue;
}
}else {
list = JSONObject.parseArray(JSON.toJSONString(cacheObject));
if (CollectionUtils.isNotEmpty(list) && list.size() > 1){
sysDictOutDTO = JSONObject.parseObject(JSON.toJSONString(list.get(1)), SysDictOutDTO.class);
}else {
continue;
}
}
sysDictOutDTOList.add(sysDictOutDTO);
}
return sysDictOutDTOList;
}
}
