Spring的緩存技術


Spring的緩存抽象

Spring為不同的緩存提供了一層抽象。通過在Java的方法上面使用注解,加了注解的方法就會將該方法執行的結果緩存起來。在下一次使用相同參數調用該方法時就判斷如果是緩存過的就將緩存結果返回,如果是沒有緩存過的就會執行方法。所以Spring的緩存是基於AOP實現的。

Spring緩存注解

@EnableCaching

  • 開啟緩存支持

@Cacheable

這個注解的作用負責將返回的數據進行緩存,當我們第一次訪問的時候,會將查出來的數據緩存起來,當之后在發起訪問的時候,會先去查看緩存中是否存在該條數據。該注解有如下介個參數支持:

| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:

@Cacheable(value="mycache")
key
@Cacheable(value="mycache",key="#userName")
condition
@Cacheable(value="mycache",condition="#userName.length()>2")

@CacheEvict

清空指定的緩存,可以清空value,也可以清空value.key的數據。它支持的參數如下:

| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:

@CachEvict(value="mycache")
key
@CachEvict(value="mycache",key="#userName")
condition
@CachEvict(value="mycache",condition="#userName.length()>2")
allEntries
@CachEvict(value="mycache",allEntries=true)
beforeInvocation
@CachEvict(value="mycache",beforeInvocation=true)

@CachePut

這個注解的作用和Cacheable類似,只不過它的作用相當於update。加上這個注解后,每次訪問數據都不會從緩存中拿取數據,而是從業務方法里查詢然后緩存。Cacheable和CachePut結合使用,因為當數據變化后,就需要將原來之前的數據給清空掉,否則的話就會產生臟數據。它的參數和Cacheable一樣。

| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:

@CachePut(value="mycache")
key
@CachePut(value="mycache",key="#userName")
condition
@CachePut(value="mycache",condition="#userName.length()>2")

@Caching

這個注解就是聯合使用。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
}

talk is cheap,show me the code

package com.lucky.spring.service.impl;

import com.lucky.spring.service.DataService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhangdd on 2020/8/9
 */
@Service
public class DataServiceImpl implements DataService {

    @Cacheable(value = "person")
    @Override
    public List<String> findAllName() {
        //模擬從數據庫查詢數據
        List<String> data = new ArrayList<>();
        data.add("張飛");
        data.add("趙雲");
        data.add("許褚");

        return data;
    }

    @CacheEvict(value = "person")
    @Override
    public void reloadPerson(){

    }
}

  • findAllName()方法模擬從數據庫讀取數據,並使用@Cacheable注解表明查詢結果會緩存起來
  • reloadPerson()方法,用來清理緩存
package com.lucky.spring;

import com.lucky.spring.service.DataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
@Slf4j
public class Application implements CommandLineRunner {

    @Autowired
    private DataService dataService;

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

    @Override
    public void run(String... args) throws Exception {
        log.info("count {}", dataService.findAllName().size());

        for (int i = 0; i < 5; i++) {
            log.info("再次執行查詢");
            dataService.findAllName();
        }

        dataService.reloadPerson();
        log.info("read after refresh");
        dataService.findAllName().forEach(p -> {
            log.info("person:{}", p);
        });
    }
}

打印結果如下:

2020-08-09 15:17:31.564  INFO 63660 --- [           main] c.l.spring.service.impl.DataServiceImpl  :  mock query from db
2020-08-09 15:17:31.565  INFO 63660 --- [           main] com.lucky.spring.Application             : count 3
2020-08-09 15:17:31.565  INFO 63660 --- [           main] com.lucky.spring.Application             : 再次執行查詢
2020-08-09 15:17:31.565  INFO 63660 --- [           main] com.lucky.spring.Application             : 再次執行查詢
2020-08-09 15:17:31.566  INFO 63660 --- [           main] com.lucky.spring.Application             : 再次執行查詢
2020-08-09 15:17:31.566  INFO 63660 --- [           main] com.lucky.spring.Application             : 再次執行查詢
2020-08-09 15:17:31.566  INFO 63660 --- [           main] com.lucky.spring.Application             : 再次執行查詢
2020-08-09 15:17:31.568  INFO 63660 --- [           main] c.l.spring.service.impl.DataServiceImpl  : clear cache data
2020-08-09 15:17:31.568  INFO 63660 --- [           main] com.lucky.spring.Application             : read after refresh
2020-08-09 15:17:31.568  INFO 63660 --- [           main] c.l.spring.service.impl.DataServiceImpl  :  mock query from db
2020-08-09 15:17:31.568  INFO 63660 --- [           main] com.lucky.spring.Application             : person:張飛
2020-08-09 15:17:31.568  INFO 63660 --- [           main] com.lucky.spring.Application             : person:趙雲
2020-08-09 15:17:31.568  INFO 63660 --- [           main] com.lucky.spring.Application             : person:許褚
  • 首先查詢得到數據集合的大小,打印了 service方法里的內容 mock query from db
  • 在繼續循環中調用該方法,就沒有在執行了,而是從緩存中獲取到的數據
  • 執行清理緩存后,再次查詢,可以看到 service 方法里的內容又被執行打印

使用Spring的緩存抽象時使用Redis作為其緩存

直接使用Spring的緩存抽象,緩存數據是被緩存在JVM中的。在集群場景下,不同的節點上緩存的數據就會又不一致性。

添加相關依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

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

配置cache屬性

spring.cache.type=redis
spring.cache.cache-names=person
#有效期5s
spring.cache.redis.time-to-live=5000
spring.cache.redis.cache-null-values=false

spring.redis.host=localhost

其他不變,看下redis里存儲的數據。到這里 數據已經緩存到redis了。
image.png


免責聲明!

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



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