微信公衆號:一個優秀的廢人
若有問題或建議,請後臺留言,我會盡力解決你的問題。
如題,今天介紹 SpringBoot 的數據緩存。作過開發的都知道程序的瓶頸在於數據庫,咱們也知道內存的速度是大大快於硬盤的,當須要重複獲取相同數據時,一次又一次的請求數據庫或者遠程服務,致使大量時間耗費在數據庫查詢或遠程方法調用上,致使性能的惡化,這即是數據緩存要解決的問題。前端
Spring 定義了 org.springframework.cache.CacheManager 和 org.springframework.cache.Cache 接口用於統一不一樣的緩存技術。其中,CacheManager 是 Spring 提供的各類緩存技術的抽象接口,Cache 接口則是包含了緩存的各類操做(增長,刪除,獲取緩存,通常不會直接和此接口打交道)。java
一、Spring 支持的 CacheManager mysql
針對不一樣的緩存技術,實現了不一樣的 CacheManager ,Spring 定義了下表所示的 CacheManager:git
CacheManager | 描述 |
---|---|
SimpleCacheManager | 使用簡單的 Collection 來存儲緩存,主要用於測試 |
ConcurrentMapCacheManager | 使用 ConcurrentMap 來存儲緩存 |
NoOpCacheManager | 僅測試用途,不會實際緩存數據 |
EhCacheCacheManager | 使用 EhCache 做爲緩存技術 |
GuavaCacheManager | 使用 Google Guava 的 GuavaCache 做爲緩存技術 |
HazelcastCacheManager | 使用 Hazelcast 做爲緩存技術 |
JCacheCacheManager | 支持 JCache(JSR-107) 標準的實現做爲緩存技術,如 ApacheCommonsJCS |
RedisCacheManager | 使用 Redis 做爲緩存技術 |
在使用以上任意一個實現的 CacheManager 的時候,需註冊實現的 CacheManager 的 Bean,如:github
@Bean public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager){ return new EhCacheCacheManager(ehCacheCacheManager); }
注意,每種緩存技術都有不少的額外配置,但配置 cacheManager 是必不可少的。web
二、聲明式緩存註解redis
Spring 提供了 4 個註解來聲明緩存規則(又是使用註解式的 AOP 的一個例子)。4 個註解以下表示:算法
註解 | 解釋 |
---|---|
@Cacheable | 在方法執行前 Spring 先查看緩存中是否有數據,如有,則直接返回緩存數據;若無數據,調用方法將方法返回值放入緩存中 |
@CachePut | 不管怎樣,都會將方法的返回值放到緩存中。 |
@CacheEvict | 將一條或多條數據從緩存中刪除 |
@Caching | 能夠經過 @Caching 註解組合多個註解策略在一個方法上 |
@Cacheable、@CachePut、@CacheEvict 都有 value 屬性,指定的是要使用的緩存名稱;key 屬性指定的是數據在緩存中存儲的鍵。spring
三、開啓聲明式緩存支持sql
開啓聲明式緩存很簡單,只需在配置類上使用 @EnableCaching 註解便可,例如:
@Configuration @EnableCaching public class AppConfig{ }
在 Spring 中使用緩存技術的關鍵是配置 CacheManager ,而 SpringBoot 爲咱們配置了多個 CacheManager 的實現。
它的自動配置放在 org.springframework.boot.autoconfigure.cache 包中。
在不作任何配置的狀況下,默認使用的是 SimpleCacheConfiguration ,即便用 ConcurrentMapCacheManager。SpringBoot 支持之前綴來配置緩存。例如:
spring.cache.type= # 可選 generic、ehcache、hazelcast、infinispan、jcache、redis、guava、simple、none spring.cache.cache-names= # 程序啓動時建立的緩存名稱 spring.cache.ehcache.config= # ehcache 配置文件的地址 spring.cache.hazelcast.config= # hazelcast配置文件的地址 spring.cache.infinispan.config= # infinispan配置文件的地址 spring.cache.jcache.config= # jcache配置文件的地址 spring.cache.jcache.provider= # 當多個 jcache 實如今類路徑的時候,指定 jcache 實現 # 等等。。。
在 SpringBoot 環境下,使用緩存技術只須要在項目中導入相關緩存技術的依賴包,並在配置類中使用 @EnableCaching 開啓緩存支持便可。
本文將以 SpringBoot 默認的 ConcurrentMapCacheManager 做爲緩存技術,演示 @Cacheable、@CachePut、@CacheEvict。
一、準備工做
二、Pom.xml 文件依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.nasus</groupId> <artifactId>cache</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cache</name> <description>cache Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- cache 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- JPA 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- web 啓動類 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mysql 數據庫鏈接類 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- lombok 依賴,簡化實體 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 單元測試類 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
註釋很清楚,無需多言。不會就谷歌一下。
三、Application.yaml 文件配置
spring: # 數據庫相關 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: root password: 123456 # jpa 相關 jpa: hibernate: ddl-auto: update # ddl-auto: 設爲 create 表示每次都從新建表 show-sql: true
四、實體類
package com.nasus.cache.entity; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @Entity @AllArgsConstructor @NoArgsConstructor public class Student { @Id @GeneratedValue private Integer id; private String name; private Integer age; }
五、dao 層
package com.nasus.cache.repository; import com.nasus.cache.entity.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends JpaRepository<Student,Integer> { }
六、service 層
package com.nasus.cache.service; import com.nasus.cache.entity.Student; public interface StudentService { public Student saveStudent(Student student); public void deleteStudentById(Integer id); public Student findStudentById(Integer id); }
實現類:
package com.nasus.cache.service.impl; import com.nasus.cache.entity.Student; import com.nasus.cache.repository.StudentRepository; import com.nasus.cache.service.StudentService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class StudentServiceImpl implements StudentService { // 使用 slf4j 做爲日誌框架 private static final Logger LOGGER = LoggerFactory.getLogger(StudentServiceImpl.class); @Autowired private StudentRepository studentRepository; @Override @CachePut(value = "student",key = "#student.id") // @CachePut 緩存新增的或更新的數據到緩存,其中緩存名稱爲 student 數據的 key 是 student 的 id public Student saveStudent(Student student) { Student s = studentRepository.save(student); LOGGER.info("爲id、key 爲{}的數據作了緩存", s.getId()); return s; } @Override @CacheEvict(value = "student") // @CacheEvict 從緩存 student 中刪除 key 爲 id 的數據 public void deleteStudentById(Integer id) { LOGGER.info("刪除了id、key 爲{}的數據緩存", id); //studentRepository.deleteById(id); } @Override @Cacheable(value = "student",key = "#id") // @Cacheable 緩存 key 爲 id 的數據到緩存 student 中 public Student findStudentById(Integer id) { Student s = studentRepository.findById(id).get(); LOGGER.info("爲id、key 爲{}的數據作了緩存", id); return s; } }
代碼講解看註釋,很詳細。
七、controller 層
package com.nasus.cache.controller; import com.nasus.cache.entity.Student; import com.nasus.cache.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.Mapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/student") public class StudentController { @Autowired private StudentService studentService; @PostMapping("/put") public Student saveStudent(@RequestBody Student student){ return studentService.saveStudent(student); } @DeleteMapping("/evit/{id}") public void deleteStudentById(@PathVariable("id") Integer id){ studentService.deleteStudentById(id); } @GetMapping("/able/{id}") public Student findStudentById(@PathVariable("id") Integer id){ return studentService.findStudentById(id); } }
八、application 開啓緩存功能
package com.nasus.cache; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching // 開啓緩存功能 @SpringBootApplication public class CacheApplication { public static void main(String[] args) { SpringApplication.run(CacheApplication.class, args); } }
測試前,先看一眼數據庫當前的數據,以下:
一、測試 @Cacheable
訪問 http://localhost:8080/student/able/2 控制檯打印出了 SQL 查詢語句,以及指定日誌。說明這一次程序是直接查詢數據庫獲得的結果。
2019-02-21 22:54:54.651 INFO 1564 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 11 ms Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=? 2019-02-21 22:54:59.725 INFO 1564 --- [nio-8080-exec-1] c.n.c.service.impl.StudentServiceImpl : 爲id、key 爲2的數據作了緩存
postman 第一次測試結果 :
再次訪問 http://localhost:8080/student/able/2 結果以下圖。但控制檯無 SQL 語句打印,也無爲id、key 爲2的數據作了緩存這句話輸出。
說明 @Cacheable 確實作了數據緩存,第二次的測試結果是從數據緩存中獲取的,並無直接查數據庫。
二、測試 @CachePut
以下圖,postman 訪問 http://localhost:8080/student/put 插入數據:
下面是控制檯打印出了 SQL Insert 插入語句,以及指定日誌。說明程序作了緩存。
Hibernate: insert into student (age, name, id) values (?, ?, ?) 2019-02-21 23:12:03.688 INFO 1564 --- [nio-8080-exec-8] c.n.c.service.impl.StudentServiceImpl : 爲id、key 爲4的數據作了緩存
插入數據返回的結果:
數據庫中的結果:
訪問 http://localhost:8080/student/able/4 Postman 結果以下圖。控制檯無輸出,驗證了 @CachePut 確實作了緩存,下圖數據是從緩存中獲取的。
三、測試 @CacheEvict
postman 訪問 http://localhost:8080/student/able/3 爲 id = 3 的數據作緩存。
postman 再次訪問 http://localhost:8080/student/able/3 確認數據是從緩存中獲取的。
postman 訪問 http://localhost:8080/student/evit/3
從緩存中刪除 key 爲 3 的緩存數據:
Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=? 2019-02-21 23:26:08.516 INFO 8612 --- [nio-8080-exec-2] c.n.c.service.impl.StudentServiceImpl : 爲id、key 爲3的數據作了緩存 2019-02-21 23:27:01.508 INFO 8612 --- [nio-8080-exec-4] c.n.c.service.impl.StudentServiceImpl : 刪除了id、key 爲3的數據緩存
再次 postman 訪問 http://localhost:8080/student/able/3 觀察後臺,從新作了數據緩存:
Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=? 2019-02-21 23:27:12.320 INFO 8612 --- [nio-8080-exec-5] c.n.c.service.impl.StudentServiceImpl : 爲id、key 爲3的數據作了緩存
這一套測試流程下來,證實了 @CacheEvict 確實刪除了數據緩存。
https://github.com/turoDog/Demo/tree/master/springboot_cache_demo
切換緩存技術除了在 pom 文件加入相關依賴包配置之外,使用方式與上面的代碼演示同樣。
一、切換 EhCache
在 pom 中添加 Encache 依賴:
<!-- EhCache 依賴 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
Ehcache 所需配置文件 ehcache.xml 只需放在類路徑(resource 目錄)下,SpringBoot 會自動掃描,如:
<?xml version="1.0" encoding="UTF-8"> <ehcache> <cache name="student" maxElementsInMmory="1000"> <ehcache>
SpringBoot 會自動配置 EhcacheManager 的 Bean。
二、切換 Guava
只需在 pom 中加入 Guava 依賴便可:
<!-- GuavaCache 依賴 --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency>
SpringBoot 會自動配置 GuavaCacheManager 的 Bean。
三、切換 RedisCache
與 Guava 同樣,只需在 pom 加入依賴便可:
<!-- cache 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
SpringBoot 會自動配置 RedisCacheManager 以及 RedisTemplate 的 Bean。
此外,切換其餘緩存技術的方式也是相似。這裏不作贅述。
以上爲 SpringBoot 數據緩存的教程。
若是本文對你哪怕有一丁點幫助,請幫忙點好看。
你的好看是我堅持寫做的動力。
另外,關注以後在發送 1024 可領取免費學習資料。資料內容詳情請看這篇舊文:Python、C++、Java、Linux、Go、前端、算法資料分享