SpringBoot 整合缓存Cacheable实战详细使用
https://www.jianshu.com/p/075da3070186
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
import java.time.Duration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
/**
*
* @author guokaige
*
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
public static final int EXPIRE_SECONDS = 60 * 60 * 3;
public static final int MY_EXPIRE_SECONDS = 60 * 10;
@Bean
@Override
public KeyGenerator keyGenerator() {
return new RedisKeyGenerator();
}
@Bean
@Primary
public CacheManager cacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory),
// 默认策略,未配置的 key 会使用这个
this.getRedisCacheConfigurationWithTtl(RedisConfig.EXPIRE_SECONDS));
}
/**
* 设置缓存过期时间
*
* @param seconds
* @return
*/
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(getSerializer());
template.setValueSerializer(getSerializer());
template.setHashKeySerializer(getSerializer());
template.setHashValueSerializer(getSerializer());
template.afterPropertiesSet();
return template;
}
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
private Jackson2JsonRedisSerializer getSerializer() {
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModule(new JavaTimeModule());
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
}
}
import org.springframework.cache.interceptor.KeyGenerator;
/**
*
* @author guokaige
*
*/
public class RedisKeyGenerator implements 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();
}
}
@Cacheable
@Cacheable 能够根据方法的请求参数对其结果进行缓存.
value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。
@Cacheable(value="findTree") //缓存名叫 findTree
public List<OrganZtreeNodes> findTree(String orgName) {
List<Organ> list= organDao.findAll();
return list;
}
@Cacheable(value="findTree", key="#orgName") //缓存名叫 findTree
public List<OrganZtreeNodes> findTree(String orgName) {
List<Organ> list= organDao.findAll();
return list;
}
@CachePut
@CachePut 这个注释可以确保方法在执行前不会去检查缓存中是否存在,而是每次都会执行该方法,并将执行结果覆盖到指定的缓存。
@CachePut(value="accountCache",key="#account.getName()")// 更新accountCache 缓存中当前用户数据
public Account updateAccount(Account account) {
return updateDB(account);
}
@CacheEvict
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict(value="accountCache",key="#account.getName()")// 更新accountCache 当前用户缓存
public void updateAccount(Account account) {
updateDB(account);
}
@CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 所有缓存
public void reload() {
reloadAll()
}
@Cacheable(value="accountCache",condition="#userName.length() <=4") // 缓存accountCache 用户名长度小于4的数据更新
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}
@Caching
@Caching注解来讲,如果有两种不同的需求,都是放在同一个方法上,这种需求如果只是使用@CacheEvict或者@CachePut是无法实现,因为他们不能多样化的作用在同一个方法上。可以使用
@Caching(evict = { @CacheEvict(value = "cache1", allEntries = true),
@CacheEvict(value = "cache2", allEntries = true),
@CacheEvict(value = "cache3", allEntries = true)} )
以上可以批量删除一些缓存
模糊匹配删除
如果批量删除的cache比较多, 就需要我们统一规划缓存vaule和key
根据经验来说:
1.缓存value 首先需指明是哪个entity的缓存,末尾拼接Cache,
userListCache
2.然后指明范围
_allEnable
3.参数
_userName=张三
4.最后指明用途,或者方法名
_searchUser
userListCache_allEnable_userName=张三_searchUser
@Cacheable(value = "userListCache", key = "'_allEnable_userName='+#userName+'_searchUser' ")
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRemove {
/**
* 需要清除的大类 例如 autocms 所有缓存
*
* @return
*/
String value() default "";
/**
* 需要清除的具体的额类型
*
* @return
*/
String[] key() default {};
}
@Component
@Aspect
public class CacheRemoveAspect {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource(name = "redisTemplate")
RedisTemplate<String, String> redisTemplate;
//截获标有@CacheRemove的方法
@Pointcut(value = "(execution(* *.*(..)) && @annotation(com.topsoft.aaf.common.cache.CacheRemove))")
private void pointcut() {
}
/**
* 正则匹配删除缓存
* @param joinPoint
*/
@AfterReturning(value = "pointcut()")
private void process(JoinPoint joinPoint) {
//获取被代理的类
Object target = joinPoint.getTarget();
//获取切入方法的数据
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入方法
Method method = signature.getMethod();
//获得注解
CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);
if (cacheRemove != null) {
//清除当前类的缓存
cleanRedisCache("*" + target.getClass().toString() + "*");
String value = cacheRemove.value();
if (!value.equals("")) {
//缓存的项目所有redis业务部缓存
cleanRedisCache("*" + value + "*");
}
//需要移除的正则key
String[] keys = cacheRemove.key();
for (String key : keys) {
//指定清除的key的缓存
cleanRedisCache("*" + key + "*");
}
}
}
private void cleanRedisCache(String key) {
if (key != null) {
Set<String> stringSet = redisTemplate.keys(key);
redisTemplate.delete(stringSet);//删除缓存
logger.info("清除 " + key + " 缓存");
}
}
}
参考Spring缓存注解@Cacheable、@CacheEvict、@CachePut使用 - fashflying - 博客园