jetcache Documentation

JetCache is a Java cache framework.

5,590 stars Java Java 1 file · ~11,256 tokens #cache#java#jcache#redis#spring#spring-cache
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy jetcache on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name jetcache --restart always redis:${IMG_VER}
Configured Services & Ports
⚙️ master
image: redis:${IMG_VER}
⚙️ slave1
image: redis:${IMG_VER}
⚙️ slave2
image: redis:${IMG_VER}
⚙️ sentinel1
image: redis:${IMG_VER}
⚙️ sentinel2
image: redis:${IMG_VER}
⚙️ sentinel3
image: redis:${IMG_VER}
⚙️ cluster1
image: redis:${IMG_VER}
⚙️ cluster2
image: redis:${IMG_VER}
⚙️ cluster3
image: redis:${IMG_VER}
⚙️ cluster4
image: redis:${IMG_VER}
⚙️ cluster5
image: redis:${IMG_VER}
⚙️ cluster6
image: redis:${IMG_VER}
⚙️ clusterCreator
image: redis:${IMG_VER}
docker-compose.yml
yaml (docker-compose.yml)
services:
  master:
    image: "redis:${IMG_VER}"
    container_name: master
    command:  redis-server --port 6379
    network_mode: "host"
  slave1:
    image: "redis:${IMG_VER}"
    container_name: slave1
    command:  redis-server --port 6380 --slaveof 127.0.0.1 6379
    network_mode: "host"
  slave2:
    image: "redis:${IMG_VER}"
    container_name: slave2
    command:  redis-server --port 6381 --slaveof 127.0.0.1 6379
    network_mode: "host"
  sentinel1:
    image: "redis:${IMG_VER}"
    container_name: sentinel1
    command: bash -c "sleep 3;
      echo port 26379 > /etc/sentinel.conf;
      sed -i '$$a sentinel monitor mymaster 127.0.0.1 6379 2' /etc/sentinel.conf;
      sed -i '$$a sentinel down-after-milliseconds mymaster 2000' /etc/sentinel.conf;
      sed -i '$$a sentinel failover-timeout mymaster 5000' /etc/sentinel.conf;
      sed -i '$$a sentinel parallel-syncs mymaster 1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-ip 127.0.0.1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-port 26379' /etc/sentinel.conf;
      redis-sentinel /etc/sentinel.conf"
    network_mode: "host"
  sentinel2:
    image: "redis:${IMG_VER}"
    container_name: sentinel2
    command: bash -c "sleep 3;
      echo port 26380 > /etc/sentinel.conf;
      sed -i '$$a sentinel monitor mymaster 127.0.0.1 6379 2' /etc/sentinel.conf;
      sed -i '$$a sentinel down-after-milliseconds mymaster 2000' /etc/sentinel.conf;
      sed -i '$$a sentinel failover-timeout mymaster 5000' /etc/sentinel.conf;
      sed -i '$$a sentinel parallel-syncs mymaster 1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-ip 127.0.0.1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-port 26380' /etc/sentinel.conf;
      redis-sentinel /etc/sentinel.conf"
    network_mode: "host"
  sentinel3:
    image: "redis:${IMG_VER}"
    container_name: sentinel3
    command: bash -c "sleep 3;
      echo port 26381 > /etc/sentinel.conf;
      sed -i '$$a sentinel monitor mymaster 127.0.0.1 6379 2' /etc/sentinel.conf;
      sed -i '$$a sentinel down-after-milliseconds mymaster 2000' /etc/sentinel.conf;
      sed -i '$$a sentinel failover-timeout mymaster 5000' /etc/sentinel.conf;
      sed -i '$$a sentinel parallel-syncs mymaster 1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-ip 127.0.0.1' /etc/sentinel.conf;
      sed -i '$$a sentinel announce-port 26381' /etc/sentinel.conf;
      redis-sentinel /etc/sentinel.conf"
    network_mode: "host"
  cluster1:
    image: "redis:${IMG_VER}"
    container_name: cluster1
    command: bash -c "echo port 7000 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  cluster2:
    image: "redis:${IMG_VER}"
    container_name: cluster2
    command: bash -c "echo port 7001 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  cluster3:
    image: "redis:${IMG_VER}"
    container_name: cluster3
    command: bash -c "echo port 7002 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  cluster4:
    image: "redis:${IMG_VER}"
    container_name: cluster4
    command: bash -c "echo port 7003 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  cluster5:
    image: "redis:${IMG_VER}"
    container_name: cluster5
    command: bash -c "echo port 7004 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  cluster6:
    image: "redis:${IMG_VER}"
    container_name: cluster6
    command: bash -c "echo port 7005 > /etc/cluster.conf;
      sed -i '$$a cluster-enabled yes' /etc/cluster.conf;
      sed -i '$$a cluster-config-file nodes.conf' /etc/cluster.conf;
      sed -i '$$a cluster-node-timeout 5000' /etc/cluster.conf;
      sed -i '$$a appendonly yes' /etc/cluster.conf;
      redis-server /etc/cluster.conf"
    network_mode: "host"
  clusterCreator:
    image: "redis:${IMG_VER}"
    container_name: clusterCreator
    command: bash -c "sleep 3;
      echo yes | redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1;
      echo cluster created"
    network_mode: "host"

File: docs/EN/AdvancedCacheAPI.md

CacheBuilder

The CacheBuilder provide a way to construct Cache instance use your own code, See Here.
CacheBuilder are useful if you work without Spring, otherwise you may not use it directly.

asynchronous API

All CacheResult returned by uppercase method support asynchronous access (since 2.2).
Presently asynchronous result only provide in redis-lettuce implementation.
Other implementations such as Tair, Jedis will block during uppercase method call.
But all the api are same.

For example:

java
CacheGetResult r = cache.GET(userId);

If you use Tair, Jedis the GET operation will block until cache access finished.
However the GET method will return immediately if you use lettuce to access redis,
any following call of r.isSuccess() or r.getValue() or r.getMessage() will block. To avoid this you can:

java
CompletionStage<ResultData> future = r.future();
future.thenRun(() -> {
    if(r.isSuccess()){
        System.out.println(r.getValue());
    }
});

The above code will execute callback in the thread which execute the asynchronous operation.
CompletionStage is a new feature in Java 8, read related documents if you are not familiar with it.
Note do not call any block method in the callback, since the callback is probably run in event-loop thread.

Some lowercase API can also get the advantage of asynchronous capability since they have no return value (in a asynchronous enabled backend such as lettuce).
For instance put and removeAll will return immediately, but get will not because get method need return a value.

Auto load(read through)

LoadingCache provide auto load capability (since 2.2). It is based on decorator pattern and implements Cache interface.
If a CacheBuilder has a loader, then its buildCache method will return a LoadingCache wrapper.
For example :

java
Cache<Long,UserDO> userCache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .loader(key -> loadUserFromDatabase(key))
                .buildCache();

The get and getAll method of LoadingCache will call the loader when cache miss.
If any exception throws in the loader, get and getAll will throw CacheInvokeException.

Notice:

  1. uppercase method such as GET and GET_ALL only operate with cache and do not invoke the loader.
  2. loader should be install on MultiLevelCache if you use multi level cache, do not install it on the cache underneath.

Unfortunately, we can't set the loader on @CreateCache because only constant is permit for the attribute of an annotation.
So we use the follow code instead:

java
@CreateCache
private Cache<Long,UserDO> userCache;

@PostConstruct
public void init(){
    userCache.config().setLoader(this::loadUserFromDatabase);
}

@CreateCache always return a LoadingCache wrapper, so we can set a loader in the init method and take effect immediately.

Auto refreshment

The RefreshCache provide auto refreshment capability (since 2.2).
It is based on decorator pattern and implements Cache interface.
The buildCache method of CacheBuilder will return a RefreshCache wrapper when loader and refreshPolicy are both be set.

CacheBuilder usage:

java
RefreshPolicy policy = RefreshPolicy.newPolicy(1, TimeUnit.MINUTES)
                .stopRefreshAfterLastAccess(30, TimeUnit.MINUTES);
Cache<String, Long> orderSumCache = LinkedHashMapCacheBuilder
                .createLinkedHashMapCacheBuilder()
                .loader(key -> loadOrderSumFromDatabase(key))
                .refreshPolicy(policy)
                .buildCache();

The above code specify that every key-value pair should refresh every 1 minute since first access.
The refreshment of one key-value pair stops if this key does not be access in 30 minutes.
If the backend cache system is a remote cache (or MultiLevelCache with a remote cache as last layer),
the refreshment is global exclusive, so it avoid two or more servers refresh same key concurrently (implements using tryLock in Cache).

Similar with LoadingCache, we init refresh policy in the init method when using with @CreateCache:

java
@CreateCache
private Cache<String, Long> orderSumCache;

@PostConstruct
public void init(){
    RefreshPolicy policy = RefreshPolicy.newPolicy(1, TimeUnit.MINUTES)
                          .stopRefreshAfterLastAccess(30, TimeUnit.MINUTES);
    orderSumCache.config().setLoader(this::loadOrderSumFromDatabase);
    orderSumCache.config().setRefreshPolicy(policy);
}

File: docs/EN/Builder.md

Annotations such as @Cached and @CreateCache are based on Spring Framework 4.0 or above.
You can use JetCache API to create, manage, monitor Cache instance.

Create Cache

Similar to guava/caffeine cache. For example, the below code create a LinkedHashMapCache instance:

java
Cache<String, Integer> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();

Create RedisCache (using jedis):

java
GenericObjectPoolConfig pc = new GenericObjectPoolConfig();
        pc.setMinIdle(2);
        pc.setMaxIdle(10);
        pc.setMaxTotal(10);
        JedisPool pool = new JedisPool(pc, "localhost", 6379);
Cache<Long, OrderDO> orderCache = RedisCacheBuilder.createRedisCacheBuilder()
                .keyConvertor(Fastjson2KeyConvertor.INSTANCE)
                .valueEncoder(JavaValueEncoder.INSTANCE)
                .valueDecoder(JavaValueDecoder.INSTANCE)
                .jedisPool(pool)
                .keyPrefix("orderCache")
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();

Note: JetCache 2.8.x enables the deserialization security filter by default. If your cached values contain custom classes (e.g. OrderDO above), you need to configure the filter allow list before creating the cache, otherwise deserialization will fail:

java
DecodeFilter.getDefault().addAllowPatterns("com.yourcompany.");

See the "Deserialization Filter Configuration" section in the configuration docs for details.

Multi level cache

java
Cache multiLevelCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
      .addCache(memoryCache, redisCache)
      .expireAfterWrite(100, TimeUnit.SECONDS)
      .buildCache();

You can even build cache more than two level.

Monitor

java
Cache orderCache = ...
CacheMonitor orderCacheMonitor = new DefaultCacheMonitor("OrderCache");
orderCache.config().getMonitors().add(orderCacheMonitor); // jetcache 2.2+, or call builder.addMonitor() before buildCache()
// Cache<Long, Order> monitedOrderCache = new MonitoredCache(orderCache, orderCacheMonitor); //before jetcache 2.2
int resetTime = 1;
boolean verboseLog = false;
DefaultCacheMonitorManager cacheMonitorManager = new DefaultCacheMonitorManager(resetTime, TimeUnit.SECONDS, verboseLog);
cacheMonitorManager.add(orderCacheMonitor);
cacheMonitorManager.start();

Each CacheMonitor used for exactly one Cache instance.
After start, DefaultCacheMonitorManager outputs statistics every resetTime using slf4j (see Statistics).

The constructor parameter of DefaultCacheMonitor will be used as cache name in output table.

You can add to CacheMonitor to every Cache instance in MultiLevelCache,
so each cache in multi level cache is monitored.

The output format can be customized using this constructor:

java
public DefaultCacheMonitorManager(int resetTime, TimeUnit resetTimeUnit, Consumer<StatInfo> statCallback)

File: docs/EN/CacheAPI.md

Introduce

The core concept of JetCache is the com.alicp.jetcache.Cache(hereinafter called Cache) interface, it provides some API similar like javax.cache.Cache in JSR107.
The reason that JetCache does not implements JSR107 includes:

  1. We want that the API of JetCache are more simpler and easy to use than JSR107.
  2. Some operation defined in javax.cache.Cache are difficult to implement efficiently in some specific distributed cache system (eg. some atomic operation and removeAll()).
  3. Implements whole JSR107 needs a lots of work.

JSR107 style API

The follows methods in JetCache Cache interface are same with those in javax.cache.Cache except that
the methods in Cache never throws exception.

java
V get(K key)
void put(K key, V value);
boolean putIfAbsent(K key, V value); //MultiLevelCache do not support this method
boolean remove(K key);
<T> T unwrap(Class<T> clazz);
Map<K,V> getAll(Set<? extends K> keys);
void putAll(Map<? extends K,? extends V> map);
void removeAll(Set<? extends K> keys);

JetCache Cache API

java
V computeIfAbsent(K key, Function<K, V> loader)
V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull)
V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit)

If there is a value associated with the key, return the value,
otherwise use the loader load the value and update the cache, then return the value.
The cacheNullWhenLoaderReturnNull parameter indicate whether null value returned by loader should put into cache.
The expire and timeUnit specifies the TTL (will overrides defaults) of the KV pair when update the cache.
Load time are recorded with these methods.

java
void put(K key, V value, long expire, TimeUnit timeUnit)

The put opperation. expire and timeUnit specifies the TTL (will overrides defaults).

java
AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit)
boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)

Try to get a lock of the key in non-block way.
Return a instance of AutoReleaseLock if there is no lock on the specified key, or else null.
If the Cache is an in-memory implementation, the lock is a local lock in the JVM,
or else it's a non-strict distributed lock.
The expire time of the lock specified by expire and timeUnit.
MultiLevelCache use the last level to acquire the lock.
Here is an example:

java
// use try-with-resource to auto release the lock
  try(AutoReleaseLock lock = cache.tryLock("MyKey",100, TimeUnit.SECONDS)){
     if(lock != null){
        // do something
     }
  }

Here is a simpler way that you never forget if(lock != null):

java
boolean hasRun = cache.tryLockAndRun("MyKey",100, TimeUnit.SECONDS, () -> {
    // do something
  });

When the cache system is distributed, the tryLock will carefully auto retry when network fails, and it never release a lock that the current machine doesn't hold. Using tryLock or tryLockAndRun are much easier than make your own lock based on a cache system.
Be keep in mind that the distributed lock based on a cache system is non-strict,
if you need strict distributed lock you should consider other framework like Zoo Keeper.

Upper case API

Operation like V get(K key) are convenient but it can not tell more information when it returns null.
So JetCache provide some more operation which return a CacheResult object like belows:

java
CacheGetResult<V> GET(K key);
MultiGetResult<K, V> GET_ALL(Set<? extends K> keys);
CacheResult PUT(K key, V value);
CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
CacheResult PUT_ALL(Map<? extends K, ? extends V> map);
CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
CacheResult REMOVE(K key);
CacheResult REMOVE_ALL(Set<? extends K> keys);
CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);

The name of these method are all uppercase. The usage are more complex and powerful:

java
CacheGetResult<OrderDO> r = cache.GET(orderId);
if( r.isSuccess() ){
    OrderDO order = r.getValue();
} else if (r.getResultCode() == CacheResultCode.NOT_EXISTS) {
    System.out.println("cache miss:" + orderId);
} else if(r.getResultCode() == CacheResultCode.EXPIRED) {
    System.out.println("cache expired:" + orderId));
} else {
    System.out.println("cache get error:" + orderId);
}

File: docs/EN/Compatibility.md

spring compatibility

jetcache tested with below spring/spring-boot versions

jetcache spring spring boot comments
2.5 4.0.8.RELEASE~5.1.1.RELEASE 1.1.9.RELEASE~2.0.5.RELEASE
2.6 5.0.4.RELEASE~5.2.4.RELEASE 2.0.0.RELEASE~2.2.5.RELEASE jetcache-redis depends on jedis3.1.0, spring-data(jedis, boot version<=2.1.X) depends on jedis2.9.3, can't used together
2.7 5.2.4.RELEASE~5.3.23 2.2.5.RELEASE~2.7.5 jetcache-redis depends on jedis4, spring-data(jedis) depends on jedis3, can't used together
2.7.4 5.2.4.RELEASE~6.2.18 2.2.5.RELEASE~3.5.14 can also support Spring 7/Spring Boot 4, but the BOM defines Spring 6/Spring Boot 3 by default
2.8 6.x~7.0.7 3.x~4.0.6 requires Java 17+; BOM defaults to Spring Framework 7.0.7 / Spring Boot 4.0.6 / Spring Data Redis 4.0.5 / SLF4J 2.x

compatible change notes

2.8.0

  • Java 17 is now the minimum required version

  • areaInCacheName default value is now false (was true in versions prior to 2.8.0).

  • kryo4 is no longer supported, com.esotericsoftware:kryo is upgraded to 5.x. The KRYO constant in SerialPolicy now uses kryo5 implementation internally. kryo4 serialized data is not compatible with kryo5, wait for old cache entries to expire or clear cache before upgrading

  • Removed fastjson1 support, fastjson key convertor now uses fastjson2 internally. If you need fastjson1, add the dependency yourself and implement a custom KeyConvertor

  • Removed Spring XML namespace support (<jetcache:xxx> tags in XML configuration are no longer available)

  • Added deserialization filter mechanism (enabled by default). This is a breaking change — if your cached values contain custom classes not in the default allowed list, deserialization (or serialization) will fail immediately after upgrading.

    Upgrade steps: Since older versions do not have this configuration option, pre-configuration before upgrading is not possible. Two recommended approaches:

    Option 1: Add decodeFilterAllowPatterns configuration at the same time as upgrading JetCache, including the package names of your custom classes in the allow list. For example:

    yaml
    jetcache:
      decodeFilterAllowPatterns:
        - com.yourcompany.

    Option 2: Disable the filter during upgrade (same behavior as 2.7):

    yaml
    jetcache:
      decodeFilterEnabled: false

    See the "Deserialization Filter Configuration" section in the configuration docs for the list of default allowed packages and detailed setup instructions.

2.7.4

  • use spring-boot 3.1.3, spring-framework 6.0.11, slf4j-api 2.x as default
  • remove javax.annotation:javax.annotation-api, if you use @PostConstruct, you may need to add this dependency by yourself

2.7.2

  • update encoder/decoder of redisson, not compatible with 2.7.1

2.7.0

  • jetcache-redis depends on jedis4,springdata(jedis) depends on jedis3, can't use together
  • encoder/decoder now support kryo4 and kryo5, in yml "kryo" is kryo4,"kryo5" is kryo5. the kryo4 and kryo5 is not compatible.
    • in maven kryo4 is com.esotericsoftware:kryo, kryo5 is com.esotericsoftware.kryo:kryo5
    • kryo4 and kryo5 can be used together
    • notice that version of com.esotericsoftware:kryo can be set to 5.x.x
  • use lettuce to connect redis cluster need specify "mode=cluster" in yml
  • default key convertor change to "fastjson2", fastjson2 and fastjson can be used together, fastjson(not fastjson2)/kryo/kryo5/mvel is now optional in maven
  • if not use spring boot, add @Import(JetCacheBaseBeans.class), and remove old configProvider bean definition. see docs for detail example.
  • change GlobalCacheConfig.areaInCacheName default value to false (has bug, default value may still be true), need to add areaInCacheName=false

2.6.0

  • GET/GET_ALL method of RefreshCache will not trigger auto refresh
  • lettuce 4 is not supported
  • jedis 2.9 is not supported

2.5.0

  • ClassCastException may occurs when upgrade directly from versions <=2.3.3 and MultiLevelCache(or cacheType=CacheType.BOTH) is used. To solve this problem, upgrade to 2.4.4 and deploy it to product env first, then upgrade to 2.5.0 or above.
  • Annotations on sub classes will override annotations on interfaces and super class.

File: docs/EN/Config.md

Here is an example of yml config file in Spring Boot:

text
jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  hidePackages: com.alibaba
  local:
    default:
      type: caffeine
      limit: 100
      keyConvertor: fastjson2 #other choose:fastjson(same as fastjson2)/jackson/jackson3
      expireAfterWriteInMillis: 100000
    otherArea:
      type: linkedhashmap
      limit: 100
      keyConvertor: none
      expireAfterWriteInMillis: 100000
  remote:
    default:
      type: redis
      keyConvertor: fastjson2 #other choose:fastjson(same as fastjson2)/jackson/jackson3
      broadcastChannel: projectA
      valueEncoder: java #other choose:kryo/kryo5
      valueDecoder: java #other choose:kryo/kryo5
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}
    otherArea:
      type: redis
      keyConvertor: fastjson2 #other choose:fastjson(same as fastjson2)/jackson/jackson3
      broadcastChannel: projectA
      valueEncoder: java #other choose:kryo/kryo5
      valueDecoder: java #other choose:kryo/kryo5
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}

You can configure GlobalCacheConfig directly without Spring Boot. It's similar. See getting started tutorial.

The description of configuration listed in the below table:

configuration key default value description
jetcache.statIntervalMinutes 0 Specify statistic interval, in minutes. 0 indicate no statistics.
jetcache.areaInCacheName true(2.6-) false(2.7+) jetcache-anno use cache name as remote cache key prefix, in jetcache 2.4.3 and previous version, it allways add area name in cache name. Since 2.4.4 we have this config item, for compatible reason default value is true. However false value are more reasonable for new project. 2.7 changes default value to false
jetcache.useDefaultLocalExpireInMultiLevelCache false If set to true, when cacheType is BOTH and localExpire is not explicitly set (including @Cached, @CreateCache annotations and QuickConfig API), the local cache expire time will be the minimum of the local cache builder's expireAfterWriteInMillis and expire.
jetcache.hiddenPackages undefined The package name startsWith(hiddenPackages) will be cut off in the generated cache instance name.
jetcache.[local/remote].${area}.type undefined Type of the backend cache system. Can be tair, redis for remote cache ,or linkedhashmap, caffeine for local cache.
jetcache.[local/remote].${area}.keyConvertor fastjson2 Global config of key convertor. 2.8+ supports key convertor: fastjson2/jackson/jackson3 (fastjson is also available, which uses fastjson2 internally). You can use none only in the case of @CreateCache(cacheType=CacheType.LOCAL), in this situation equals is used to distinguish key. Method caching must specify a keyConvertor
jetcache.[local/remote].${area}.valueEncoder java Global config of value encoder, only remote cache need it. 2.8+ supports valueEncoder: java/kryo/kryo5 (kryo and kryo5 both use kryo5 implementation)
jetcache.[local/remote].${area}.valueDecoder java Global config of value decoder, only remote cache need it. 2.8+ supports valueDecoder: java/kryo/kryo5 (kryo and kryo5 both use kryo5 implementation)
jetcache.[local/remote].${area}.limit 100 Global config of max elements in local memory for each Cache instance. Only local cache need it.
jetcache.[local/remote].${area}.expireAfterWriteInMillis infinity Global config of write expire time, in millis.
jetcache.remote.${area}.broadcastChannel n/a jetcahe2.7 support invalidate local cache of other jvm after updatation (cacheType = CacheType.BOTH), this config specify broadcast channel, this feature disabled if not set
jetcache.local.${area}.expireAfterAccessInMillis 0 Global config of read expire time, in millis. Need jetcache2.2+, only local cache support this feature. 0 indicates disabled read expire feature.
jetcache.decodeFilterEnabled true Master switch for deserialization filter, enabled by default. Set to false to restore old behavior (NOT recommended)
jetcache.decodeFilterAllowPatterns undefined User-defined allow patterns appended to the default allow list. Three match modes are supported (see below)
jetcache.decodeFilterDenyPatterns undefined User-defined deny patterns appended to the default deny list. Deny patterns always take precedence over allow patterns

The ${area} of the above table is the area attribute of @Cached and @CreateCache. Note that the default value of area attribute of the two annotation is "default".

There are multi place which the write expire time can be set:

  1. if a method like put in Cache interface sets expire, then use it.
  2. if not set in method like put, use default expire of the Cache instance
  3. the default expire of the Cache instance can be set in attribute on @CreateCache or @Cached, if not, JetCache use global config defaultExpireInMillis defined in yml(for instance @Cached(cacheType=local) use jetcache.local.default.expireAfterWriteInMillis), if there is not defined yet then use infinity.

Deserialization Filter Configuration

JetCache 2.8.x enables deserialization filter by default. The filter maintains both an allow list and a deny list. The deny list takes the highest priority and cannot be overridden by user-defined allow patterns.

The default allow list:

Pattern Match mode Description
java.lang Package match Direct classes only (e.g. String, Integer), excluding subpackages (reflect, invoke)
java.util. Prefix match Collections and subpackages (e.g. HashMap, concurrent.ConcurrentHashMap)
java.time. Prefix match Date/time classes (e.g. LocalDate, Duration)
java.math Package match BigDecimal, BigInteger, etc. (no subpackages exist)
java.net Package match URI, URL, etc. Direct classes only, excluding subpackages
com.alicp.jetcache. Prefix match JetCache internal classes

If your cached values contain custom classes, you need to configure the filter:

yaml
jetcache:
  decodeFilterEnabled: true  # default true, can set false to disable
  decodeFilterAllowPatterns:
    - com.example.          # prefix match: all classes under com.example and subpackages
    - org.myapp.dto         # package match: direct classes in org.myapp.dto (no subpackages)
    - org.myapp.dto.UserDTO # exact match: only this specific class
  decodeFilterDenyPatterns:
    - com.example.internal.      # block this package and its subpackages
    - org.myapp.dto.SecretDTO    # block one specific class

Filter rules: The built-in deny list includes known deserialization gadget chains (e.g. Commons Collections, Spring AOP, Hibernate, Groovy, JNDI/RMI, C3P0, etc.), dangerous classes like java.lang.Runtime and ProcessBuilder, and JDK internal packages like com.sun. and sun.. Deny patterns cannot be overridden by allow rules. If necessary, you can remove specific deny patterns via DecodeFilter.getDefault().removeDenyPatterns(...) (evaluate security risks yourself).

Pattern matching rules:

  • Prefix match (ends with .): matches all classes in the package and all subpackages. For example, com.example. matches com.example.Foo, com.example.sub.Bar, etc.
  • Package match (no trailing ., not a full class name): matches only classes directly in the package, excluding subpackages. For example, com.example matches com.example.Foo but not com.example.sub.Bar. The default allow list uses this mode for java.lang and java.net.
  • Exact match (full class name): matches only one specific class. For example, org.myapp.dto.UserDTO matches only org.myapp.dto.UserDTO.

Tip: If your custom classes are spread across multiple packages, prefix match (ending with .) is the most convenient option.

You can also configure programmatically (non-Spring Boot scenario):

java
DecodeFilter filter = DecodeFilter.getDefault();
filter.addAllowPatterns("com.example.");

If a class is blocked during deserialization, an ERROR log is emitted (containing the rejected class name and configuration examples), and an exception is thrown. Kryo and JSON paths throw DecodeFilterException; Java serialization throws InvalidClassException (JDK internal behavior).

Notes:

  • JDK dynamic proxy classes (e.g. jdk.proxy1.$Proxy0) are not in the default allow list. If you cache proxy objects (e.g. Spring AOP proxies), add an allow rule (e.g. jdk.proxy.).
  • Packages like java.rmi., javax.naming., java.lang.reflect., javax.script., javax.management. are in the built-in deny list — adding allow rules cannot override them. Packages like java.io, java.beans (except EventHandler) are not in the allow list but also not in the deny list; they can be added via decodeFilterAllowPatterns or addAllowPatterns.
  • If you need to block additional packages or classes beyond the built-in deny list, add them via decodeFilterDenyPatterns or addDenyPatterns.

File: docs/EN/CreateCache.md

CacheManager

use CacheManager to create Cache instance, it returns same Cache instance with @Cached if area and name equals.

notice: in jetcache 2.7 CreateCache annotation is deprecated, use CacheManager.getOrCreateCache(QuickConfig) instead

example:

java
@Autowired
private CacheManager cacheManager;
private Cache<String, UserDO> userCache;

@PostConstruct
public void init() {
    QuickConfig qc = QuickConfig.newBuilder("userCache")
        .expire(Duration.ofSeconds(100))
        .cacheType(CacheType.BOTH) // two level cache
        .syncLocal(true) // invalidate local cache in all jvm process after update
        .build();
    userCache = cacheManager.getOrCreateCache(qc);
}

CreateCache annotation

You can use @CreateCache annotation to create and configure a Cache instance in a Spring Bean.
For Example:

java
@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;

The attributes of @CreateCache

attribute default value description
area “default” If you want to use multi backend cache system, you can setup multi "cache area" in configuration, this attribute specifies the name of the "cache area" you want to use.
name undefined The name of this Cache instance, optional. If you do not specify, JetCache will auto generate one. The name is used to display statistics information and as part of key prefix when using a remote cache. If two @CreateCache have same name and area, they will point to same Cache instance.
expire undefined The default expire time of this Cache instance. Use global config if the attribute value is absent, and if the global config is not defined either, use infinity.
timeUnit TimeUnit.SECONDS Specify the time unit of expire
cacheType CacheType.REMOTE Type of the Cache instance. May be CacheType.REMOTE, CacheType.LOCAL, CacheType.BOTH. Use two level cache (local+remote) when value is CacheType.BOTH.
localLimit undefined Specify max elements in local memory when cacheType is CacheType.LOCAL or CacheType.BOTH. Use global config if the attribute value is absent, and if the global config is not defined either, use 100.
serialPolicy undefined Specify the serialization policy of remote cache when cacheType is CacheType.REMOTE or CacheType.BOTH. The JetCache build-in serialPolicy are SerialPolicy.JAVA or SerialPolicy.KRYO. Use global config if the attribute value is absent, and if the global config is not defined either, use SerialPolicy.JAVA.
keyConvertor undefined Specify the key convertor. Used to convert the complex key object. The JetCache build-in keyConvertor are KeyConvertor.FASTJSON, KeyConvertor.JACKSON, KeyConvertor.JACKSON3 or KeyConvertor.NONE. NONE indicate do not convert, FASTJSON will use fastjson2 to convert key object to a string (since 2.8, fastjson1 is removed). Use global config if the attribute value is absent.

Default values

There are some attributes in the above table has no default value. JetCache will use global config value when you not specify the value in annotation.
See Configuration details for more information about global config.


File: docs/EN/Embedded.md

The keyConvertor is optional if you are using Cache API in jetcache-core,
the local cache uses equals to identity the key.
You must specify keyConvertor if you use annotations in jetcache-anno, such as @Cached and @CreateCache.

There are two local cache (class AbstractEmbeddedCache) implementation in JetCache.

LinkedHashMapCache

LinkedHashMapCache is a simple implementation in JetCache.
It is built on java.util.LinkedHashMap and supports LRU algorithm.

java
Cache<Long, OrderDO> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();

CaffeineCache

CaffeineCache is built on caffeine cache.

java
Cache<Long, OrderDO> cache = CaffeineCacheBuilder.createCaffeineCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();

File: docs/EN/GettingStarted.md

Create Cache instance

Create a Cache instance using @CreateCache annotation with default TTL 100 seconds.

java
@Autowired
private CacheManager cacheManager;

private Cache<Long, UserDO> userCache;

@PostConstruct
public void init() {
    QuickConfig qc = QuickConfig.newBuilder("userCache") // name used in statistical information
        .expire(Duration.ofSeconds(100))
        //.cacheType(CacheType.BOTH) // create two level cache
        //.localLimit(100) // limit for local cache elements, only used for CacheType.LOCAL and CacheType.BOTH
        //.syncLocal(true) // invalidate local cache in other JVM after updates, only used for CacheType.BOTH, need set broadcastChannel in configuration. 
        .build();
    userCache = cacheManager.getOrCreateCache(qc);
}

Then use Cache instance like a map:

java
UserDO user = userCache.get(123L);
userCache.put(123L, user);
userCache.remove(123L);

Create method cache

@Cached can be used to add method cache for a method.
JetCache use Spring AOP to generate proxy to support method cache。
The @Cached annotation can be add on interface method or class method, but the interface or class must defined as a Spring bean.

java
public interface UserService {
    @Cached(name="UserService.getUserById", expire = 3600)
    User getUserById(long userId);
}

Basic configuration (using Spring Boot)

This example using jedis to accessing redis(or you can consider using lettuce client):

POM

xml
<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>${jetcache.latest.version}</version>
</dependency>

Create a Spring Boot style configuration file application.yml and put it into the resource dir.

text
jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson2 #other choose:fastjson(same as fastjson2)/jackson/jackson3
  remote:
    default:
      type: redis
      keyConvertor: fastjson2 #other choose:fastjson(same as fastjson2)/jackson/jackson3
      broadcastChannel: projectA
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: 127.0.0.1
      port: 6379

Note: JetCache 2.8.x enables the deserialization security filter by default. The configuration above uses the java serializer. If your cached values contain custom classes (e.g. UserDO, OrderDO, etc.), deserialization will be blocked by the filter. You need to add decodeFilterAllowPatterns to allow your classes:

yaml
jetcache:
  decodeFilterAllowPatterns:
    - com.company.mypackage.  # allow all classes under this package

If you also need to block additional packages or classes, configure decodeFilterDenyPatterns. See the "Deserialization Filter Configuration" section in the configuration docs for details. You can set jetcache.decodeFilterEnabled: false to disable the filter temporarily (NOT recommended in production).

Then create the application class of Spring Boot:

java
package com.company.mypackage;

import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class MySpringBootApp {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApp.class);
    }
}

The @EnableMethodCache and @EnableCreateCacheAnnotation annotation activate @Cached and @CreateCache respectively.
Other code are same with standard Spring Boot Application. This class can run directly using main method.

Basic configuration (without Spring Boot)

This example using jedis to accessing redis:

xml
<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-anno</artifactId>
    <version>${jetcache.latest.version}</version>
</dependency>
<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-redis</artifactId>
    <version>${jetcache.latest.version}</version>
</dependency>

The following code configure JetCache using hand coding, then you can use @CreateCache and @Cached in you beans.

text
/* Detailed source-code truncated for AI context efficiency. */

read more


File: docs/EN/MethodCache.md

The method caching in JetCache is similar like Spring Cache. JetCache provide naitve TTL support and two level cache support.

Add @Cached on a method of a Spring bean to enable method caching. Since 2.4 @CacheUpdate and @CacheInvalidate are introduced for removing and updating method cache. You can even add the annotation on the interface that the bean implements.

java
public interface UserService {
    @Cached(name="userCache.", key="#userId", expire = 3600)
    User getUserById(long userId);

    @CacheUpdate(name="userCache.", key="#user.userId", value="#user")
    void updateUser(User user);

    @CacheInvalidate(name="userCache.", key="#userId")
    void deleteUser(long userId);
}

the key and value attribute use SpEL script. To enable use parameter name such as key="#userId", the -parameters javac compiler flag should be set, otherwise use index to access parameters like key="args[0]".

The attributes of @Cached are similar with @CreateCache except @Cached has more attributes:

attribute default value description
area “default” If you want to use multi backend cache system, you can setup multi "cache area" in configuration, this attribute specifies the name of the "cache area" you want to use.
name undefined The unique name of this Cache instance in an area, optional. If you do not specify, JetCache will auto generate one. The name is used to display statistics information and as part of key prefix when using a remote cache.
key undefined use SpEL script to specify the key. If not specified, JetCache will auto generate one using all method parameters.
expire undefined The expire time. Use global config if the attribute value is absent, and if the global config is not defined either, use infinity instead.
timeUnit TimeUnit.SECONDS Specify the time unit of expire
cacheType CacheType.REMOTE Type of the Cache instance. May be CacheType.REMOTE, CacheType.LOCAL, CacheType.BOTH. Create a two level cache (local+remote) when value is CacheType.BOTH.
localLimit undefined Specify max elements in local memory when cacheType is CacheType.LOCAL or CacheType.BOTH. Use global config if the attribute value is absent, and if the global config is not defined either, use 100 instead.
localExpire undefined Only use with cacheType=CacheType.BOTH, specify a different local expire (typically less than expire) for local cache
serialPolicy undefined Specify the serialization policy of remote cache when cacheType is CacheType.REMOTE or CacheType.BOTH. The JetCache build-in serialPolicy are SerialPolicy.JAVA or SerialPolicy.KRYO. Use global config if the attribute value is absent, and if the global config is not defined either, use SerialPolicy.JAVA instead.
keyConvertor undefined Specify the key convertor. Used to convert the complex key object. The JetCache build-in keyConvertor are KeyConvertor.FASTJSON, KeyConvertor.JACKSON, KeyConvertor.JACKSON3 or KeyConvertor.NONE. NONE indicate do not convert, FASTJSON will use fastjson2 to convert key object to a string (since 2.8, fastjson1 is removed). Use global config if the attribute value is absent.
enabled true Specify whether the method caching is enabled. If set to false, you can enable it in thread context using CacheContext.enableCache(Supplier<T> callback)
cacheNullValue false Specify whether a null value should be cached.
condition undefined Expression script used for conditioning the method caching, the cache is not used when evaluation result is false. Can't refer return value of real method.
postCondition undefined Expression script used for conditioning the method cache updating, the cache updating action is vetoed when the evaluation result is false. Evaluation occurs after real method invocation so we can refer #result in script.

@CacheInvalidate attribute table:

attribute default value description
area “default” If you want to use multi backend cache system, you can setup multi "cache area" in configuration, this attribute specifies the name of the "cache area" you want to use.
name undefined The unique name of this Cache instance in an area. refer to name of @Cached.
key undefined use SpEL script to specify the key.
condition undefined Expression script used for conditioning the cache operation, the operation is vetoed when evaluation result is false. Evaluation occurs after real method invocation so we can refer #result in script.

@CacheUpdate attribute table:

attribute default value description
area “default” If you want to use multi backend cache system, you can setup multi "cache area" in configuration, this attribute specifies the name of the "cache area" you want to use.
name undefined The unique name of this Cache instance in an area. refer to name of @Cached.
key undefined use SpEL script to specify the key.
value undefined use SpEL script to specify the value.
condition undefined Expression script used for conditioning the cache operation, the operation is vetoed when evaluation result is false. Evaluation occurs after real method invocation so we can refer #result in script.

Note that remote cache operation related to @CacheUpdate and @CacheInvalidate may fail, so it is important to set the '''expire''' attribute.

@CacheRefresh attribute table:

attribute default value description
refresh undefined interval of refreshment
timeUnit TimeUnit.SECONDS time unit
stopRefreshAfterLastAccess undefined if specified, refresh action will stop if the associated key is not accessed after specified time unit
refreshLockTimeout 60 seconds the distributed lock timeout when cacheType is REMOTE or BOTH

@CachePenetrationProtect:

This annotation used to synchronize concurrent cache loading operation.
Currently it only take effect only in each single JVM, that is, in one JVM there is only one thread load for same key,
other threads wait for the result.

There are some attributes in the above table has no default value. JetCache will use global config when you not specify the value in annotation.
See Configuration details for more information about global config.