SpringBoot使用Redis做集中式緩存

依賴

<!--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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章