springboot 使用spring cache缓存 和 使用fastjson配置redis系列化

springboot 使用spring cache缓存 和 使用fastjson配置redis系列化

此文档,是上篇文档"springboot 使用spring cache缓存 和 缓存数据落地到redis"的继续

此文使用fastjson完成spring cache对象的系列化

springboot 2.7.3

一、maven依赖

<properties>
    <java.version>1.8</java.version>
    <fastjson.version>1.2.83</fastjson.version>
    <codec.version>1.15</codec.version>
</properties>
<dependencies>
 	<dependency>
    	 <groupId>org.springframework.boot</groupId>
     	 <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- spring boot 缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- Spring boot Redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <!-- 加解密 使用到了DigestUtils.sha256Hex(jsonString) -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>${codec.version}</version>
    </dependency>
 </dependencies>

二、redis 默认配置文件

application.yml配置文件,使用redis的默认配置,可以不设置

#application.yml
## redis

# redis 定义变量
REDIS_HOST: localhost
REDIS_PORT: 6379
REDIS_PWD:

redis:
  #数据库索引
  database: ${REDIS_DB:0}
  host: ${REDIS_HOST:127.0.0.1}
  port: ${REDIS_PORT:6379}
  password: ${REDIS_PWD:}
  #连接超时时间
  timeout: 5000

三、spring cache redis配置类

主要做了两方面的内容:

  1. 对spring cache 进行使用fastjson配置系列化处理,解决了redis中显示的乱码,redis中可以正常显示内容了。

    配置了spring cache 异常处理

    配置了spring cache的KeyGenerator自定义生成

  2. 对Redis系列化,使用了fastjson进行系列化的配置,自定义了redis的 key-value系列化。

package space.goldchen.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Redis系列化等配置类:
 * 主要有对spring cache 的配置 和 使用fastjson系列化的配置
 * @author Goldchen
 * @create 2022-08-25 14:47
 */
@Slf4j
@Configuration
@EnableCaching
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {

    /**
     *  设置spring cache: 设置@Cacheable 序列化方式 和 设置 spring cache 数据默认过期时间,默认2小时;解决了redis中显示乱码的问题;
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        // 使用 fastjson替换默认系列化
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration.serializeValuesWith(
                // 设置使用 fastjson系列化
                RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
                //设置为2小时redis值过期
                .entryTtl(Duration.ofHours(2));
        return configuration;
    }

    /**
     *  设置spring cache: 注解@cacheable 重写 redis cache异常,只打印日志,当redis失败时,会重新请求应用
     *
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {

        // 异常处理,当redis异常时,只打印日志,但是程序正常走
        log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                log.error("Redis occur handleCacheGetError:key -> [{}]", key, exception);
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                log.error("Redis occur handleCachePutError:key -> [{}]", key, value, exception);
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                log.error("Redis occur handleCacheEvictError:key ->[{}]", key, exception);

            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                log.error("Redis occur handleCacheClearError:", exception);

            }
        };
    }



    /**
     *  设置spring cache: 注解 @Cacheable 的 redisKey 的自定义生成器,缓存时,会用这个生成key
     * 用法 :@Cacheable(cacheNames = "redisUser_cache", keyGenerator = "keyGenerator")
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {

        return (target, method, params) -> {
            Map<String, Object> container = new HashMap<>(8);
            Class<?> targetClass = target.getClass();
            // 包名称
            container.put("package", targetClass.getPackage());
            // 类地址
            container.put("class", targetClass.toGenericString());
            // 方法名
            container.put("methodName", method.getName());
            // 参数列表
            for (int i = 0; i < params.length; i++) {
                container.put(String.valueOf(i), params[i]);
            }
            // 转为JSON字符串
            String jsonString = JSON.toJSONString(container);
            // 做 SHA256 Hash计算,得到SHA256值作为Key
            return DigestUtils.sha256Hex(jsonString);
        };
    }

    /**
     * RedisTemplate 的注入配置
     * @param redisConnectionFactory
     * @return
     */
    @SuppressWarnings("all")
    @Bean(name = "redisTemplate")
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        // 序列化
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);

        // key的序列化,使用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);

        // fastjson 升级到 1.2.83 后需要指定序列化白名单.不然会报错:
        // com.alibaba.fastjson.JSONException: autoType is not support. space.goldchen.pojo.RedisUser
        ParserConfig.getGlobalInstance().addAccept("space.goldchen");
        return template;
    }

    /**
     * 重写序列化器: fastjson对redis中value的序列化
     */
    class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
        private final Class<T> clazz;

        FastJsonRedisSerializer(Class<T> clazz) {
            super();
            this.clazz = clazz;
        }

        @Override
        public byte[] serialize(T t) {
            if (t == null) {
                return new byte[0];
            }
            // 加上SerializerFeature.WriteClassName后,生成的json中会有对象的标记:
            // 如:@class:"space.goldchen.pojo.RedisUser"
            return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
        }

        @Override
        public T deserialize(byte[] bytes) {
            if (bytes == null || bytes.length <= 0) {
                return null;
            }
            String str = new String(bytes, StandardCharsets.UTF_8);
            return JSON.parseObject(str, clazz);
        }
    }

    /**
     * 重写序列化器: fastjson对redis中key的序列化
     */
    class StringRedisSerializer implements RedisSerializer<Object> {
        private final Charset charset;

        private StringRedisSerializer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }

        StringRedisSerializer() {
            this(StandardCharsets.UTF_8);
        }

        @Override
        public byte[] serialize(Object o) {
            String string = JSON.toJSONString(o);
            if (org.apache.commons.lang3.StringUtils.isBlank(string)) {
                return null;
            }
            string = string.replace("\"", "");
            return string.getBytes(charset);
        }

        @Override
        public Object deserialize(byte[] bytes) {
            return (bytes == null ? null : new String(bytes, charset));
        }
    }

}

四、测试

测试用的主体类

package space.goldchen.pojo;

import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.lang.reflect.Field;

/**
 * 测试redis的对象
 * @author Goldchen
 * @create 2022-09-03 16:02
 */
@Data
public class RedisUser {
    private Integer id;
    private String name;
    private Integer age;
    @Override
    public String toString() {
        ToStringBuilder builder = new ToStringBuilder(this);
        Field[] fields = this.getClass().getDeclaredFields();
        try {
            for (Field f : fields) {
                f.setAccessible(true);
                builder.append(f.getName(), f.get(this)).append("\n");
            }
        } catch (Exception e) {
            builder.append("toString builder encounter an error");
        }
        return builder.toString();
    }
}
package space.goldchen.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.web.bind.annotation.*;
import space.goldchen.pojo.RedisUser;

/**
 * cache 演示
 * @author goldchen
 * @create 2022-09-4 23:57
 * 类上设置后,相当于每个方法设置
 * @CacheConfig(cacheNames = "test_cache")
 */
@Slf4j
@RestController
@RequestMapping("/cache")
public class TestSpringCacheController {
/**
     * 测试对象的缓存,redis是否正常
     */
    @GetMapping("/getUser")
    @Cacheable(cacheNames = "redisUser_cache", key = "#user.id")
    public RedisUser getUser(@RequestBody RedisUser user){
        RedisUser redisUser = new RedisUser();
        redisUser.setId(user.getId());
        redisUser.setAge(18);
        redisUser.setName("空间1");
        return redisUser;
    }
    /**
     * 测试使用自定义Key生成
     * 测试对象的缓存,redis是否正常,KeyGenerator
     */
    @GetMapping("/getUser2")
    @Cacheable(cacheNames = "redisUser_cache", keyGenerator = "keyGenerator")
    public RedisUser getUser2(@RequestBody RedisUser user){
        RedisUser redisUser = new RedisUser();
        redisUser.setId(user.getId());
        redisUser.setAge(18);
        redisUser.setName("空间1");
        return redisUser;
    }
    
}

redis的配置完成,下一篇文章将进行redis的使用演示。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis是一种非关系型数据库,支持多种数据类型的存储,其中之一就是字符串类型。在Redis中,可以使用fastjson对对象进行序列化,然后将其存储为字符串类型的值。 以下是使用fastjson进行序列化和反序列化的示例代码: ```java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import redis.clients.jedis.Jedis; public class RedisUtil { private Jedis jedis; public RedisUtil() { jedis = new Jedis("localhost", 6379); } public void set(String key, Object value) { String jsonValue = JSON.toJSONString(value, SerializerFeature.WriteClassName); jedis.set(key, jsonValue); } public <T> T get(String key, Class<T> clazz) { String jsonValue = jedis.get(key); return JSON.parseObject(jsonValue, clazz); } } ``` 上述示例代码中,我们使用fastjson的JSON类进行序列化和反序列化。在序列化时,我们使用SerializerFeature.WriteClassName参数,这样可以将对象的类名写入生成的JSON字符串中,以便在反序列化时能够正确地识别出对象的类型。 使用示例: ```java public static void main(String[] args) { RedisUtil redisUtil = new RedisUtil(); // 存储对象 User user = new User(1, "张三", 20); redisUtil.set("user:1", user); // 获取对象 User user1 = redisUtil.get("user:1", User.class); System.out.println(user1); } ``` 在这个示例中,我们首先创建了一个User对象,并将其存储在Redis中。然后,我们通过get方法从Redis中获取了这个对象,并进行了打印输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Goldchenn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值