1. 程式人生 > 實用技巧 >Springboot+redis實現快取機制

Springboot+redis實現快取機制

SpringBoot專案使用Redis做快取

首先需要安裝redis並啟動,下載連結:http://download.redis.io/releases/redis-4.0.14.tar.gz

1.專案pom檔案引入Cache和Redis依賴

<!-- kaptcha -->  
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.配置application.properties

## Redis部分
# Redis資料庫索引(預設為0)
spring.redis.database=0
# Redis伺服器地址
spring.redis.host=106.14.72.179
# Redis伺服器連線埠
spring.redis.port=6379
# Redis伺服器連線密碼(預設為空)
spring.redis.password=
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.jedis.pool.max-active=8
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.jedis.pool.max
-wait=-1ms # 連線池中的最大空閒連線 spring.redis.jedis.pool.max-idle=8 # 連線池中的最小空閒連線 spring.redis.jedis.pool.min-idle=0 # 連線超時時間(毫秒) spring.redis.timeout=5000 ## Cache部分 #快取的名稱集合,多個採用逗號分割 spring.cache.cache-names= #快取的型別,官方提供了很多,這裡我們填寫redis spring.cache.type=redis #是否快取null資料,預設是false spring.cache.redis.cache-null
-values=false #redis中快取超時的時間,預設60000ms spring.cache.redis.time-to-live=60000 #快取資料key是否使用字首,預設是true spring.cache.redis.use-key-prefix=true #快取資料key的字首,在上面的配置為true時有效, spring.cache.redis.key-prefix=

3.配置Configuration

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
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 org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Configuration
public class CacheConfig  {

    @Bean
    CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();

        //common資訊快取配置
        RedisCacheConfiguration userCacheConfiguration = defaultCacheConfig
                // 設定 key為string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 設定value為json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).disableCachingNullValues();
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //entryTtl設定快取失效時間,單位是秒
        redisCacheConfigurationMap.put("common", userCacheConfiguration.entryTtl(Duration.ofSeconds(30)));


        //設定CacheManager的值序列化方式為JdkSerializationRedisSerializer,但其實RedisCacheConfiguration預設就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下注釋程式碼為預設實現
        //ClassLoader loader = this.getClass().getClassLoader();
        //JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
        //RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
        //RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("common");
        //初始化RedisCacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(defaultCacheConfig).initialCacheNames(cacheNames).withInitialCacheConfigurations(redisCacheConfigurationMap).build();
        return cacheManager;
    }
}

4.啟動類Application或App加@EnableCaching註解

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

5.快取註解的使用

  • Cacheable:呼叫方法時先從快取中查詢有沒有對應key的資料,如果有直接從快取獲取返回,如果沒有則執行方法,將返回值存入快取中。
  • CacheEvict:呼叫方法後從快取中刪除對應key的資料
  • Caching:當一個方法需要查詢多個快取或者刪除多個快取時使用
package com.example.demo.service;

import com.example.demo.domain.Blog;
import com.example.demo.domain.BlogExample;
import com.example.demo.enumdata.EnumBlogType;
import com.example.demo.mapper.BlogMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

@Service
public class BlogService {

    @Resource
    private BlogMapper blogMapper;

    //unless 引數就是不執行Cacheable的條件
    @Cacheable(value = "common", key = "'blog_'+#blogId", unless = "#blogId == null")
    public Blog getBlogById (Long blogId) {
        return blogMapper.selectByPrimaryKey(blogId);
    }

    //condition 引數就是執行Cacheable的條件
    @Cacheable(value = "common", key = "'blog_by_page'",condition = "#page == null")
    public List<Blog> getBlogByPage (Integer page, Integer size) {
        int start = 0;
        if (size == null)
            size = 10;
        if (page != null) {
            page = page <= 0 ? 1 : page;
            start = (page - 1) * size;
        }
        BlogExample example = new BlogExample();
        example.setOrderByClause("blog_id desc limit "+ start + ","+ size);
        return blogMapper.selectByExampleWithBLOBs(example);
    }

    @Cacheable(value = "common", key = "'blog_all'")
    public List<Blog> getBlogs () {
        BlogExample example = new BlogExample();
        example.setOrderByClause("blog_id desc");
        return blogMapper.selectByExampleWithBLOBs(example);
    }

    //執行下面方法需要刪除三個快取的資料,所以使用Caching
    @Transactional
    @Caching(evict={@CacheEvict(value = "common", key="'blog_by_page'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_all'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_'+#blog.blogId",condition="#blog.blogId!=null")})
    public int save (Blog blog) {
        if (blog == null) return 0;
        if (blog.getBlogId() == null) {
            return insert(blog);
        }
        return update(blog);
    }

    public int insert (Blog blog) {
        if (StringUtils.isBlank(blog.getAuthor()))blog.setAuthor("Cocoivan");
        if (blog.getBlogType() == null)blog.setBlogType(EnumBlogType.MISCELLANEOUS.getValue());
        return blogMapper.insertSelective(blog);
    }

    public int update (Blog blog) {
        return blogMapper.updateByPrimaryKeySelective(blog);
    }
}

6.注意

Spring @Cacheable、@CacheEvict、@Caching是基於Spring AOP代理類,內部方法呼叫時,註解是失效的。

舉例子,Controller接收請求呼叫BlogService.save方法
快取相關注解不生效
@Transactional
    public int save (Blog blog) {
        if (blog == null) return 0;
        if (blog.getBlogId() == null) {
            return insert(blog);
        }
        return update(blog);
    }

    @Caching(evict={@CacheEvict(value = "common", key="'blog_by_page'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_all'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_'+#blog.blogId",condition="#blog.blogId!=null")})
    public int insert (Blog blog) {
        if (StringUtils.isBlank(blog.getAuthor()))blog.setAuthor("Cocoivan");
        if (blog.getBlogType() == null)blog.setBlogType(EnumBlogType.MISCELLANEOUS.getValue());
        return blogMapper.insertSelective(blog);
    }

    @Caching(evict={@CacheEvict(value = "common", key="'blog_by_page'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_all'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_'+#blog.blogId",condition="#blog.blogId!=null")})
    public int update (Blog blog) {
        return blogMapper.updateByPrimaryKeySelective(blog);
    }
快取相關注解生效
@Transactional
    @Caching(evict={@CacheEvict(value = "common", key="'blog_by_page'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_all'",condition="#blog!=null")
            , @CacheEvict(value = "common", key="'blog_'+#blog.blogId",condition="#blog.blogId!=null")})
    public int save (Blog blog) {
        if (blog == null) return 0;
        if (blog.getBlogId() == null) {
            return insert(blog);
        }
        return update(blog);
    }

    public int insert (Blog blog) {
        if (StringUtils.isBlank(blog.getAuthor()))blog.setAuthor("Cocoivan");
        if (blog.getBlogType() == null)blog.setBlogType(EnumBlogType.MISCELLANEOUS.getValue());
        return blogMapper.insertSelective(blog);
    }

    public int update (Blog blog) {
        return blogMapper.updateByPrimaryKeySelective(blog);
    }

7.完成