第十三章:SpringBoot2.3.0 @Cacheable和@CacheEvict声明式注释缓存,自定义KeyGenerator生成策略

一)基于声明式注释的缓存

@Cacheable:触发​​缓存填充。

@CacheEvict:触发​​缓存删除。

@CachePut:更新缓存,而不会干扰方法的执行。

@Caching:重新组合要应用于一个方法的多个缓存操作。

@CacheConfig:在类级别共享一些与缓存相关的常见设置。

 

二)@Cacheable

含义:当调用该注解声明的方法时,会先从缓存中查找,判断是否有key相同缓存的数据,如果有,就直接返回数据,如果没有,执行方法,然后把返回的数据以键值的方式存储到缓存中,方便下次同样参数请求时,直接从缓存中返回数据。

 

@Cacheable支持如下几个参数:

cacheNames:缓存位置的一段名称,不能为空,和value一个含义。

value:缓存位置的一段名称,不能为空,和cacheNames一个含义。

key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL。

keyGenerator:指定key的生成策略。

condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL。

 

默认秘钥生成

由于缓存本质上是键值存储,因此每次调用缓存方法都需要转换为适合缓存访问的键。

缓存抽象使用KeyGenerator基于以下算法的简单方法:

1、如果没有给出参数,则返回SimpleKey.EMPTY

2、如果仅给出一个参数,则返回该实例。

3、如果给定多个参数,则返回SimpleKey包含所有参数的参数。

只要参数具有自然键并实现有效hashCode()equals()方法,此方法就适用于大多数用例。

使用方式

// 可定义多个key
@Cacheable(cacheNames = {"name1", "name2"})
public User listUsers(User user) {...}

 

自定义秘钥生成

当默认秘钥生成方式不满足需求时,可自定义秘钥生成方式。要提供其他默认密钥生成器,您需要实现org.springframework.cache.interceptor.KeyGenerator接口。

使用方式

@Cacheable(cacheNames = "user", key = "#user")
public User listUsers(User user, boolean isBool) {...}

@Cacheable(cacheNames = "user", key = "#user.id")
public User listUsers(User user, boolean isBool) {...}

@Cacheable(cacheNames = "user", keyGenerator = "myKeyGenerator")
public User listUsers(User user, boolean isBool) {...}

自定义KeyGenerator:

/**
 * 自定义Redis Key生产策略
 * 在使用是, 指定@Cacheable(cacheNames = "keyName", keyGenerator = "myKeyGenerator")
 */
@Bean(name = "myKeyGenerator")
public KeyGenerator keyGenerator() {
    return new KeyGenerator() {
        @Override
        public Object generate(Object target, Method method, Object... params) {
            StringBuilder builder = new StringBuilder();
            builder.append(target.getClass().getName()); // 类名
            builder.append(method.getName()); // 方法名
            for (Object param : params) { // 参数
                builder.append(param);
            }
            return builder.toString();
        }
    };
}

 

自定义缓存解析

/**
 * 自定义缓存解析
 * @param connectionFactory
 * @return
 */
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
    RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
		
    // 设置全局Redis Key有效时间, 10分钟
    configuration = configuration.entryTtl(Duration.ofMinutes(10)).disableCachingNullValues();
		
    RedisCacheManager manager = new RedisCacheManager(writer, configuration);
    return manager;
}

 

三)@CachePut

含义:当需要在不影响方法执行的情况下更新缓存时使用。但不建议在同一方法上使用@CachePut@Cacheable注释。

@CachePut(cacheNames = "user", key = "#user")
public User updateUser(User user, boolean isBool) {...}

 

四)@CacheEvict

含义:当存在相同key的缓存时,把缓存清空,相当于删除。

 

@CacheEvict支持如下几个参数:

cacheNames:缓存位置的一段名称,不能为空,和value一个含义。

value:缓存位置的一段名称,不能为空,和cacheNames一个含义。

key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL。

condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL。

allEntries:true表示清除value中的全部缓存,默认为false。

 

使用方式

@CacheEvict(cacheNames = "user", allEntries = true)
public User loadUsers(User user, boolean isBool) {...}

 

五)@Caching

含义:因为条件或键表达式在不同的缓存之间不相同的时候,可能需要指定相同类型的多个注释(例如@CacheEvict或 @CachePut)。

使用方式

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public User importUsers(String deposit, Date date){}

 

六)@CacheConfig

含义:指定要用于该类的每个高速缓存操作的高速缓存的名称可以由单个类级定义代替。即该方式属于类级别操作。

@CacheConfig("users") 
public class UserRepositoryImpl implements UserRepository {

    @Cacheable
    public User listUsers(User user) {...}
}

 

七)启用缓存注释

@Configuration
@EnableCaching
public class RedisConfig {
}

 

八)声明式注解缓存案例

RedisConfig工具类:

package com.oysept.config;

import java.lang.reflect.Method;
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.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.RedisSerializer;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 自定义Redis Key生产策略
     */
    @Bean(name = "myKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder builder = new StringBuilder();
                builder.append(target.getClass().getName());
                builder.append(method.getName());
                for (Object param : params) {
                    builder.append(param);
                }
                return builder.toString();
            }
        };
    }
	
    /**
     * 自定义缓存解析
     * @param connectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
		
        // 设置全局Redis Key有效时间, 10分钟
        configuration = configuration.entryTtl(Duration.ofMinutes(10)).disableCachingNullValues();
		
        RedisCacheManager manager = new RedisCacheManager(writer, configuration);
        return manager;
    }
	
    /**
     * 自定义Redis key value序列化方式
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(connectionFactory);
		
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(RedisSerializer.string());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

缓存测试:

package com.oysept.test;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;

public class RdisCacheTest {

    /**
     * 缓存填充, 缓存redis key生成规则: conf:redis:类名方法名参数hashcode
     * 注意: @Cacheable注解生成的类型在redis中默认都是string
     *      在每次请求的时候,都是先根据key到redis查询是否存在,如不存在则执行方法中的代码
     */
    @Cacheable(cacheNames = "redis", keyGenerator = "myKeyGenerator")
    public String getRedisString(String param1, String param2) {
        return param1+":"+param2;
    }

    /**
     * 清除缓存
     */
    @CacheEvict(cacheNames = "redis", allEntries = true)
    public String cleanCache() {
        return "success";
    }

    @Caching(evict = {@CacheEvict(cacheNames = "redis", allEntries = true)})
    public String cleanCaching() {
        return "success";
    }
}

 

识别二维码关注个人微信公众号

本章完结,待续,欢迎转载!
 
本文说明:该文章属于原创,如需转载,请标明文章转载来源!

你可能感兴趣的:(SpringBoot2.3.0)