springBoot集成caffeine,自定义缓存配置 CacheManager

目录

springboot集成caffeine

Maven依赖

配置信息:properties文件

config配置

使用案例

Caffeine定制化配置多个cachemanager

springboot集成redis并且定制化配置cachemanager


springboot集成caffeine

Caffeine是一种基于服务器内存的缓存库。它将数据存储在应用程序的内存中,以实现快速的数据访问和高性能。

由于Caffeine缓存在服务器内存中存储数据,因此它适用于需要快速读取和频繁访问的数据,比如一些热门数据、配置信息、常用查询的结果等。但是它不适用于大规模数据或需要持久化存储的数据,因为服务器内存有限,无法存储大量数据,并且缓存数据在应用程序重启后会丢失

Maven依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.2</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- caffeine服务器本地缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <!-- caffeine -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>2.8.1</version>
    </dependency>
    <!--   可以对自定义配置的信息进行提示 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
</dependencies>

配置信息:properties文件

/**
 * Caffeine Cache自定义配置属性类
 */
@Data   //lombok插件的  看自己需求是否引入
@Component
@ConfigurationProperties(prefix = "com.common.caffeine")  
//这个ConfigurationProperties是为了方便配置yaml文件,
//如果有需要的话可以在yaml文件中进行配置,需要与spring-boot-configuration-processor
//这个依赖搭配使用,
//然后再在resources目录下创建一个META-INF文件夹,该文件夹下存放一个配置文件提升信息(下面会贴出来的),
//这样的话在yaml文件中进行书写的时候就会有提升信息了
public class CaffeineCacheProperties {

    /**
     * 缓存初始容量  映射的话会很灵活  羊肉串写法与驼峰命名都可以和下面的属性进行映射
     * com.ifi.performance.common.caffeine.init-cache-capacity
     */
    private Integer initCacheCapacity = 256;

    /**
     * 缓存最大容量,超过之后会按照recently or very often(最近最少)策略进行缓存剔除
     * com.ifi.performance.common.caffeine.max-cache-capacity
     */
    private Long maxCacheCapacity = 10000L;

    /**
     * 是否允许空值null作为缓存的value
     * com.ifi.performance.common.caffeine.allow-null-value
     */
    private Boolean allowNullValue = Boolean.TRUE;

}

配置yaml文件提升信息:additional-spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "com.ifi.performance.common.caffeine.init-cache-capacity",
      "type": "java.lang.Integer",
      "description": "Caffeine缓存的初始容量",
      "defaultValue": 256
    },
    {
      "name": "com.ifi.performance.common.caffeine.max-cache-capacity",
      "type": "java.lang.Long",
      "description": "Caffeine缓存的最大容量",
      "defaultValue": "10000L"
    },
    {
      "name": "com.ifi.performance.common.caffeine.allow-null-value",
      "type": "java.lang.Boolean",
      "description": "Caffeine缓存是否允许null作为value的属性",
      "defaultValue": "true"
    }
  ]
}

这样在yaml文件中输入com.common.caffeine的时候就会有提示信息了。

config配置

/**
 * 服务端公用缓存常量类
 */
public interface CacheConstants {

    /**
     * 系统服务端公用缓存名称 默认缓存
     */
    String CACHE_NAME = "server_cache";
}       

编写config配置文件:CaffeineCacheConfig.java

@SpringBootConfiguration
@EnableCaching  //开启注解扫描
@Slf4j
public class CaffeineCacheConfig {

    //刚刚编写的properties配置文件
    @Autowired
    private CaffeineCacheProperties properties;


    /**
     * 默认的全局缓存管理器
     * @return
     */
    @Bean("defaultCacheManager")
    public CaffeineCacheManager defaultCacheManager() {
        CaffeineCacheManager defaultCacheManager = new CaffeineCacheManager(CacheConstants.CACHE_NAME); //指定这个缓存的命名,这里我是使用了一个常量类
        defaultCacheManager.setAllowNullValues(properties.getAllowNullValue());
        Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .initialCapacity(properties.getInitCacheCapacity())
                .maximumSize(properties.getMaxCacheCapacity()) //从配置文件中获取最大容量
                .expireAfterWrite(7, TimeUnit.DAYS); //过期时间
        defaultCacheManager.setCaffeine(caffeineBuilder);
        log.info("the caffeine cache manager is loaded successfully!");
        return defaultCacheManager;
    }
}

使用案例

这样配置好了之后就可以在controller、service中使用配置好的缓存了。

比如说我们要在service中进行使用,案例如下:

@Autowired
@Qualifier(value = "defaultCacheManager") //这里可以直接使用Autowired就行,Autowired默认是按照对象类型进行依赖注入,如果容器中有两个及以上的同一种类型的bean,那么就会报bean冲突的错误,后面的定制化配置会用到这个Autowired
private CaffeineCacheManager defaultCacheManager;

Cache cache = defaultCacheManager.getCache(CacheConstants.CACHE_NAME); //配置类中对这个cachemanager设置的缓存名称
//下面就可以通过cache操作数据:存入缓存或者是从缓存中获取数据、删除数据了。
//存入缓存的数据是key-value形式的,key最好是一个唯一的标识,不然可能会出现缓存覆盖的情况,value可以是一个字符串也可以是自定义的对象,这里我是自定义了一个对象,比如UserCache对象,这个对象中有我需要缓存的数据,比如userid等自己需要的数据
UserCache usercache = new UserCache();
usercache.setuserId = "123456"; //实际业务场景中,比如用户登录成功,可以把用户id设置进来,方便其他业务进行获取  LOGIN_INFO 常量为自定义的一个标识符,方便在查询辨别缓存数据
cache.put(userId+CacheConstants.LOGIN_INFO,usercache);

//特别注意:get缓存数据的时候,很大可能获取到的是null对象,因为比如有一些业务没有被触发从而导致没有填充你需要的数据到缓存,所以这里一定要加一个空指针判断!!!
UserCache usercache = cache.get( userId + CacheConstants.LOGIN_INFO, UserCache.class);
if(usercache == null){
    return null;
}

//清除缓存中指定的数据  通过key来清除缓存中的数据,比如用户退出登录了可以把他的一些缓存信息给清除
cache.evict(userId + CacheConstants.LOGIN_INFO);

因为我们使用的缓存管理接口是spring-cache的,spring-cache中的操作api中并没有提供设置数据缓存的过期时间的,所以如果我们有这种业务需要(比如验证码信息需要5分钟才过期,用户登录信息30天才过期),那么就需要定制化配置cachemanager了,这样从源头上解决问题。

Caffeine定制化配置多个cachemanager

依赖与上面一致,只需要修改CaffeineCacheConfig类与使用方式就行。

@SpringBootConfiguration
@EnableCaching  //开启注解扫描
@Slf4j
public class CaffeineCacheConfig {

    @Autowired
    private CaffeineCacheProperties properties;


    /**
     * 默认的全局缓存管理器
     * @return
     */
    @Primary  //必须要加这个注解,用于标识一个Bean(组件)是首选的候选者。当有多个同类型的Bean(组件)时,使用了@Primary注解的Bean将会成为默认选择,如果没有其他限定符(如@Qualifier)指定具体要使用的Bean,则会优先选择带有@Primary注解的Bean。
    @Bean("defaultCacheManager")
    public CaffeineCacheManager defaultCacheManager() {
        CaffeineCacheManager defaultCacheManager = new CaffeineCacheManager(CacheConstants.ACHE_NAME);
        defaultCacheManager.setAllowNullValues(properties.getAllowNullValue());
        Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .initialCapacity(properties.getInitCacheCapacity())
                .maximumSize(properties.getMaxCacheCapacity())
                .expireAfterWrite(7, TimeUnit.DAYS);
        defaultCacheManager.setCaffeine(caffeineBuilder);
        log.info("the caffeine cache manager is loaded successfully!");
        return defaultCacheManager;
    }

    /**
     *  用于管理验证码缓存,设置过期时间为300秒
     */
    @Bean("verificationCodeCacheManager")
    public CaffeineCacheManager verificationCodeCacheManager() {
        CaffeineCacheManager verificationCodeCacheManager = new CaffeineCacheManager(CacheConstants.CODE_CACHE_NAME);
        verificationCodeCacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(properties.getInitCacheCapacity())
                .maximumSize(properties.getMaxCacheCapacity())
                .expireAfterWrite(300, TimeUnit.SECONDS));
        log.info("the verificationCodeCacheManager cache manager is loaded successfully!");
        return verificationCodeCacheManager;
    }

}

使用方式,和上面的案例一致,不过这个时候就必须使用@Qualifier来指定相关的bean。其他使用方式都是一致的。

@Autowired
@Qualifier(value = "defaultCacheManager") 
private CaffeineCacheManager defaultCacheManager;

@Autowired
@Qualifier(value = "verificationCodeCacheManager")
private CaffeineCacheManager verificationCodeCacheManager;

springboot集成redis并且定制化配置cachemanager

缓存使用到的Maven依赖:

<!-- 集成redis依赖  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

yaml文件:Redis部分的配置

spring:
  redis:
    database: 0
    host: localhost
    port: 6379
    password: abc123456  #这个看你的Redis版本,一些老的Redis用的是auth,一些比较新的用的是password配置
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 3

config配置类:

@SpringBootConfiguration
@EnableCaching
@Slf4j
public class RedisCacheConfig {

    /**
     * 定制链接和操作Redis的客户端工具   配置一些序列化器,这样通过缓存管理工具看到的缓存数据就不是乱码了
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //配置序列化器
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();

        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setDefaultSerializer(genericJackson2JsonRedisSerializer);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }


    /**
     * 默认的全局缓存管理器   配置一些序列化器,这样通过缓存管理工具看到的缓存数据就不是乱码了
     * @return
     */
    @Primary  //关于这个相关的解释,上面的caffeine配置中都有说明,这里就不额外说明了
    @Bean("defaultCacheManager")
    public RedisCacheManager defaultCacheManager(RedisConnectionFactory redisConnectionFactory) {
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        RedisCacheConfiguration config = RedisCacheConfiguration.
                defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));

        RedisCacheManager defaultCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config).build();

        log.info("the redis defaultCacheManager is loaded successfully!");
        return defaultCacheManager;
    }

    /**
     *  用于管理验证码缓存,设置过期时间为300秒
     */
    @Bean("verificationCodeCacheManager")
    public RedisCacheManager verificationCodeCacheManager(RedisConnectionFactory redisConnectionFactory) {
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        RedisCacheConfiguration config = RedisCacheConfiguration.
                defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(5))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));

        RedisCacheManager verificationCodeCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config).build();

        log.info("the verificationCodeCacheManager cache manager is loaded successfully!");
        return verificationCodeCacheManager;
    }


}

使用方式,和上面的一致:

@Autowired
@Qualifier(value = "defaultCacheManager")
private CaffeineCacheManager defaultCacheManager;

Cache cache = defaultCacheManager.getCache(CacheConstants.CACHE_NAME); //配置类中对这个cachemanager设置的缓存名称

UserCache usercache = new UserCache();
usercache.setuserId = "123456"; 
//实际业务场景中,比如用户登录成功,可以把用户id设置进来,方便其他业务进行获取  LOGIN_INFO 常量为自定义的一个标识符,方便在查询辨别缓存数据
cache.put(userId+CacheConstants.LOGIN_INFO,usercache);

//特别注意:get缓存数据的时候,很大可能获取到的是null对象
UserCache usercache = cache.get( userId + CacheConstants.LOGIN_INFO, UserCache.class);
if(usercache == null){
    return null;
}

//清除缓存中指定的数据  通过key来清除缓存中的数据
cache.evict(userId + CacheConstants.LOGIN_INFO);