天天看點

SpringBoot學習筆記(八:Cache內建Redis )一、緩存簡介二、Redis緩存

文章目錄

Spring 3.1 引入了激動人心的基于注釋(annotation)的緩存(cache)技術,它本質上不是一個具體的緩存實作方案(例如 EHCache 或者 Redis),而是一個對緩存使用的抽象,通過在既有代碼中添加少量它定義的各種 annotation,即能夠達到緩存方法的傳回對象的效果。

目前 Spring Boot 支援的緩存有如下幾種:

  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • lnfinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple

目前常用的緩存實作 Ehcache 2.x和 Redis。由于 Spring 早己将緩存領域統一 ,是以無論使用哪種緩存實作,不同的隻是緩存配置,開發者使用的緩存注解是一緻的( Spring 緩存注解和各緩存實作的關系就像 JDBC和各種資料庫驅動的關系)。

這裡學習使用Redis作為緩存實作。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
      

在application.properties中添加配置:

spring.redis.host=localhost
spring.redis.password=
# 一般來說是不用配置的,Spring Cache 會根據依賴的包自行裝配
spring.cache.type=redis
# 連接配接逾時時間(毫秒)
spring.redis.timeout=10000
# Redis預設情況下有16個分片,這裡配置具體使用的分片
spring.redis.database=0
# 連接配接池最大連接配接數(使用負值表示沒有限制) 預設 8
spring.redis.lettuce.pool.max-active=8
# 連接配接池最大阻塞等待時間(使用負值表示沒有限制) 預設 -1
spring.redis.lettuce.pool.max-wait=-1
# 連接配接池中的最大空閑連接配接 預設 8
spring.redis.lettuce.pool.max-idle=8
# 連接配接池中的最小空閑連接配接 預設 0
spring.redis.lettuce.pool.min-idle=0
      

spring-boot-starter-cache 是 Spring Boot 體系内提供使⽤ Spring Cache的Starter 包。其中最核⼼的三個注解:@Cacheable、@CacheEvict、@CachePut。

@Cacheable⽤來聲明⽅法是可緩存的,将結果存儲到緩存中以便後續使⽤相同參數調⽤時不需執⾏實際的⽅法,直接從緩存中取值。@Cacheable 可以标記在⼀個⽅法上,也可以标記在⼀個類上。當标記在⼀個⽅法上時表示該⽅法是⽀持緩存的,當标記在⼀個類上時則表示該類所有的⽅法都是⽀持緩存的。

例如:

@RequestMapping("/hello")
@Cacheable(value="helloCache")
public String hello(String name) {
 System.out.println("沒有⾛緩存!");
 return "hello "+name;
}      

@Cacheable ⽀持如下⼏個參數。

  • value:緩存的名稱。
  • key:緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫;如果不指定,則預設按照⽅法的所有參數進⾏組合。
  • condition:觸發條件,隻有滿⾜條件的情況才會加⼊緩存,預設為空,既表示全部都加⼊緩存,⽀持SpEL。

把上⾯的⽅法稍微更改:

@RequestMapping("/condition")
@Cacheable(value="condition",condition="#name.length() <= 4")
public String condition(String name) {
 System.out.println("沒有⾛緩存!");
 return "hello "+name;
}      

項⽬運⾏中會對資料庫的資訊進⾏更新,如果仍然使⽤ @Cacheable 就會導緻資料庫的資訊和緩存的資訊不⼀緻。在以往的項⽬中,⼀般更新完資料庫後,再⼿動删除掉 Redis 中對應的緩存,以保證資料的⼀緻性。Spring 提供了另外的⼀種解決⽅案,可以以優雅的⽅式去更新緩存。

與 @Cacheable 不同的是使⽤ @CachePut 标注的⽅法在執⾏前,不會去檢查緩存中是否存在之前執⾏過的結果,⽽是每次都會執⾏該⽅法,并将執⾏結果以鍵值對的形式存⼊指定的緩存中。

@CachePut 配置⽅法:

  • value 緩存的名稱。
  • key 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則預設按照⽅法的所有參數進⾏組合。
  • condition 緩存的條件,可以為空,使⽤ SpEL 編寫,傳回 true 或者 false,隻有為 true 才進⾏緩存。

@CacheEvict 是⽤來标注在需要清除緩存元素的⽅法或類上的,當标記在⼀個類上時表示其中所有的⽅法的執⾏都會觸發緩存的清除操作。@CacheEvict 可以指定的屬性有 value、key、condition、allEntries 和beforeInvocation,其中 value、key 和 condition 的語義與 @Cacheable 對應的屬性類似。即 value 表示清除操作是發⽣在哪些 Cache 上的(對應 Cache 的名稱);key 表示需要清除的是哪個 key,如未指定則會使⽤預設政策⽣成的 key;condition 表示清除操作發⽣的條件。

  • @Cacheable 作⽤和配置⽅法

主要針對⽅法配置,能夠根據⽅法的請求參數對其結果進⾏緩存:

主要參數 解 釋 舉例
value 緩存的名稱,在 spring 配置⽂件中定義,必須指定⾄少⼀個 如 @Cacheable(value=“mycache”)或者 @Cacheable(value={“cache1”,“cache2”}
key 緩存的 key,可以為空,如果指定要按照 SpEL表達式編寫,如果不指定,則預設按照⽅法的所有參數進⾏組合 如@Cacheable(value=“testcache”,key="#userName")
condition 緩存的條件,可以為空,使⽤ SpEL 編寫,傳回true 或者 false,隻有為 true 才進⾏緩存 如@Cacheable(value=“testcache”,condition="#userName.length()>2")
  • @CachePut 作⽤和配置⽅法

@CachePut 的作⽤是主要針對⽅法配置,能夠根據⽅法的請求參數對其結果進⾏緩存,和 @Cacheable 不同的是,它每次都會觸發真實⽅法的調⽤。

解釋
如@Cacheable(value=“mycache”)或者 @Cacheable(value={“cache1”,“cache2”}
  • @CacheEvict 作⽤和配置⽅法

主要針對⽅法配置,能夠根據⼀定的條件對緩存進⾏清空。

如 @CachEvict(value=“mycache”)或者 @CachEvict(value={“cache1”,“cache2”}
緩存的 key,可以為空,如果指定要按照SpEL 表達式編寫,如果不指定,則預設按照⽅法的所有參數進⾏組合 如@CachEvict(value=“testcache”,key="#userName")
緩存的條件,可以為空,使⽤ SpEL 編寫,傳回 true 或者 false,隻有為 true 才清空緩存 如@CachEvict(value=“testcache”,condition="#userName.length()>2")
allEntries 是否清空所有緩存内容,預設為 false,如果指定為 true,則⽅法調⽤後将⽴即清空所有緩存 如@CachEvict(value=“testcache”,allEntries=true)
beforeInvocation 是否在⽅法執⾏前就清空,預設為 false,如果指定為 true,則在⽅法還沒有執⾏的時候就清空緩存,預設情況下,如果⽅法執⾏抛出異常,則不會清空緩存 如@CachEvict(value=“testcache”,beforeInvocation=true)

定義一個User類,模拟對象存儲:

public class User implements Serializable {
    private static final long serialVersionUID = 8655851615465363473L;
    private Long id;
    private String username;
    private String password;
    
    //省略getter、setter
}             

  • 接口:
public interface UserService {

    /**
     * 删除
     *
     * @param user 使用者對象
     * @return 操作結果
     */
    User saveOrUpdate(User user);

    /**
     * 添加
     *
     * @param id key值
     * @return 傳回結果
     */
    User get(Long id);

    /**
     * 删除
     *
     * @param id key值
     */
    void delete(Long id);
}      
  • 實作類:

實作類裡用到了最核心的三個注解:@Cacheable、@CachePut、@CacheEvict

@Service
public class UserServiceImpl implements UserService {
    private static final Map<Long, User> DATABASES = new HashMap<>();

    static {
        DATABASES.put(1L, new User(1L, "u1", "p1"));
        DATABASES.put(2L, new User(2L, "u2", "p2"));
        DATABASES.put(3L, new User(3L, "u3", "p3"));
    }


    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Long id) {
        // TODO 假設它是從資料庫讀取出來的
        log.info("進入 get 方法");
        return DATABASES.get(id);
    }

    @CachePut(value = "user", key = "#user.id")
    @Override
    public User saveOrUpdate(User user) {
        DATABASES.put(user.getId(), user);
        log.info("進入 saveOrUpdate 方法");
        return user;
    }

    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(Long id) {
        DATABASES.remove(id);
        log.info("進入 delete 方法");
    }
}      

啟動類需要開啟緩存配置@EnableCaching

@SpringBootApplication
@EnableCaching
public class SpringbootCacheRedisApplication {

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

}      

@RunWith(SpringRunner.class)
@SpringBootTest
public class CacheRedisTest {

    private static final Logger log = LoggerFactory.getLogger(CacheRedisTest.class);


    @Autowired
    private UserService userService;


    @Test
    public void get() {
        final User user = userService.saveOrUpdate(new User(5L, "u5", "p5"));
        log.info("[saveOrUpdate] - [{}]", user);
        final User user1 = userService.get(5L);
        log.info("[get] - [{}]", user1);
        userService.delete(5L);
    }
}      

運作結果:

SpringBoot學習筆記(八:Cache內建Redis )一、緩存簡介二、Redis緩存

可以看到增删改查中,查詢是沒有日志輸出的,因為它直接從緩存中擷取的資料,而添加、修改、删除都是會進入方法内執行具體的業務代碼,然後通過切面去删除掉Redis中的緩存資料。