天天看点

在Springboot中手写一个简单的本地缓存并在启动时加载数据到缓存中

闲来无事,琢磨了一下,如果需要用到缓存,但是又刚好在没网络, 本地仓库又没有对应依赖的情况下,要怎么办。

这种情况下,就只能手动定义一个集合或Map,将要缓存的数据保存在里面。

但是会有不支持过期时间的问题。

所以就考虑了一下如何自定义一个缓存的工具类,可以实现类似单机版Redis的功能。

首先该类需要有保存数据和获取数据两个供用户使用的方法。

我的定义如下:

/**
 * 向缓存中保存数据
 * @param key 
 * @param value
 */
public void set(String key, Object value);

/**
 * 向缓存中保存数据
 * @param key 
 * @param value
 * @param expire 数据有效期(过期时间)
 */
public void set(String key, Object value, long expire);

/**
 * 从缓存中获取数据
 * @param key 
 */
public Object get(String key);
           

接下来是定义缓存的数据结构

这里我使用ConcurrentHashMap来保存数据,来应对并发情况。

该Map中的value项我定义为了CacheEntry. 这个类主要保存了该数据的值和过期时间。

class CacheEntry {
    private long expireTime;
    private Object value;
}
           

接下来是该类的完整代码:

@Component
public class LocalCache implements Serializable {
    /**
     * 缓存容器
     */
    private Map<String, CacheEntry> container = new ConcurrentHashMap<>();
   
    @Builder
    @Data
    static class CacheEntry {
        private long expireTime;
        private Object value;
    }

    /**
     * 向缓存中保存数据
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
		// 默认设置为永不过期,0代表永不过期
        CacheEntry cacheNode = CacheEntry.builder()
                .expireTime(0)
                .value(value)
                .build();
        
        // 将数据保存到容器中
        container.put(key, cacheNode);
    }
    
	/**
     * 向缓存中保存数据,包含过期时间
     * @param key 
     * @param value
     * @param expire 数据有效期(过期时间)
     */
    public void set(String key, Object value, long expire) {
        // 先根据参数,计算出过期的时间戳
        long expireTime = System.currentTimeMillis() + expire;
        
        // 将计算出的过期时间保存到CacheEntry中, 便于在取值的时候判断是否过期
        CacheEntry cacheEntry = CacheEntry.builder()
                .expireTime(expireTime)
                .value(value)
                .build();
        
        // 将数据保存到容器中
        container.put(key, cacheNode);
    }
	
    /**
     * 从缓存中获取数据
     * @param key 
     */
    public Object get(String key) {
        // 先获取数据
        CacheEntry cacheNode = container.get(key);
        
        // 如果为null,说明不存在
        if (cacheNode == null) {
            return null;
        }
        
        // 获取过期时间
        long expireTime = cacheNode.getExpireTime();
        
        // 如果没有设置过期时间 或者 没有到达过期时间, 则返回该数据
        if (expireTime == 0L || expireTime >= System.currentTimeMillis()) {
            return cacheNode.value;
        }
        
        // 其他情况下,说明该数据已过期,将该数据移除, 并返回null
        container.remove(key);
        return null;
    }

}
           

这里在类上使用了@Component注解,将该类注入到Spring容器中,可以直接通过@Autowired使用。

如果要在Springboot启动时,向缓存中保存数据,只需要将启动类 implements CommandLineRunner, 重写run方法即可。

@SpringBootApplication
public class LocalCacheApplication implements CommandLineRunner {

    @Autowired
    private LocalCache localCache;

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

    @Override
    public void run(String... args) throws Exception {
        localCache.set("test", "testValue");
        System.out.println(localCache.get("test"));
    }
}