Spring Boot整合Redis緩存

spring-boot-demo-cache-redis

此 demo 主要演示了 Spring Boot 如何整合 redis,操作redis中的數據,並使用redis緩存數據。連接池使用 Lettuce。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-demo-cache-redis</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-boot-demo-cache-redis</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.xkcoding</groupId>
        <artifactId>spring-boot-demo</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
		<!-- data-redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <!--基於spring aop的方式 爲函數添加緩存的 框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- 對象池,使用redis時必須引入 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!-- 引入 jackson 對象json轉換 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring-boot-demo-cache-redis</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

spring:
  redis:
    host: localhost
    # 連接超時時間(記得添加單位,Duration)
    timeout: 10000ms
    # Redis默認情況下有16個分片,這裏配置具體使用的分片
    # database: 0
    lettuce:
      pool:
        # 連接池最大連接數(使用負值表示沒有限制) 默認 8
        max-active: 8
        # 連接池最大阻塞等待時間(使用負值表示沒有限制) 默認 -1
        max-wait: -1ms
        # 連接池中的最大空閒連接 默認 8
        max-idle: 8
        # 連接池中的最小空閒連接 默認 0
        min-idle: 0
  cache:
    # 一般來說是不用配置的,Spring Cache 會根據依賴的包自行裝配
    type: redis
logging:
  level:
    com.xkcoding: debug

RedisConfig.java

/**
 * <p>
 * redis配置
 * </p>
 *
 * @package: com.xkcoding.cache.redis.config
 * @description: redis配置
 */
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching //開啓緩存
public class RedisConfig {

    /**
     * 默認情況下的模板只能支持RedisTemplate<String, String>,也就是隻能存入字符串,因此支持序列化
     */
    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 配置使用註解的時候緩存配置,默認是序列化反序列化的形式,加上此配置則爲 json 形式
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 配置序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
    }
}

UserServiceImpl.java

/**
 * <p>
 * UserService
 * </p>
 *
 * @package: com.xkcoding.cache.redis.service.impl
 * @description: UserService 使用的是cache集成了redis是使用redis
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    /**
     * 模擬數據庫
     */
    private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();

    /**
     * 初始化數據
     */
    static {
        DATABASES.put(1L, new User(1L, "user1"));
        DATABASES.put(2L, new User(2L, "user2"));
        DATABASES.put(3L, new User(3L, "user3"));
    }

    /**
     * 保存或修改用戶
     *
     * @param user 用戶對象
     * @return 操作結果
     */
    @CachePut(value = "user", key = "#user.id")
    @Override
    public User saveOrUpdate(User user) {
        DATABASES.put(user.getId(), user);
        log.info("保存用戶【user】= {}", user);
        return user;
    }

    /**
     * 獲取用戶
     *
     * @param id key值
     * @return 返回結果
     */
    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Long id) {
        // 我們假設從數據庫讀取
        log.info("查詢用戶【id】= {}", id);
        return DATABASES.get(id);
    }

    /**
     * 刪除
     *
     * @param id key值
     */
    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(Long id) {
        DATABASES.remove(id);
        log.info("刪除用戶【id】= {}", id);
    }
}

RedisTest.java

主要測試使用 RedisTemplate 操作 Redis 中的數據:

  • opsForValue:對應 String(字符串)
  • opsForZSet:對應 ZSet(有序集合)
  • opsForHash:對應 Hash(哈希)
  • opsForList:對應 List(列表)
  • opsForSet:對應 Set(集合)
  • opsForGeo:** 對應 GEO(地理位置)
/**
 * <p>
 * Redis測試
 * </p>
 *
 * @package: com.xkcoding.cache.redis
 * @description: Redis測試
 * @author: yangkai.shen
 * @date: Created in 2018/11/15 17:17
 * @copyright: Copyright (c) 2018
 * @version: V1.0
 * @modified: yangkai.shen
 */
@Slf4j
public class RedisTest extends SpringBootDemoCacheRedisApplicationTests {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;

    /**
     * 測試 Redis 操作
     */
    @Test
    public void get() {
        // 測試線程安全,程序結束查看redis中count的值是否爲1000
        ExecutorService executorService = Executors.newFixedThreadPool(1000);
        IntStream.range(0, 1000).forEach(i -> executorService.execute(() -> stringRedisTemplate.opsForValue().increment("count", 1)));

        stringRedisTemplate.opsForValue().set("k1", "v1");
        String k1 = stringRedisTemplate.opsForValue().get("k1");
        log.debug("【k1】= {}", k1);

        // 以下演示整合,具體Redis命令可以參考官方文檔
        String key = "xkcoding:user:1";
        redisCacheTemplate.opsForValue().set(key, new User(1L, "user1"));
        // 對應 String(字符串)
        User user = (User) redisCacheTemplate.opsForValue().get(key);
        log.debug("【user】= {}", user);
    }
}

UserServiceTest.java

主要測試使用Redis緩存是否起效

/**
 * <p>
 * Redis - 緩存測試
 * </p>
 *
 * @package: com.xkcoding.cache.redis.service
 * @description: Redis - 緩存測試
 * @author: yangkai.shen
 * @date: Created in 2018/11/15 16:53
 * @copyright: Copyright (c) 2018
 * @version: V1.0
 * @modified: yangkai.shen
 */
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheRedisApplicationTests {
    @Autowired
    private UserService userService;

    /**
     * 獲取兩次,查看日誌驗證緩存
     */
    @Test
    public void getTwice() {
        // 模擬查詢id爲1的用戶
        User user1 = userService.get(1L);
        log.debug("【user1】= {}", user1);

        // 再次查詢
        User user2 = userService.get(1L);
        log.debug("【user2】= {}", user2);
        // 查看日誌,只打印一次日誌,證明緩存生效
    }

    /**
     * 先存,再查詢,查看日誌驗證緩存
     */
    @Test
    public void getAfterSave() {
        userService.saveOrUpdate(new User(4L, "測試中文"));

        User user = userService.get(4L);
        log.debug("【user】= {}", user);
        // 查看日誌,只打印保存用戶的日誌,查詢是未觸發查詢日誌,因此緩存生效
    }

    /**
     * 測試刪除,查看redis是否存在緩存數據
     */
    @Test
    public void deleteUser() {
        // 查詢一次,使redis中存在緩存數據
        userService.get(1L);
        // 刪除,查看redis是否存在緩存數據
        userService.delete(1L);
    }

}

參考資料

  • spring-data-redis 官方文檔:https://docs.spring.io/spring-data/redis/docs/2.0.1.RELEASE/reference/html/
  • redis 文檔:https://redis.io/documentation
  • redis 中文文檔:http://www.redis.cn/commands.html

StringRedisTemplate和RedisTemplate的區別及使用方法

Cache註解:

是spring自帶的緩存,本質就是緩存方法返回的結果,下次在訪問這個方法就是從緩存取.默認Spring Cache是緩存到jvm虛擬機緩存中,這樣的並不好,所有一般使用整合dataRedis一起使用,就是緩存在Redis中!

發佈了29 篇原創文章 · 獲贊 3 · 訪問量 1625
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章