tio-boot tio-utils 内置 CacheUtils

## tio-boot 内置 CacheUtils

### 概述

tio-utils 内置了 CacheUtils 用户提供对缓存数据的支持,提供的工具类如下,tio-boot 已经内置了 tio-utils,所以不需要添加 tio-utils 的依赖.但是需要添加对于缓存库的依赖

- com.litongjava.tio.utils.cache.caffeine.CaffeineCache

- com.litongjava.tio.utils.cache.guava.GuavaCache

- com.litongjava.tio.utils.cache.caffeineredis.CaffeineRedisCache

- com.litongjava.tio.utils.cache.guavaredis.GuavaRedisCache

- com.litongjava.tio.utils.cache.j2cache.J2Cache

- com.litongjava.tio.utils.cache.redis.RedisCache

下面演示一下 CaffeineCache,RedisCache,CaffeineRedisCache 的使用

### CacheUtils

com.litongjava.tio.utils.cache.CacheUtils 提供了 get 方法,方法签名如下

```
public static <T extends Serializable> T get(ICache cache, String cacheKey, boolean putTempToCacheIfNull,FirsthandCreater<T> firsthandCreater);
```

方法解释:

根据 cacheKey 从缓存中获取对象,如果缓存中没有该 key 对象,则用 firsthandCreater 获取对象,并将对象用 cacheKey 存于 cache 中


### 缓存数据到 Caffeine

#### 添加依赖

tio-utils 虽然提供了对 caffeine 的支持,但是并没有继承 caffeine 依赖,所以添加 caffeine 依赖,推荐 2.x 版本,因为 3.x 版已经不支持 jdk 1.8

```
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
  <version>2.9.3</version>
</dependency>
```


#### 配置类 CacheNameConfig

CacheName,使用默认的 com.litongjava.tio.utils.cache.CacheName

CacheNameService,使用默认的 com.litongjava.tio.utils.cache.CacheNameService

```
package com.litongjava.tio.web.hello.config;
import java.util.Collection;
import com.litongjava.jfinal.aop.annotation.Bean;
import com.litongjava.jfinal.aop.annotation.Configuration;
import com.litongjava.tio.utils.cache.CacheName;
import com.litongjava.tio.utils.cache.CacheNameService;
import com.litongjava.tio.utils.cache.caffeine.CaffeineCacheFactory;
import com.litongjava.tio.utils.time.Time;
@Configuration
public class CacheNameConfig {
  @Bean
  public CacheNameService register() {
    CacheName demo = new CacheName("demo", null, Time.MINUTE_1 * 10);
    CacheNameService cacheNameService = new CacheNameService();
    cacheNameService.add(demo);
    Collection<CacheName> names = cacheNameService.cacheNames();
    for (CacheName cacheName : names) {
      CaffeineCacheFactory.INSTANCE.register(cacheName);
    }
    return cacheNameService;
  }
}
```

package com.litongjava.tio.web.hello.controller;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.utils.cache.CacheUtils;
import com.litongjava.tio.utils.cache.FirsthandCreater;
import com.litongjava.tio.utils.cache.ICache;
import com.litongjava.tio.utils.cache.caffeine.CaffeineCacheFactory;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/cache/caffeine")
@Slf4j
public class CacheCaffeineTestController {
  public Object test2() {
    // firsthandCreater用户查询数据库
    FirsthandCreater<String> firsthandCreater = new FirsthandCreater<String>() {
      @Override
      public String create() {
        log.info("查询数据库");
        return "index";
      }
    };
    // 通常是tableName
    String cacheName = "demo";
    ICache cache = CaffeineCacheFactory.INSTANCE.getCache(cacheName);
    String key = "key";
    boolean putTempToCacheIfNull = false;
    String value = CacheUtils.get(cache, key, putTempToCacheIfNull, firsthandCreater);
    return value;
  }
}
```


访问测试 http://localhost/cache/caffeine/test2

#### CacheNameService 类

- **目的**: 管理缓存名称和相关设置。

- **成员变量**: 包含一个 `CacheName` 类型的 `demo` 对象,初始化为一个缓存名称为 "demo",生命周期为 10 分钟(`Time.MINUTE_1 * 10`)的缓存。

- **方法 `cacheNames()`**: 返回一个包含 `demo` 缓存配置的列表。


#### CacheNameConfig 类

- **目的**: 配置缓存。

- **方法 `register()`**:

  - 创建 `CacheNameService` 实例。

  - 遍历 `cacheNames()` 方法返回的所有缓存名称。

  - 对每个缓存名称,使用 `CaffeineCacheFactory.register` 方法注册缓存,设定其生存和空闲时间。

  - 返回 `CacheNameService` 实例。


#### CacheTestController 类

- **目的**: 演示如何使用缓存。

- **方法 `test2()`**:

  - 定义 `FirsthandCreater` 匿名类实例,用于在缓存未命中时获取数据(例如从数据库中)。

  - 通过 `Aop.get(CacheNameService.class)` 获取 `CacheNameService` 实例,并从中获取 `demo` 缓存的名称。

  - 使用 `CaffeineCache.getCache` 方法获取对应名称的缓存实例。

  - 使用 `CacheUtils.get` 方法从缓存中获取键为 "key" 的数据。如果缓存中没有该数据,则会调用 `FirsthandCreater` 实例的 `create` 方法来获取数据并缓存它。

  - 返回获取的值。


#### 总结


整体而言,这些代码段展示了如何在 Tio 框架中配置和使用 Caffeine 缓存。它们通过 `CacheNameService` 类管理缓存配置,`CacheNameConfig` 类负责缓存的注册,而 `CacheTestController` 类演示了如何实际从缓存中获取数据。这是一个典型的缓存使用场景,特别是在需要高效读取频繁访问数据的应用中。


### 缓存数据到 redis


使用 tio-utils 提供的 CacheUtils 缓存数据到 redis

#### 添加依赖

因为需要使用 redisson 连接 redis,所以需要添加 redisson 依赖

```
<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson</artifactId>
  <version>3.16.0</version>
</dependency>
```


#### RedissonConfig 配置类

```
package com.litongjava.tio.boot.hello.config;
import com.litongjava.jfinal.aop.annotation.Bean;
import com.litongjava.jfinal.aop.annotation.Configuration;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
@Configuration
public class RedissonConfig {
  @Bean(destroyMethod = "shutdown", priority = 10)
  public RedissonClient redissonClient() {
    Config config = new Config();
    config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
    // 如果你的 Redis 设置了密码
    // .setPassword("yourPassword");
    RedissonClient client = null;
    try {
      client = Redisson.create(config);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return client;
  }
}
```


#### CacheNameConfig 配置类

```
package com.litongjava.tio.web.hello.config;
import java.util.Collection;
import org.redisson.api.RedissonClient;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.Bean;
import com.litongjava.jfinal.aop.annotation.Configuration;
import com.litongjava.tio.utils.cache.CacheName;
import com.litongjava.tio.utils.cache.CacheNameService;
import com.litongjava.tio.utils.cache.caffeine.CaffeineCacheFactory;
import com.litongjava.tio.utils.cache.redis.RedisCacheFactory;
import com.litongjava.tio.utils.time.Time;
@Configuration
public class CacheNameConfig {
  @Bean
  public CacheNameService register() {
    //设置CacheName
    CacheName demo = new CacheName("demo", null, Time.MINUTE_1 * 10);
    //将CacheName添加到CacheNameService
    CacheNameService cacheNameService = new CacheNameService();
    cacheNameService.add(demo);
    //将redissonClient添加到RedisCacheFactory
    RedissonClient redissonClient = Aop.get(RedissonClient.class);
    RedisCacheFactory.INSTANCE.setRedisson(redissonClient);
    //注册cacheName
    Collection<CacheName> names = cacheNameService.cacheNames();
    for (CacheName cacheName : names) {
      CaffeineCacheFactory.INSTANCE.register(cacheName);
      RedisCacheFactory.INSTANCE.register(cacheName);
    }
    return cacheNameService;
  }
}
```


#### 测试 Controller

```
package com.litongjava.tio.web.hello.controller;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.utils.cache.CacheUtils;
import com.litongjava.tio.utils.cache.FirsthandCreater;
import com.litongjava.tio.utils.cache.ICache;
import com.litongjava.tio.utils.cache.redis.RedisCacheFactory;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/cache/redis")
@Slf4j
public class CacheRedisTestController {
  public Object test3() {
    // firsthandCreater用户查询数据库
    FirsthandCreater<String> firsthandCreater = new FirsthandCreater<String>() {
      @Override
      public String create() {
        log.info("查询数据库");
        return "index";
      }
    };
    String cacheName = "demo";
    ICache cache = RedisCacheFactory.INSTANCE.getCache(cacheName);
    String key = "key";
    boolean putTempToCacheIfNull = false;
    String value = CacheUtils.get(cache, key, putTempToCacheIfNull, firsthandCreater);
    return value;
  }
}
```


#### 访问测试


http://localhost/cache/redis/test3

代码展示了如何在 Tio 框架中使用 Redis 作为缓存解决方案。以下是对代码的详细解释:


#### RedissonConfig 类

- **目的**: 配置 Redisson 客户端以连接 Redis 服务器。

- **方法 `redissonClient()`**:

  - 使用 `Config` 类创建 Redis 配置,指定 Redis 服务器地址和数据库索引。

  - 如果 Redis 设置了密码,可以通过 `.setPassword("yourPassword")` 方法设置。

  - 创建 `RedissonClient` 实例并返回。

  - `priority = 10` 指定这个 Bean 的初始化优先级,值越小优先级越高,确保在其他依赖 RedissonClient 的 Bean 之前初始化。


#### CacheNameConfig 类

- **目的**: 配置缓存名称和设置。

- **方法 `register()`**:

  - 获取 `RedissonClient` 实例。

  - 创建 `CacheNameService` 实例,然后获取其提供的缓存配置列表。

  - 遍历列表,使用 `RedisCache.register` 方法为每个缓存名称注册 Redis 缓存,指定存活时间和空闲时间。

  - 返回 `CacheNameService` 实例。


#### CacheTestController 类

- **目的**: 演示如何使用缓存。

- **方法 `test3()`**:

  - 定义 `FirsthandCreater` 匿名类,用于在缓存未命中时获取数据(比如从数据库获取)。

  - 通过 `Aop.get(CacheNameService.class)` 获取 `CacheNameService` 实例,进而获得 `demo` 缓存的名称。

  - 使用 `RedisCache.getCache` 方法获取对应名称的缓存实例。

  - 使用 `CacheUtils.get` 方法从缓存中获取键为 "key" 的数据。如果缓存中没有该数据,则会调用 `FirsthandCreater` 实例的 `create` 方法来获取数据,并将其缓存。

  - 返回获取的值。


#### 总结


这段代码演示了如何在 Tio 框架中配置和使用 Redis 作为缓存解决方案。它使用 RedissonClient 连接到 Redis 服务器,并通过 CacheNameService 管理缓存的不同配置。CacheTestController 类演示了如何在实际应用中从缓存中读取数据,如果缓存中没有数据,会从数据库中获取并缓存。这种方式在需要高效读取频繁访问数据的应用中非常有用。


### 使用 CacheUtils 整合 caffeine 和 redis 实现的两级缓存


#### 配置类 CacheNameConfig


```
package com.litongjava.tio.web.hello.config;
import java.util.Collection;
import org.redisson.api.RedissonClient;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.Bean;
import com.litongjava.jfinal.aop.annotation.Configuration;
import com.litongjava.tio.utils.cache.CacheName;
import com.litongjava.tio.utils.cache.CacheNameService;
import com.litongjava.tio.utils.cache.caffeineredis.CaffeineRedisCacheFactory;
import com.litongjava.tio.utils.time.Time;
@Configuration
public class CacheNameConfig {
  @Bean
  public CacheNameService register() {
    //设置CacheName
    CacheName demo = new CacheName("demo", null, Time.MINUTE_1 * 10);
    //将CacheName添加到CacheNameService
    CacheNameService cacheNameService = new CacheNameService();
    cacheNameService.add(demo);
    //将redissonClient添加到CaffeineRedisCacheFactory
    RedissonClient redissonClient = Aop.get(RedissonClient.class);
    CaffeineRedisCacheFactory.INSTANCE.init(redissonClient);
    //注册cacheName
    Collection<CacheName> names = cacheNameService.cacheNames();
    for (CacheName cacheName : names) {
      //CaffeineCacheFactory.INSTANCE.register(cacheName);
      //RedisCacheFactory.INSTANCE.register(cacheName);
      CaffeineRedisCacheFactory.INSTANCE.register(cacheName);
    }
    return cacheNameService;
  }
}
```


#### 测试类 CacheCaffeineRedisTestController


```
package com.litongjava.tio.web.hello.controller;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.utils.cache.CacheUtils;
import com.litongjava.tio.utils.cache.FirsthandCreater;
import com.litongjava.tio.utils.cache.ICache;
import com.litongjava.tio.utils.cache.caffeineredis.CaffeineRedisCacheFactory;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/cache/caffeine/redis")
@Slf4j
public class CacheCaffeineRedisTestController {
  public Object test() {
    // firsthandCreater用户查询数据库
    FirsthandCreater<String> firsthandCreater = new FirsthandCreater<String>() {
      @Override
      public String create() {
        log.info("查询数据库");
        return "index";
      }
    };
    String cacheName = "demo";
    ICache cache = CaffeineRedisCacheFactory.INSTANCE.getCache(cacheName);
    String key = "key";
    boolean putTempToCacheIfNull = false;
    String value = CacheUtils.get(cache, key, putTempToCacheIfNull, firsthandCreater);
    return value;
  }
}
```

访问 http://localhost/cache/caffeine/redis/test 查看测试结果


评论区