spring boot redis 緩存(cache)集成


Spring Boot 集成教程


概述

本文介紹spring boot項目集成redis緩存的過程。java

redis是一個開源的內存NOSQL數據庫,在web開發中主要被用於數據緩存。通常在高併發的狀況下,web服務器接受訪問時,直接從數據庫加載是慢的,須要把經常使用數據緩存到redis中,提升加載速度和併發能力。mysql

項目內容

建立一個spring boot項目,配置redis各相關 bean,實現幾個接口,經過兩種方式測試redis緩存:linux

  • 以註解方式自動緩存
  • RedisTemplate手動訪問redis服務器

要求

  • 安裝redis服務器,參考官網文檔,如沒有linux系統可虛擬機安裝
  • JDK1.8或更新版本
  • Eclipse開發環境

如沒有開發環境,可參考前面章節:[spring boot 開發環境搭建(Eclipse)]。git

項目建立

建立spring boot項目

打開Eclipse,建立spring boot的spring starter project項目,選擇菜單:File > New > Project ...,彈出對話框,選擇:Spring Boot > Spring Starter Project,在配置依賴時,勾選webredis,完成項目建立。github

image

項目依賴

須要用到commons-pool2庫,在pom.xml中添加依賴web

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

項目配置

application.properties文件中配置redis服務器的鏈接redis

## REDIS (RedisProperties)
# Redis數據庫索引(默認爲0)
spring.redis.database=0  
# Redis服務器地址
spring.redis.host=192.168.0.99
# Redis服務器鏈接端口
spring.redis.port=6379  
# Redis服務器鏈接密碼(默認爲空)
spring.redis.password=  
# 鏈接池最大鏈接數(使用負值表示沒有限制)
spring.redis.lettuce.pool.max-active=8  
# 鏈接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.lettuce.pool.max-wait=-1
# 鏈接池中的最大空閒鏈接
spring.redis.lettuce.pool.max-idle=8  
# 鏈接池中的最小空閒鏈接
spring.redis.lettuce.pool.min-idle=0

代碼實現

項目目錄結構以下圖,咱們添加了幾個類,下面將詳細介紹。spring

image

Redis Java配置(RedisConfig.java)

首先使用@EnableCaching開啓以註解方式使用緩存。sql

而後配置redis相關的bean數據庫

  • RedisTemplate - 訪問redis的bean,用於手動訪問redis服務器
  • 緩存管理器 - 註解方式使用緩存的配置
  • KeyGenerator - 自定義緩存key的生成
  • Json序列化 - Json對象被緩存時的序列化
/**
 * @description redis配置  配置序列化方式以及緩存管理器 
 */
@EnableCaching // 開啓緩存
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    
    /**
     * 配置自定義redisTemplate
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setValueSerializer(jackson2JsonRedisSerializer());
        //使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * json序列化
     * @return
     */
    @Bean
    public RedisSerializer<Object> jackson2JsonRedisSerializer() {
        //使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        return serializer;
    }

    /**
     * 配置緩存管理器
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 生成一個默認配置,經過config對象便可對緩存進行自定義配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // 設置緩存的默認過時時間,也是使用Duration設置
        config = config.entryTtl(Duration.ofMinutes(1))
                // 設置 key爲string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 設置value爲json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
                // 不緩存空值
                .disableCachingNullValues();

        // 設置一個初始化的緩存空間set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("timeGroup");
        cacheNames.add("user");

        // 對每一個緩存空間應用不一樣的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("timeGroup", config);
        configMap.put("user", config.entryTtl(Duration.ofSeconds(120)));

        // 使用自定義的緩存配置初始化一個cacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
                // 必定要先調用該方法設置初始化的緩存名,再初始化相關的配置
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }
    
    /**
     * 緩存的key是 包名+方法名+參數列表
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append("::" + method.getName() + ":");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

}

添加實體類 User

public class User {
    private long id;
    private String nickname;
    private String mobile;
    
    @JsonProperty(access = Access.WRITE_ONLY) //在輸出的Json數據中隱藏密碼,只能輸入不輸出
    private String password;
    private String role;
    
    public User(long id, String nickname, String mobile, String password, String role) {
        this.id = id;
        this.nickname = nickname;
        this.mobile = mobile;
        this.password = password;
        this.role = role;
    }

    public User() {
        super();
    }
    
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
}

經常使用的緩存註解

  • @Cacheable - 代表對應方法的返回結果能夠被緩存,首次調用後,下次就從緩存中讀取結果,方法不會再被執行了。
  • @CachePut - 更新緩存,方法每次都會執行
  • @CacheEvict - 清除緩存,方法每次都會執行

添加User的服務層

由於主要的業務邏輯在服務層實現,通常會把緩存註解加在服務層的方法上。

下面幾個服務層的方法會加緩存註解:

  • getUserById - 方法的返回結果會被緩存到redis,使用註解@Cacheable
  • updateUserNickname - 原始數據被更新了,廢棄緩存數據,使用註解@CacheEvict

UserSevice.java 接口

public interface UserService {
    
    public User getUserById(long userId);
    public User updateUserNickname(long userId, String nickname);
}

UserServiceImpl.java 實現類

@Service("userService")
public class UserServiceImpl implements UserService {
    
    private static final org.slf4j.Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    
    private User user = new User(1l, "abc1", "13512345678", "123456", "role-user");
    
    @Cacheable(value = "user", key= "#userId")
    @Override
    public User getUserById(long userId) {
        
        log.info("加載用戶信息");
        return user;
    }
    
    @CacheEvict(value = "user", key= "#userId")
    @Override
    public User updateUserNickname(long userId, String nickname) {
        
        user.setNickname(nickname);
        
        return user;
    }
}

添加User的控制層

@RestController
@EnableAutoConfiguration
@RequestMapping("/user")
public class UserController {
    
    // 注入service類
    @Resource
    private UserService userService;
    
    // 注入RedisTemplate
    @Resource
    private RedisTemplate<String, Object> redis;
    
    // 讀取用戶信息,測試緩存使用:除了首次讀取,接下來都應該從緩存中讀取
    @RequestMapping(value="{id}", method=RequestMethod.GET, produces="application/json")
    public User getUser(@PathVariable long id) throws Exception {
        
        User user = this.userService.getUserById(id);
        
        return user;
    }
    
    // 修改用戶信息,測試刪除緩存
    @RequestMapping(value = "/{id}/change-nick", method = RequestMethod.POST, produces="application/json")
    public User changeNickname(@PathVariable long id) throws Exception{
        
        String nick = "abc-" + Math.random();
        User user = this.userService.updateUserNickname(id, nick);
        
        return user;
    }
    
    // 使用RedisTemplate訪問redis服務器
    @RequestMapping(value="/redis", method=RequestMethod.GET, produces="application/json")
    public String redis() throws Exception {
        
        // 設置鍵"project-name",值"qikegu-springboot-redis-demo"
        redis.opsForValue().set("project-name", "qikegu-springboot-redis-demo");
        String value = (String) redis.opsForValue().get("project-name");
        
        return value;
    }
}

運行

Eclipse左側,在項目根目錄上點擊鼠標右鍵彈出菜單,選擇:run as -> spring boot app運行程序。 打開Postman訪問接口,
同時監控redis服務器。

監控redis服務器,使用redis-cli命令連上服務器,而後使用monitor命令開始監控:

image

運行結果以下:

獲取用戶信息

image

redis中的數據,能夠看到數據經過SET指令保存進redis了

image

屢次獲取用戶信息,能夠看到經過GET指令從redis中讀取緩存

image

修改用戶信息

image

redis中的緩存被刪除了

image

測試使用RedisTemplate訪問redis服務器

image

redis中的數據變化

image

總結

完整代碼

相關文章
相關標籤/搜索