一篇掌握SpringBoot+SpringCache+Redis超詳細例項

一抹餘陽發表於2020-10-23

1、建立maven專案

2、pom檔案

<?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>

    <groupId>org.example</groupId>
    <artifactId>springWithRedis</artifactId>
    <version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin></plugins></build>

    <dependencies>

        <!-- lombok依賴 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

        <!-- spring整合redis依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>

        <!-- spring依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>

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

    </dependencies>
</project>

2、application.properties 配置檔案

# Redis資料庫索引(預設為0)
spring.redis.database=0
# Redis伺服器地址
spring.redis.host=**********

#配置快取相關
cache.default.expire-time=20000
cache.user.expire-time=1800
cache.user.name=suiyiqi

3、實體類

package com.cache.redis.entity;

import lombok.Data;
import java.io.Serializable;

/**
 * @author 一抹餘陽
 */
@Data
// 物件要序列化,不然可能會出現亂碼
public class UserInfo implements Serializable {

    private String id;

    private String nickName;

}

4、service

package com.cache.redis.service;

import com.cache.redis.entity.UserInfo;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import java.util.List;

/**
 * @author 一抹餘陽
 */
// @CacheConfig:定義公共設定
@CacheConfig(cacheNames="userInfo2")
public interface UserInfoService {

    // 1、@Cacheable:定義快取,用於觸發快取.
    // 該註解用於標註於方法之上用於標識該方法的返回結果需要被快取起來,
    // 標註於類之上標識該類中所有方法均需要將結果快取起來。
    // 該註解標註的方法每次被呼叫前都會觸發快取校驗,
    // 校驗指定引數的快取是否已存在(已發生過相同引數的呼叫),
    // 若存在,直接返回快取結果,否則執行方法內容,最後將方法執行結果儲存到快取中。
    // 2、key:用於指定當前方法的快取儲存時的鍵的組合方式,
    // 預設的情況下使用所有的引數組合而成,這樣可以有效區分不同引數的快取。
    // 當然我們也可以手動指定,指定的方法是使用SPEL表示式。
    // #root.methodName -> 即為用當前方法名稱做為key
    // #id -> 即為用入參名稱id作為key
    // #userInfo.id -> 即為用UserInfo物件的id屬性作為key
    // 3、 @Cacheable(cacheNames = "userInfo",key = "#id"),cacheNames也可以單獨指定
    // 4、鍵值對快取key,就是說redis快取的時候key的生成時如下格式:value::key 或 cacheNames::key
    // 例如,指定value或cacheNames為 userInfo2,key為1,
    // 即 @Cacheable(value="userInfo2",key="1") 或 @Cacheable(cacheNames = "userInfo",key = "#id")
    // 生成的快取key值為 userInfo2::1
    @Cacheable(key = "#root.methodName")
    List<UserInfo> findAll();

    // @CachePut:定義更新快取,觸發快取更新
    @Cacheable(key = "#id")
    UserInfo findById(String id);

    // @CachePut:該註解用於更新快取,無論結果是否已經快取,
    // 都會在方法執行結束插入快取,相當於更新快取,一般用於更新方法之上。
    @CachePut(key = "#userInfo.id")
    UserInfo saveUserInfo(UserInfo userInfo);

    // @CacheEvict:該註解主要用於刪除快取操作
    // allEntries=true:刪除所有快取
    // @CacheEvict(key = "#id") 刪除id為某值的快取
    @CacheEvict(allEntries=true)
    void delUserInfo(String id);

    // 擴充套件:condition屬性是用來指定判斷條件從而確定是否生成快取
    // @Cacheable(value = "userInfo2",key = "#id",condition="#id%2 == 0")
    // 如果id%2 == 0判斷條件成立的話,將會生成redis快取,即返回true時生成Redis快取,
    // 如果EL表示式返回false的話則不生成快取


}

5、service實現類

package com.cache.redis.service.impl;

import com.cache.redis.entity.UserInfo;
import com.cache.redis.service.UserInfoService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 一抹餘陽
 * 全部直接模擬資料庫操作,省掉dao層,如果需要可以自行新增
 */
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Override
    public List<UserInfo> findAll() {
        System.out.println("查了全部資料。。。");
        UserInfo userInfo = new UserInfo();
        userInfo.setId("200");
        userInfo.setNickName("搬磚小能手");
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setId("300");
        userInfo2.setNickName("我是誰");
        List<UserInfo> userInfoList = new ArrayList<>();
        userInfoList.add(userInfo);
        userInfoList.add(userInfo2);
        return userInfoList;
    }

    @Override
    public UserInfo findById(String id) {
        System.out.println("查了一次資料庫。。。");
        UserInfo userInfo = new UserInfo();
        if("100".equals(id)){
            userInfo.setNickName("呵呵");
            userInfo.setId("100");
        }else{
            userInfo.setNickName("哈哈");
            userInfo.setId("101");
        }
        return userInfo;
    }

    @Override
    public UserInfo saveUserInfo(UserInfo userInfo) {
        System.out.println("新增或者修改了一次資料。。。");
        return userInfo;
    }

    @Override
    public void delUserInfo(String id) {
        System.out.println("刪除了一個資料。。。");
    }
}

6、redisconfig類

package com.cache.redis.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author 一抹餘陽
 * @create 2020-09-25 10:25
 */
@Configuration      //@Configuration:專案啟動配置註解
@EnableCaching      //@EnableCaching:開啟快取功能
public class RedisConfig {

    //@ 讀取 application.properties 配置的值
    @Value("${cache.default.expire-time}")
    private int defaultExpireTime;
    @Value("${cache.user.expire-time}")
    private int userCacheExpireTime;
    @Value("${cache.user.name}")
    private String userCacheName;

    /**
     * 快取管理器
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory lettuceConnectionFactory) {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        // 設定快取管理器管理的快取的預設過期時間
        defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(defaultExpireTime))
                // 設定 key為string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 設定value為json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                // 不快取空值
                .disableCachingNullValues();

        Set<String> cacheNames = new HashSet<>();
        cacheNames.add(userCacheName);

        // 將需要不同過期時間 cacheNames 配置
        // 對每個快取空間應用不同的配置,如果所有cacheNames的過期時間都一直,則則不需要特殊配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put(userCacheName, defaultCacheConfig.entryTtl(Duration.ofSeconds(userCacheExpireTime)));

        RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory)
                .cacheDefaults(defaultCacheConfig)
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }
}

7、啟動類

package com.cache.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

/**
 * @author 一抹餘陽
 */

@SpringBootApplication
@PropertySources(value = {@PropertySource(value = {"classpath:application.properties"}, encoding = "utf-8")
})
@Slf4j
public class App {

    /**
     * 啟動方法
     * @param args
     */
    public static void main(String[] args) {
        log.info("ApiMain begin...");
        try {
            SpringApplication.run(App.class, args);
            log.info("App run sucessful!");
        } catch (Throwable throwable) {
            log.error("App run error!error msg is {}", throwable.getMessage(), throwable);
        }
    }
}

8、測試類

import com.cache.redis.App;
import com.cache.redis.entity.UserInfo;
import com.cache.redis.service.UserInfoService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;

/**
 *  @author 一抹餘陽
 *  redis快取測試
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class CacheTest {

    @Resource
    UserInfoService userInfoService;

    @Test
    public void findAll(){
        // 測試快取:
        // 第一次查詢:沒有快取會先模擬查詢資料庫 列印 查了全部資料。。。
        // 然後列印查到的資料
        // 第二次查詢:快取裡有資料,會直接列印快取的資料
        List<UserInfo> all = userInfoService.findAll();
        System.out.println(all);
    }

    @Test
    public void queryUserInfo(){
        // 第一次查詢:沒有快取資料會先模擬查詢資料庫 列印 查了一次資料庫。。。
        // 然後列印模擬查詢的資料
        UserInfo user = userInfoService.findById("100");
        System.out.println(user);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 第二查詢:快取中有資料,會直接從快取中查詢到資料直接列印,不去模擬資料庫查詢操作
        UserInfo user2 = userInfoService.findById("100");
        System.out.println(user2);
    }

    @Test
    public void delUserINfo(){
        // 如 queryUserInfo() ,快取中有資料則直接返回快取中資料,沒有模擬查詢資料庫
        UserInfo user = userInfoService.findById("100");
        System.out.println(user);
        // 刪除掉快取中 id為100 的資料
        userInfoService.delUserInfo("100");
        // 再次查詢則還需要模擬資料庫查詢操作
        UserInfo user2 = userInfoService.findById("100");
        System.out.println(user2);
    }

    @Test
    public void saveOrUpdateUserInfo(){
        // 不論快取中有沒有資料,都去模擬資料庫操作,更新userInfo.id為100的到快取
        UserInfo userInfo = new UserInfo();
        userInfo.setNickName("嘎嘎");
        userInfo.setId("100");
        UserInfo userInfo2 = userInfoService.saveUserInfo(userInfo);
        System.out.println("修改後的資料" + userInfo2);
    }


}

9、專案架構截圖

jiegou

 

相關文章