[笔记] Java Cache([note] Java cache)

依赖

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

                <dependency>
                        <groupId>com.github.ben-manes.caffeine</groupId>
                        <artifactId>jcache</artifactId>
                </dependency>

里面包含了

        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
        </dependency>

如果只指定 cache-api 这个依赖,编译不会报错,但运行报错,因为这个只定义接口没有实现

也可以使用其他实现

定义要缓存的类,必须是可序列化的

package com.example.demo.entity;

import com.example.demo.utils.Logger;

import java.io.Serializable;


public class MyObject implements Serializable {
    private final String name;
    private final String id;

    public MyObject(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public void display() {
        Logger.message("This is " + name + " id is " + id);
    }
}

定义 Factory,通过 @Bean 注解创建一个 Cache,这个 Cache 的 key 是 String,对应 value 是 MyObject

setExpiryPolicyFactory 决定了缓存失效时间

setCacheLoaderFactory 决定了如果 key 在缓存里不存在或失效的话,要怎么去获取

package com.example.demo.factory;

import com.example.demo.entity.MyObject;
import com.example.demo.utils.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.cache.Cache;
import javax.cache.Caching;
import javax.cache.configuration.Configuration;
import javax.cache.configuration.FactoryBuilder;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;

import java.util.concurrent.TimeUnit;

import static org.springframework.web.context.WebApplicationContext.SCOPE_APPLICATION;


@Component
public class MyCacheFactory {
    CachingProvider cachingProvider = Caching.getCachingProvider();

    @Bean
    @Scope(SCOPE_APPLICATION)
    public Cache<String, MyObject> produce() {
        Logger.message("MyCacheFactory produce conn");

        Configuration<String, MyObject> config =
                new MutableConfiguration<String, MyObject>()
                        .setTypes(String.class, MyObject.class)
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.HOURS, 1)))
                        .setReadThrough(true)
                        .setCacheLoaderFactory(FactoryBuilder.factoryOf(MyObjectLoader.class));

        return cachingProvider.getCacheManager().createCache("MyObject", config);
    }
}

定义 MyObjectLoader 决定如果缓存不存在或过期,如何获取

package com.example.demo.factory;

import com.example.demo.entity.MyObject;
import com.example.demo.utils.Logger;

import javax.cache.integration.CacheLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class MyObjectLoader implements CacheLoader<String, MyObject> {

    @Override
    public MyObject load(String key) {
        Logger.message("load " + key);
        return this.get(key);
    }

    @Override
    public Map<String, MyObject> loadAll(Iterable<? extends String> keys) {
        Logger.message("loadAll " + keys);

        Map<String, MyObject> mapper = new HashMap<>();

        for (String key : keys) {
            mapper.put(key, get(key));
        }

        return mapper;
    }

    private MyObject get(String key) {
        return new MyObject(key, UUID.randomUUID().toString());
    }
}

应用,@Autowired 自动找到了 @Bean 注解的返回类型为 Cache<String, MyObject> 的函数所创建的实例

    @Autowired
    private Cache<String, MyObject> cache;

    @PostConstruct
    public void init() {
        this.verifyCache();    // 第一次,缓存里找不到,会调用 load 和 loadAll
        Logger.message("\nbetween 2 verify cache");
        this.verifyCache();    // 第二次,缓存里找得到,直接返回缓存
    }

    public void verifyCache() {
        Logger.message("\n================\nverifyCache\n================\n");

        cache.get("a").display();
        cache.get("b").display();

        Set<String> set = new HashSet<>();
        set.add("c");
        set.add("d");
        Map<String, MyObject> mapper = cache.getAll(set);
        for (Map.Entry<String, MyObject> entry : mapper.entrySet()) {
           entry.getValue().display();
        }
    }

结果

================
verifyCache
================

load a
This is a id is 0cde6a55-04e9-498d-bcc6-7ff01c18ffe8
load b
This is b id is 89e86e9b-dbe0-4140-a0a8-449344fbba5d
loadAll [c, d]
This is c id is 42c101a8-40d6-4998-8905-758febf2841f
This is d id is 970e537e-9e5d-4c01-9226-e0261e1d0747

between 2 verify cache

================
verifyCache
================

This is a id is 0cde6a55-04e9-498d-bcc6-7ff01c18ffe8
This is b id is 89e86e9b-dbe0-4140-a0a8-449344fbba5d
This is c id is 42c101a8-40d6-4998-8905-758febf2841f
This is d id is 970e537e-9e5d-4c01-9226-e0261e1d0747
————————

rely on

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

                <dependency>
                        <groupId>com.github.ben-manes.caffeine</groupId>
                        <artifactId>jcache</artifactId>
                </dependency>

It contains

        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
        </dependency>

If only the cache API dependency is specified, the compilation will not report an error, but the operation will report an error, because this only defines the interface and does not implement it

Other implementations can also be used

Defines the class to cache, which must be serializable

package com.example.demo.entity;

import com.example.demo.utils.Logger;

import java.io.Serializable;


public class MyObject implements Serializable {
    private final String name;
    private final String id;

    public MyObject(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public void display() {
        Logger.message("This is " + name + " id is " + id);
    }
}

Define factory and create a cache with @ bean annotation. The key of this cache is string and the corresponding value is MyObject

Setexpirypolicyfactory determines the cache expiration time

Setcacheloaderfactory determines how to get the key if it does not exist or becomes invalid in the cache

package com.example.demo.factory;

import com.example.demo.entity.MyObject;
import com.example.demo.utils.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.cache.Cache;
import javax.cache.Caching;
import javax.cache.configuration.Configuration;
import javax.cache.configuration.FactoryBuilder;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;

import java.util.concurrent.TimeUnit;

import static org.springframework.web.context.WebApplicationContext.SCOPE_APPLICATION;


@Component
public class MyCacheFactory {
    CachingProvider cachingProvider = Caching.getCachingProvider();

    @Bean
    @Scope(SCOPE_APPLICATION)
    public Cache<String, MyObject> produce() {
        Logger.message("MyCacheFactory produce conn");

        Configuration<String, MyObject> config =
                new MutableConfiguration<String, MyObject>()
                        .setTypes(String.class, MyObject.class)
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.HOURS, 1)))
                        .setReadThrough(true)
                        .setCacheLoaderFactory(FactoryBuilder.factoryOf(MyObjectLoader.class));

        return cachingProvider.getCacheManager().createCache("MyObject", config);
    }
}

Define myobjectloader to determine how to get if the cache does not exist or expires

package com.example.demo.factory;

import com.example.demo.entity.MyObject;
import com.example.demo.utils.Logger;

import javax.cache.integration.CacheLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class MyObjectLoader implements CacheLoader<String, MyObject> {

    @Override
    public MyObject load(String key) {
        Logger.message("load " + key);
        return this.get(key);
    }

    @Override
    public Map<String, MyObject> loadAll(Iterable<? extends String> keys) {
        Logger.message("loadAll " + keys);

        Map<String, MyObject> mapper = new HashMap<>();

        for (String key : keys) {
            mapper.put(key, get(key));
        }

        return mapper;
    }

    private MyObject get(String key) {
        return new MyObject(key, UUID.randomUUID().toString());
    }
}

In the application, @ Autowired automatically found the return type of @ bean annotation as cache & lt; String, MyObject> The instance created by the function

    @Autowired
    private Cache<String, MyObject> cache;

    @PostConstruct
    public void init() {
        this.verifyCache();    // 第一次,缓存里找不到,会调用 load 和 loadAll
        Logger.message("\nbetween 2 verify cache");
        this.verifyCache();    // 第二次,缓存里找得到,直接返回缓存
    }

    public void verifyCache() {
        Logger.message("\n================\nverifyCache\n================\n");

        cache.get("a").display();
        cache.get("b").display();

        Set<String> set = new HashSet<>();
        set.add("c");
        set.add("d");
        Map<String, MyObject> mapper = cache.getAll(set);
        for (Map.Entry<String, MyObject> entry : mapper.entrySet()) {
           entry.getValue().display();
        }
    }

result

================
verifyCache
================

load a
This is a id is 0cde6a55-04e9-498d-bcc6-7ff01c18ffe8
load b
This is b id is 89e86e9b-dbe0-4140-a0a8-449344fbba5d
loadAll [c, d]
This is c id is 42c101a8-40d6-4998-8905-758febf2841f
This is d id is 970e537e-9e5d-4c01-9226-e0261e1d0747

between 2 verify cache

================
verifyCache
================

This is a id is 0cde6a55-04e9-498d-bcc6-7ff01c18ffe8
This is b id is 89e86e9b-dbe0-4140-a0a8-449344fbba5d
This is c id is 42c101a8-40d6-4998-8905-758febf2841f
This is d id is 970e537e-9e5d-4c01-9226-e0261e1d0747