写点什么

Java 设计模式如何优雅的使用本地缓存?

用户头像
张音乐
关注
发布于: 3 小时前
Java设计模式如何优雅的使用本地缓存?

一、为什么要选择 guava cache

1、缓存 Cache 和 ConcurrentMap 虽然类似,但又不完全一样。最根本的区别是,ConcurrentMap 会保存所有添加到其中的元素直到它们被明确的移除。而 Cache 通常可以配置一个自动化的回收策略去限制它的内存空间。

2、如果你还需要缓存满足以下几点要求

(1)、如果你打算牺牲更多内存来换取速度的提升。

(2)、缓存中的数据会频繁的被使用到。

(3)、Guava Cache 只会把数据存储在内存中(Guava Cache 是把数据存储于你运行的单个应用上,它不会把数据存储在文件或外部的服务器上)。

二、设计要求

1、需要满足为不同业务对象灵活创建缓存。

2、有效减少不同业务对象创建缓存, 查询缓存, 设置缓存这些步骤的代码冗余。

3、不同业务对象查询缓存业务代码与通用代码解偶。

三、常规用法

如下代码示例是 guava 缓存的常规用法, 顺序是先调用 CacheBuilder 的 newBuilder()方法, 然后构建出一个缓存对象, 可以看出, 如果每个需要缓存的业务对象都这样使用的话, 代码会非常臃肿, 并且例如判断是否为空等代码都是一模一样的. 没有必要每次都写一遍.

        // 常规方式调用        Cache<String, Optional<User>> baseCache = CacheBuilder.newBuilder().maximumSize(500).expireAfterAccess(7, TimeUnit.DAYS).build();        String key = "123456";        Optional<User> baseOptional = null;        if(StringUtils.isNotBlank(key)) {            baseOptional = baseCache.get(key, () -> {                System.out.println("[App]-[main]--------------> 没有命中缓存, 执行业务查询");                System.out.println("[App]-[main]--------------> 查询逻辑.... 此处我为了简便, 直接new 了一个user对象");                User user = new User(key, "张音乐");                System.out.println("[App]-[main]--------------> 查询结束");                return Optional.of(user);            });            baseCache.put(key, baseOptional);            System.out.println("[App]-[main]--------------> base=" + JSONObject.toJSONString(baseOptional.get()));        }
复制代码

四、设计思路

1、利用模板方法设计模式来实现业务代码剥离, 抽取出通用模板。

2、如果缓存中没有数据, 则执行业务方法进行查询, 可以看下面的示例代码。service 参数实际上是一个接口, 具体业务代码通过 interface 参数的形式传递进来执行, 实现解偶。

    /**     * 查询     * 如果缓存中没有数据, 则执行业务方法进行查询     * @param key     * @param service     * @return     */    public Optional<T> query(String key, ICache<T> service) {        try{            if(StringUtils.isBlank(key)) {                return Optional.empty();            }            return cacheHolder.get(key, () -> service.query(key));        }catch (Exception e) {            e.printStackTrace();        }        return Optional.empty();    }
复制代码


五、完整代码

引用依赖

        <dependency>            <groupId>com.google.guava</groupId>            <artifactId>guava</artifactId>            <version>29.0-jre</version>        </dependency>         <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.68</version>        </dependency>
复制代码

缓存模板

package com.biubiu.cache; import com.google.common.cache.Cache;import com.google.common.cache.CacheBuilder;import org.apache.commons.lang3.StringUtils; import java.util.Optional;import java.util.concurrent.TimeUnit; /** * @author :张音乐 * @date :Created in 2021/5/20 上午9:14 * @description:本地缓存 * @email: zhangyule1993@sina.com * @version: 1.0 */public class LocalCache<T> {    /**     * guava cache     */    private Cache<String, Optional<T>> cacheHolder;     private int maximumSize = 500;     private int duration = 7;     /**     * 配置缓存参数     * @param size     * @param duration     * @return     */    public LocalCache<T> setParameters(int size, int duration) {        this.maximumSize = size;        this.duration = duration;        return this;    }      /**     * 构建一个缓存     * @return     */    public LocalCache<T> build() {        cacheHolder = CacheBuilder.newBuilder().maximumSize(maximumSize).expireAfterAccess(duration, TimeUnit.DAYS).build();        return this;    }     /**     * 查询     * 如果缓存中没有数据, 则执行业务方法进行查询     * @param key     * @param service     * @return     */    public Optional<T> query(String key, ICache<T> service) {        try{            if(StringUtils.isBlank(key)) {                return Optional.empty();            }            Optional<T> value = cacheHolder.get(key, () -> service.query(key));            // 设置进入缓存            put(key, value);            return value;        }catch (Exception e) {            e.printStackTrace();        }        return Optional.empty();    }      /**     * 把值推到缓存中     * @param key     * @param optional     */    public void put(String key, Optional<T> optional) {        cacheHolder.put(key, optional);    }      /**     * 通用接口, 利用模板方法设计模式, 将业务方法抽取出来. 不同的业务 传递不同的查询逻辑.     * @param <T>     */    public interface ICache<T> {        /**         * 通用接口         * @param key         * @return         */        Optional<T> query(String key);    }}
复制代码

六、使用示例

package com.biubiu.cache; import com.alibaba.fastjson.JSONObject; import java.math.BigDecimal;import java.util.Optional; /** * @author :张音乐 * @date :Created in 2021/5/20 上午9:25 * @description:demo * @email: zhangyule1993@sina.com * @version: 1.0 */public class App {     /**     * 用户实体     */    static class User {         private String userId;         private String username;         public User() {        }         public User(String userId, String username) {            this.userId = userId;            this.username = username;        }         public String getUserId() {            return userId;        }         public void setUserId(String userId) {            this.userId = userId;        }         public String getUsername() {            return username;        }         public void setUsername(String username) {            this.username = username;        }    }     /**     * 订单实体     */    static class Order {        private String orderId;         private BigDecimal number;         public Order() {        }         public Order(String orderId, BigDecimal number) {            this.orderId = orderId;            this.number = number;        }         public String getOrderId() {            return orderId;        }         public void setOrderId(String orderId) {            this.orderId = orderId;        }         public BigDecimal getNumber() {            return number;        }         public void setNumber(BigDecimal number) {            this.number = number;        }    }     public static void main(String[] args) {        // 创建一个用户缓存        LocalCache<User> userCache = new LocalCache<User>().setParameters(500, 7).build();        String userId = "123456";        //查询, 验证缓存中每有缓存的时候是从哪里进行查询的        Optional<User> userOptional = userCache.query(userId, user -> getUserById(userId));        System.out.println("[App]-[main]--------------> user=" + JSONObject.toJSONString(userOptional.get()));         // 再验证数据是从缓存中查询还是从业务中查询        userOptional = userCache.query(userId, user -> getUserById(userId));        System.out.println("[App]-[main]--------------> user=" + JSONObject.toJSONString(userOptional.get()));         System.out.println();        // 创建一个订单缓存        LocalCache<Order> orderCache = new LocalCache<Order>().setParameters(500, 7).build();        String orderId = "TB123456";        //查询, 验证缓存中每有缓存的时候是从哪里进行查询的        Optional<Order> orderOptional = orderCache.query(orderId, order -> getOrderById(orderId));        System.out.println("[App]-[main]--------------> order=" + JSONObject.toJSONString(orderOptional.get()));        // 再验证数据是从缓存中查询还是从业务中查询        orderOptional = orderCache.query(orderId, order -> getOrderById(orderId));        System.out.println("[App]-[main]--------------> order=" + JSONObject.toJSONString(orderOptional.get()));     }      private static Optional<User> getUserById(String userId) {        System.out.println("[App]-[getUserById]--------------> 没有命中缓存, 执行业务查询");        System.out.println("[App]-[getUserById]--------------> 查询逻辑.... 此处我为了简便, 直接new 了一个user对象");        User user = new User(userId, "张音乐");        System.out.println("[App]-[getUserById]--------------> 查询结束");        return Optional.of(user);    }     private static Optional<Order> getOrderById(String orderId) {        System.out.println("[App]-[getOrderById]--------------> 没有命中缓存, 执行业务查询");        System.out.println("[App]-[getOrderById]--------------> 查询逻辑.... 此处我为了简便, 直接new 了一个order对象");        Order order = new Order(orderId, new BigDecimal("9.9"));        System.out.println("[App]-[getOrderById]--------------> 查询结束");        return Optional.of(order);    }}
复制代码

七、运行截图

从图中可以看出, 第一次没有命中缓存, 执行了业务查询, 第二次命中了缓存, 从缓存中获取的数据。


发布于: 3 小时前阅读数: 4
用户头像

张音乐

关注

求你关注我,别不识抬举.别逼我跪下来求你. 2021.03.28 加入

还未添加个人简介

评论

发布
暂无评论
Java设计模式如何优雅的使用本地缓存?