【Luck-Report】缓存

导航:LuckReport专栏

一、快速了解

项目简介

Luck-Report 是一款基于开源项目 UReport2 重构的 Java 高性能报表引擎,通过迭代单元格可以实现任意复杂的中国式报表。相较于 UReport2,Luck-Report 在技术架构上进行了全新升级,后端基于 SpringBoot 框架开发、前端采用 Vue 框架构建,技术选型贴合当下主流项目开发标准,可精准适配各类实际开发需求。

Luck-Report 提供了全新的基于网页的报表设计器,可以在 Chrome、Firefox、Edge 等各种主流浏览器运行(IE 浏览器除外)。使用 Luck-Report,打开浏览器即可完成各种复杂报表的设计制作。

Luck-Report 基于 Apache-2.0 开源协议开源

在线体验

二、 缓存

分布式或集群系统需采用分布式缓存方案,以保障多节点间报表缓存数据的一致性,下文以 Redis 为例进行说明

添加 Redis 依赖 [​](#添加 Redis 依赖)

pom.xml 中添加 Spring Data Redis 依赖:

xml

复制代码
<!-- Redis 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

修改配置文件,禁用本地缓存

application.yml 中添加以下配置,禁用本地缓存:

yaml

复制代码
luck-report:
  # ...
  # 禁用本地缓存,分布式/集群环境必须配置
  disableLocalReportCache: true

注入 Redis 工具类 Bean [​](#注入 Redis 工具类 Bean)

创建 Redis 配置类,注入 RedisTemplate Bean,序列化策略采用 JSON:

java

复制代码
package com.luck.product.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis 配置类,用于配置 RedisTemplate
 *
 * @author luckyPools
 * @since 2017年3月8日
 */
@Configuration
public class RedisConfig {

    /**
     * 配置 RedisTemplate Bean。
     * 设置 Key 使用 String 序列化器,Value 使用 JSON 序列化器
     *
     * @param connectionFactory Redis 连接工厂,由 Spring Boot 自动注入
     * @return 配置好的 RedisTemplate 实例
     */
    @Bean("remoteRedisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();

        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);

        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

实现 ReportCache 接口 [​](#实现 ReportCache 接口)

创建 Redis 缓存实现类,实现 ReportCache 接口:

java

复制代码
package com.luck.product.boot.cache;

import com.luck.report.core.cache.ReportCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis 缓存服务实现,基于 Spring Data Redis 提供分布式缓存能力
 *
 * @author luckyPools
 * @since 2017年3月8日
 */
@Service
public class RedisCache implements ReportCache {

    private static final Logger log = LoggerFactory.getLogger(RedisCache.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 检查 Redis 缓存服务是否可用。
     *
     * @return Redis 不可用返回 true,可用返回 false
     */
    @Override
    public boolean disabled() {
        try {
            redisTemplate.getConnectionFactory().getConnection().ping();
            return false;
        } catch (Exception e) {
            log.error("检查 Redis 连接状态失败", e);
            return true;
        }
    }

    /**
     * 根据键获取缓存值。
     *
     * @param key 缓存键,不能为空
     * @param <T> 返回值类型
     * @return 缓存值,不存在或已过期返回 null
     */
    @Override
    public <T> T get(String key) {
        if (key == null) {
            return null;
        }
        try {
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                return null;
            }
            @SuppressWarnings("unchecked")
            T result = (T) value;
            return result;
        } catch (Exception e) {
            log.error("获取缓存失败,key: {}", key, e);
            return null;
        }
    }

    /**
     * 根据键获取缓存值并转换为指定类型。
     *
     * @param key   缓存键,不能为空
     * @param clazz 目标类型,不能为空
     * @param <T>   返回值类型
     * @return 缓存值,不存在或已过期返回 null
     */
    @Override
    public <T> T get(String key, Class<T> clazz) {
        if (key == null || clazz == null) {
            return null;
        }
        try {
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                return null;
            }
            if (clazz.isInstance(value)) {
                return clazz.cast(value);
            }
            return null;
        } catch (Exception e) {
            log.error("获取缓存并转换类型失败,key: {}, clazz: {}", key, clazz.getName(), e);
            return null;
        }
    }

    /**
     * 存入缓存,不设置过期时间。
     *
     * @param key   缓存键,不能为空
     * @param value 缓存值,不能为空
     * @param <T>   值类型
     */
    @Override
    public <T> void put(String key, T value) {
        if (key == null || value == null) {
            return;
        }
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            log.error("存入缓存失败,key: {}", key, e);
        }
    }

    /**
     * 存入缓存,指定过期时间。
     *
     * @param key   缓存键,不能为空
     * @param value 缓存值,不能为空
     * @param time  过期时间,单位:秒
     * @param <T>   值类型
     */
    @Override
    public <T> void put(String key, T value, long time) {
        if (key == null || value == null) {
            return;
        }
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
        } catch (Exception e) {
            log.error("存入缓存失败,key: {}, time: {}", key, time, e);
        }
    }

    /**
     * 判断缓存键是否存在。
     *
     * @param key 缓存键,不能为空
     * @return 存在且未过期返回 true,否则返回 false
     */
    @Override
    public boolean exists(String key) {
        if (key == null) {
            return false;
        }
        try {
            Boolean hasKey = redisTemplate.hasKey(key);
            return hasKey != null && hasKey;
        } catch (Exception e) {
            log.error("判断缓存键是否存在失败,key: {}", key, e);
            return false;
        }
    }

    /**
     * 删除指定缓存键。
     *
     * @param key 缓存键,不能为空
     */
    @Override
    public void remove(String key) {
        if (key == null) {
            return;
        }
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("删除缓存失败,key: {}", key, e);
        }
    }

    /**
     * 根据前缀查询匹配的所有缓存键。
     *
     * @param keyPatten 缓存键前缀,不能为空
     * @return 匹配的缓存键集合
     */
    @Override
    public Set<String> keys(String keyPatten) {
        if (keyPatten == null) {
            return null;
        }
        try {
            return redisTemplate.keys(keyPatten + "*");
        } catch (Exception e) {
            log.error("查询缓存键失败,keyPatten: {}", keyPatten, e);
            return null;
        }
    }

    /**
     * 设置缓存键的过期时间。
     *
     * @param key  缓存键,不能为空
     * @param time 过期时间,单位:秒
     * @return 设置成功返回 true,键不存在或已过期返回 false
     */
    @Override
    public boolean setExpire(String key, long time) {
        if (key == null || time <= 0) {
            return false;
        }
        try {
            Boolean result = redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return result != null && result;
        } catch (Exception e) {
            log.error("设置缓存过期时间失败,key: {}, time: {}", key, time, e);
            return false;
        }
    }
}

配置说明:

配置项 说明
disableLocalReportCache 设置为 true 禁用本地缓存,分布式环境必须配置

相关推荐
@菜菜_达7 小时前
jquery.inputmask插件介绍
前端·javascript·jquery
jiayong237 小时前
前端面试题库 - 浏览器与网络篇
前端·网络·面试
Csvn7 小时前
小程序开发:微信小程序与 uni-app 实战指南
前端
XiYang-DING7 小时前
【Spring】SpringMVC
java·后端·spring
想学习java初学者8 小时前
SpringBoot整合GS1编码解码
java·spring boot·后端
日月云棠8 小时前
2 快速入门实战指南
java·后端
日月云棠8 小时前
3 Dubbo 2.7 高级配置:检查控制、版本策略与协议选择
java·后端
摸鱼小李上线了8 小时前
vue项目页面添加水印实现方法
前端·javascript·vue.js
砍材农夫8 小时前
物联网 基于netty构建mqtt协议规范(主题通配符订阅)
java·前端·javascript·物联网·netty