spring boot redis 緩存(cache)集成



Spring Boot 集成教程


概述

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

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

項目內容

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

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

要求

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

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

項目創建

創建spring boot項目

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

image

項目依賴

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

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

項目配置

application.properties文件中配置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

代碼實現

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

image

Redis Java配置(RedisConfig.java)

首先使用@EnableCaching開啟以注解方式使用緩存。

然后配置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

總結

完整代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM