Skip to content
xiaolyuh123 edited this page Aug 2, 2024 · 42 revisions

注解说明

@Cacheable

表示该方法的结果是可以被缓存的,当该方法被调用时先检查缓存是否命中,如果没有命中再调用被缓存的方法,并将其返回值放到缓存中。

名称 默认值 说明
value 空字符串数组 缓存名称,cacheNames的别名
cacheNames 空字符串数组 缓存名称
key 空字符串 缓存key,支持SpEL表达式
depict 空字符串 缓存描述(在缓存统计页面会用到)
cacheMode CacheMode.ALL 缓存模式(只使用一级缓存或者二级缓存)
firstCache 一级缓存配置
secondaryCache 二级缓存配置

@BatchCacheable

表示该方法的结果是可以被批量缓存的,当该方法被调用时先检查单条key缓存是否命中,如果命中直接走缓存,剩下的没有命中的key再调用被缓存的方法,并将其返回值批量放到缓存中。

特别提醒:

  • 方法返回值必须是List结构。
  • keys的EL表达式必须能同时应用与参数和返回值,比如keys = "#users.![userId]"中的id需要保证参数和返回值同时都能取到值。
名称 默认值 说明
value 空字符串数组 缓存名称,cacheNames的别名
cacheNames 空字符串数组 缓存名称
keys 必填,无默认值 缓存keys,需要是SpEL表达式。这个表达式会用于从参数和返回值中获取数据对应缓存keys
depict 空字符串 缓存描述(在缓存统计页面会用到)
cacheMode CacheMode.ALL 缓存模式(只使用一级缓存或者二级缓存)
firstCache 一级缓存配置
secondaryCache 二级缓存配置

@FirstCache

一级缓存配置项

名称 默认值 说明
initialCapacity 10 缓存初始Size
maximumSize 5000 缓存最大Size
expireTime 9 缓存有效时间
timeUnit TimeUnit.MINUTES 时间单位,默认分钟
expireMode ExpireMode.WRITE 缓存失效模式,ExpireMode.WRITE:最后一次写入后到期失效,ExpireMode.ACCESS:最后一次访问后到期失效

@SecondaryCache

二级缓存配置项

名称 默认值 说明
expireTime 5 缓存有效时间
preloadTime 1 缓存主动在失效前强制刷新缓存的时间,建议是 expireTime * 0.2
timeUnit TimeUnit.HOURS 时间单位,默认小时
forceRefresh false 是否强制刷新(直接执行被缓存方法)
magnification 1 非空值和null值之间的时间倍率,默认是1。如expireTime=60秒,magnification=10,那么当缓存空值时,空值的缓存过期时间是60/10=6秒。

@CachePut

将数据放到缓存中

名称 默认值 说明
value 空字符串数组 缓存名称,cacheNames的别名
cacheNames 空字符串数组 缓存名称
key 空字符串 缓存key,支持SpEL表达式
depict 空字符串 缓存描述(在缓存统计页面会用到)
cacheMode CacheMode.ALL 缓存模式(只使用一级缓存或者二级缓存)
firstCache 一级缓存配置
secondaryCache 二级缓存配置

@CacheEvict

删除缓存

名称 默认值 说明
value 空字符串数组 缓存名称,cacheNames的别名
cacheNames 空字符串数组 缓存名称
key 空字符串 缓存key,支持SpEL表达式
allEntries false 是否删除缓存中所有数据,默认情况下是只删除关联key的缓存数据,当该参数设置成 true 时 key 参数将无效
cacheMode CacheMode.ALL 缓存模式(只使用一级缓存或者二级缓存)
async false 是否异步删除缓存中所有数据

@Caching

同时使用多个缓存注解

名称 默认值 说明
cacheable Cacheable数组 Cacheable注解
put CachePut数组 CachePut注解
evict CacheEvict数组 CacheEvict注解

快速开始

集成 Spring Boot

  1. 引入layering-cache
<dependency>
    <groupId>com.github.xiaolyuh</groupId>
    <artifactId>layering-cache-starter</artifactId>
    <version>${layering.version}</version>
</dependency>
  1. 添加配置
server.port=8082

#layering-cache 配置
layering-cache.stats=true
# 缓存命名空间,如果不配置取 "spring.application.name"
layering-cache.namespace=layering-cache-web

layering-cache.redis.database=0
layering-cache.redis.timeout=60
layering-cache.redis.password=
# redis单机
#layering-cache.redis.host=127.0.0.1
#layering-cache.redis.port=6378
# redis集群
#layering-cache.redis.cluster=127.0.0.1:6379,127.0.0.1:6378
# redis sentinel
layering-cache.redis.sentinel-nodes=127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
layering-cache.redis.sentinel-master=mymaster

# 设置redis值的序列化方式,默认是kryo
#com.github.xiaolyuh.redis.serializer.KryoRedisSerializer
#com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer
#com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer
#com.github.xiaolyuh.redis.serializer.JdkRedisSerializer
#com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer
layering-cache.redis.serializer=com.github.xiaolyuh.redis.serializer.KryoRedisSerializer
  1. 配置类中添加注解@EnableLayeringCache启用layering-cache
@SpringBootApplication
@EnableLayeringCache
public class LayeringCacheStartDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LayeringCacheStartDemoApplication.class, args);
    }
}

集成 Spring

  1. 引入layering-cache
  • maven 方式
<dependency>
    <groupId>com.github.xiaolyuh</groupId>
    <artifactId>layering-cache-aspectj</artifactId>
    <version>${layering.version}</version>
</dependency>
  • gradle 方式
compile 'com.github.xiaolyuh:layering-cache:${layering.version}'
  1. RedisClient(可以参考layering-cache-aspectj的Test配置)
@Configuration
@PropertySource({"classpath:application.properties"})
public class RedisConfig {

    @Value("${spring.redis.database:0}")
    private int database;

    @Value("${spring.redis.host:192.168.83.128}")
    private String host;

    @Value("${spring.redis.password:}")
    private String password;

    @Value("${spring.redis.port:6378}")
    private int port;

    @Bean
    public RedisClient layeringCacheRedisClient() {
        RedisProperties redisProperties = new RedisProperties();
        redisProperties.setDatabase(database);
        redisProperties.setHost(host);
        redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
        redisProperties.setPort(port);

        KryoRedisSerializer<Object> kryoRedisSerializer = new KryoRedisSerializer<>(Object.class);
        StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
        SingleRedisClient redisClient = new SingleRedisClient(redisProperties);
        redisClient.setKeySerializer(keyRedisSerializer);
        redisClient.setValueSerializer(kryoRedisSerializer);
        return redisClient;
    }
}
  1. 声明CacheManager和LayeringAspect
/**
 * 多级缓存配置
 *
 * @author yuhao.wang3
 */
@Configuration
@Import({RedisConfig.class})
@EnableAspectJAutoProxy
public class CacheConfig {

    @Bean
    public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient, CacheStatsReportService cacheStatsReportService, LayeringCacheProperties layeringCacheProperties) {

        LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
        // 默认开启统计功能
        layeringCacheManager.setStats(layeringCacheProperties.isStats());
        // 上报缓存统计信息
        layeringCacheManager.setCacheStatsReportService(cacheStatsReportService);
        // 设置缓存命名空间
        GlobalConfig.setNamespace(StringUtils.isBlank(layeringCacheProperties.getNamespace()) ? applicationName : layeringCacheProperties.getNamespace());
        return layeringCacheManager;
    }

    @Bean
    @ConditionalOnMissingBean(CacheStatsReportService.class)
    public CacheStatsReportService cacheStatsReportService() {
        return new DefaultCacheStatsReportServiceImpl();
    }
}

使用

注解形式

直接在需要缓存的方法上加上Cacheable、BatchCacheable、CacheEvict、CachePut、Caching注解。

  • Cacheable注解
@Cacheable(value = "user:info", depict = "用户信息缓存", cacheMode = CacheMode.ALL,
		firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
		secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User getUser(User user) {
	logger.debug("调用方法获取用户名称");
	return user;
}
  • BatchCacheable注解

特别提醒:

  • 方法返回值必须是List结构。
  • keys的EL表达式必须能同时应用与参数和返回值,比如keys = "#users.![userId]"中的id需要保证参数和返回值同时都能取到值。
@BatchCacheable(value = "user:info", keys = "#users.![userId]", depict = "用户信息缓存",
    firstCache = @FirstCache(expireTime = 10, timeUnit = TimeUnit.SECONDS),
    secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3,timeUnit = TimeUnit.SECONDS))
public List<User> getUserByIds(List<User> users) {
    logger.debug("测试正常配置的批量缓存方法");

    ArrayList<User> res = new ArrayList<>();
    for (int i = 0; i < Math.min(users.size(),2) ; i++) {
        User user = new User();
        user.setUserId(users.get(i).getUserId());
        user.setAge(i);
        user.setLastName(new String[]{"w", "y", Integer.toString(i)});
        res.add(user);
    }
    return res;
}
  • CachePut注解
@CachePut(value = "user:info", key = "#userId", depict = "用户信息缓存", cacheMode = CacheMode.ALL,
		firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
		secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User putUser(long userId) {
	User user = new User();
	user.setUserId(userId);
	user.setAge(31);
	user.setLastName(new String[]{"w", "y", "h"});

	return user;
}
  • CacheEvict注解
@CacheEvict(value = "user:info", key = "#userId")
public void evictUser(long userId) {

}

@CacheEvict(value = "user:info", allEntries = true)
public void evictAllUser() {
}
  • Caching注解
@Caching(evict = {@CacheEvict(value = "user:info:caching:3-4-0", key = "'evict'+#userId"), @CacheEvict(value = "user:info:caching", allEntries = true)},
        put = {@CachePut(value = "user:info:caching:3-4-0", key = "'put'+#userId",
                secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, timeUnit = TimeUnit.SECONDS))},
        cacheable = {@Cacheable(value = "user:info:caching:3-4-0", key = "#userId",
                firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
                secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 7, timeUnit = TimeUnit.SECONDS))})
public User cachingAll(long userId) {
    return user;
}

直接使用API

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CacheConfig.class})
public class CacheCoreTest {
    private Logger logger = LoggerFactory.getLogger(CacheCoreTest.class);

    @Autowired
    private CacheManager cacheManager;

    @Test
    public void testCacheExpiration() {
        FirstCacheSetting firstCacheSetting = new FirstCacheSetting(10, 1000, 4, TimeUnit.SECONDS, ExpireMode.WRITE);
        SecondaryCacheSetting secondaryCacheSetting = new SecondaryCacheSetting(10, 4, TimeUnit.SECONDS, true);
        LayeringCacheSetting layeringCacheSetting = new LayeringCacheSetting(firstCacheSetting, secondaryCacheSetting);

        String cacheName = "cache:name";
        String cacheKey = "cache:key1";
        LayeringCache cache = (LayeringCache) cacheManager.getCache(cacheName, layeringCacheSetting);
        cache.get(cacheKey, () -> initCache(String.class));
        cache.put(cacheKey, "test");
        cache.evict(cacheKey);
        cache.clear();
    }

    private <T> T initCache(Class<T> t) {
        logger.debug("加载缓存");
        return (T) "test";
    }
}

使用API清除缓存

@Resource
private LayeringCacheManager layeringCacheManager;

@PostMapping("/clear-layering-cache")
@ApiOperation(value = "清空layering-cache缓存", httpMethod = "POST")
public Boolean clearLayeringCache(String cacheName) {
    LayeringCacheSetting defaultSetting = new LayeringCacheSetting(new FirstCacheSetting(), new SecondaryCacheSetting(), "默认缓存配置(删除时生成)", CacheMode.ALL);
    Cache cache = layeringCacheManager.getCache(cacheName, defaultSetting);
    cache.clear();
    return true;
}