SpringBoot使用Redis做集中式缓存

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_34845394/article/details/102622791

依赖

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

配置

关于 SpringBoot 中配置 Redis,本文不在赘述,请看:SpringBoot2.0.X配置Redis,本文配置也是在这个上面进行改造的。

1、启用缓存,在 Application 上添加 @EnableCaching 注解。

/*
 * 启用缓存功能
 * */
@EnableCaching
public class BuildingApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(BuildingApplication.class);
        SpringApplication.run(BuildingApplication.class, args);
    }
}

2、使 RedisConfig 继承 CachingConfigurerSupport,重写 keyGenerator 方法,并缓存配置管理器

RedisConfig.java

/**
 * Redis配置
 * <p>
 * 创建人:leigq <br>
 * 创建时间:2018-11-08 10:11 <br>
 * <p>
 * 修改人: <br>
 * 修改时间: <br>
 * 修改备注: <br>
 * </p>
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
     * <br/>
     * 配置参考:https://www.cnblogs.com/taiyonghai/p/9454764.html
     * <br/>
     * 使用参考:
     * <ul>
     *     <li>
     *         <a href='http://blog.didispace.com/springbootcache2/'>Spring Boot中的缓存支持(二)使用Redis做集中式缓存</a>
     *     </li>
     *     <li>
     *         <a href='http://blog.didispace.com/springbootcache1/'>Spring Boot中的缓存支持(一)注解配置与EhCache使用</a>
     *     </li>
     * </ul>
     * <p>
     */
    @Bean(name = "redisCacheKeyGenerator")
    @Primary
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    /**
     * 缓存配置管理器
     */
    @Bean(name = "redisCacheManager")
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 以锁写入的方式创建RedisCacheWriter对象
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory);
        /*
        设置CacheManager的Value序列化方式为Jackson2JsonRedisSerialize,
        RedisCacheConfiguration默认就是使用
        StringRedisSerializer序列化key,
        JdkSerializationRedisSerializer序列化value,
         */
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        objMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objMapper);
        RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer);
        // 创建默认缓存配置对象
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
        return new RedisCacheManager(writer, config);
    }
}

完整配置如下:

package com.blog.www.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置
 * <p>
 * 创建人:leigq <br>
 * 创建时间:2018-11-08 10:11 <br>
 * <p>
 * 修改人: <br>
 * 修改时间: <br>
 * 修改备注: <br>
 * </p>
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 使用 JacksonConfig 中的 objMapper,兼容 java8 时间
     *
     * @see JacksonConfig#getObjMapper()
     */
    private final ObjectMapper objMapper;

    public RedisConfig(@Qualifier(value = "objMapper") ObjectMapper objMapper) {
        this.objMapper = objMapper;
    }

    /**
     * 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
     * <br/>
     * 配置参考:https://www.cnblogs.com/taiyonghai/p/9454764.html
     * <br/>
     * 使用参考:
     * <ul>
     *     <li>
     *         <a href='http://blog.didispace.com/springbootcache2/'>Spring Boot中的缓存支持(二)使用Redis做集中式缓存</a>
     *     </li>
     *     <li>
     *         <a href='http://blog.didispace.com/springbootcache1/'>Spring Boot中的缓存支持(一)注解配置与EhCache使用</a>
     *     </li>
     * </ul>
     * <p>
     */
    @Bean(name = "redisCacheKeyGenerator")
    @Primary
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    /**
     * 缓存配置管理器
     */
    @Bean(name = "redisCacheManager")
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 以锁写入的方式创建RedisCacheWriter对象
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory);
        /*
        设置CacheManager的Value序列化方式为Jackson2JsonRedisSerialize,
        RedisCacheConfiguration默认就是使用
        StringRedisSerializer序列化key,
        JdkSerializationRedisSerializer序列化value,
         */
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        objMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objMapper);
        RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer);
        // 创建默认缓存配置对象
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
        return new RedisCacheManager(writer, config);
    }

    /**
     * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类, 生产环境不建议这样
     * <br/>
     * 参考:https://blog.csdn.net/m0_37893932/article/details/78259288
     * <br>创建人: leigq
     * <br>创建时间: 2018-11-08 10:12
     * <br>
     *
     * @param redisConnectionFactory redis连接工厂
     * @return RedisTemplate
     */
    @Bean(value = "redisTemp")
    @Primary
    public RedisTemplate<Object, Object> getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {

        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        objMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objMapper);

        // 以下代码为将 RedisTemplate 的 Value 序列化方式由 JdkSerializationRedisSerializer更换为 Jackson2JsonRedisSerializer
        // 此种序列化方式结果清晰、容易阅读、存储字节少、速度快,所以推荐更换
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 设置 key 的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        // 是否启用事务
//		redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }

}

测试

写个简单的根据用户id查询用户信息,使用 @Cacheable 注解来缓存,cacheNames 为缓存名称(必填),cacheManagerkeyGenerator 用我们刚才在 RedisConfig.java 中配置的。

// 确实可以缓存, @Cache* 注解使用详解:http://blog.didispace.com/springbootcache1/
@Cacheable(cacheNames = {"users"}, cacheManager = "redisCacheManager", keyGenerator = "redisCacheKeyGenerator")
public User getUser(Long id) {
    return userMapper.selectByPrimaryKey(id);
}
package com.blog.www;

import com.blog.www.base.BaseApplicationTests;
import com.blog.www.domain.entity.User;
import com.blog.www.service.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;

/**
 * Redis 缓存测试
 * <p>
 * 创建人:leigq <br>
 * 创建时间:2018-12-08 14:48 <br>
 * <p>
 * 修改人: <br>
 * 修改时间: <br>
 * 修改备注: <br>
 * </p>
 */
public class RedisCacheTest extends BaseApplicationTests {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

    /**
     * 用户缓存测试
     */
    @Test
    public void cacheUserTest() {
        // 可根据cacheManager查看具体使用哪种缓存
        log.warn(cacheManager.getCache("user").getName());
        User user1 = userService.getUser(1L);
        User user2 = userService.getUser(1L);
        User user3 = userService.getUser(1L);
        log.warn("user1 is [{}]", user1);
        log.warn("user2 is [{}]", user2);
        log.warn("user3 is [{}]", user3);
    }
}

BaseApplicationTests.java

package com.blog.www.base;

import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 测试基类,其他类继承此类
 * <br/>
 * @author     :leigq
 * @date       :2019/8/13 17:17
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public abstract class BaseApplicationTests {

    protected Logger log = LoggerFactory.getLogger(this.getClass());

    private Long time;

    @Before
    public void setUp() {
        this.time = System.currentTimeMillis();
        log.info("==> 测试开始执行 <==");
    }

    @After
    public void tearDown() {
        log.info("==> 测试执行完成,耗时:{} ms <==", System.currentTimeMillis() - this.time);
    }
}

测试结果如下:

20191018114040.png

20191018114135.png

我的项目是使用 MyBatis 并且打开了SQL执行日志打印,可以看到,第一次查询打印了SQL,第2、3次的时候没打印,说明第2、3次直接走的缓存。

常用注解

20191018114559.png

注解使用详解:http://blog.didispace.com/springbootcache1/

猜你喜欢

转载自blog.csdn.net/qq_34845394/article/details/102622791