SpringBoot(七) SpringBoot中的快取機制
隨著時間的積累,應用的使用使用者不斷增加,資料規模也越來越大,往往資料庫查詢操作會成為影響使用者使用體驗的瓶頸,此時使用快取往往是解決這一問題非常好的手段之一。Spring 3開始提供了強大的基於註解的快取支援,可以通過註解配置方式低侵入的給原有Spring應用增加快取功能,提高資料訪問效能。
SpringBoot中的Cache快取
1、基本概念
Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager介面來統一不同的快取技術;同時支援JCache(JSR-107)註解。
Cache | 快取介面,定義快取操作,實現有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 快取管理器,管理各種快取(Cache)元件 |
@Cacheable | 針對方法配置,根據方法的請求引數對其結果進行快取 |
@CacheEvict | 清空快取 |
@CachePut | 保證方法被呼叫,又希望結果被快取 update,呼叫,將資訊更新快取 |
@EnableCaching | 開啟基於註解的快取 |
KeyGenerator | 快取資料時key生成的策略 |
serialize | 快取資料時value序列化策略 |
2、整合專案
1、新建一個SpringBoot1.5+web+mysql+mybatis+cache
2、編寫配置檔案,連線Mysql
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.1.125:3306/test01 spring.datasource.username=root spring.datasource.password=root mybatis.configuration.map-underscore-to-camel-case=true server.port=9000
3、建立JaveBean例項
public class Employee { private Integer id; private String lastName; private String gender; private String email; private Integer dId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Integer getdId() { return dId; } public void setdId(Integer dId) { this.dId = dId; } @Override public String toString() { return "Employee{" + "id=" + id + ", lastName='" + lastName + '\'' + ", gender='" + gender + '\'' + ", email='" + email + '\'' + ", dId=" + dId + '}'; } }
4、建立mapper介面對映資料庫,並訪問資料庫中的資料
import com.wdjr.cache.bean.Employee; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; @Mapper public interface EmployeeMapper { @Select("SELECT * FROM employee WHERE id = #{id}") public Employee getEmpById(Integer id); @Update("UPDATE employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}") public void updateEmp(Employee employee); }
5、在pom.xml中引入cache依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
6、主程式添加註解MapperScan,並且使用@EnableCaching,開啟快取功能
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching @MapperScan("com.wdjr.cache.mapper") @SpringBootApplication public class Springboot01CacheApplication { public static void main(String[] args) { SpringApplication.run(Springboot01CacheApplication.class, args); } }
7、編寫service,來具體實現mapper中的方法,使用@Cacheable增加快取
import com.wdjr.cache.bean.Employee; import com.wdjr.cache.mapper.EmployeeMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class EmployeeService { @Autowired EmployeeMapper employeeMapper; /** * 將方法的執行結果進行快取,以後要是再有相同的資料,直接從快取中獲取,不用呼叫方法 * CacheManager中管理多個Cache元件,對快取的真正CRUD操作在Cache元件中,每個快取元件都有自己的唯一名字; * * 屬性: * CacheName/value:指定儲存快取元件的名字 * key:快取資料使用的key,可以使用它來指定。預設是使用方法引數的值,或方法的返回值 * 編寫Spel表示式:#id 引數id的值, #a0/#p0 #root.args[0] * keyGenerator:key的生成器,自己可以指定key的生成器的元件id * key/keyGendertor二選一使用 * * cacheManager指定Cache管理器,或者cacheReslover指定獲取解析器 * condition:指定符合條件的情況下,才快取; * unless:否定快取,unless指定的條件為true,方法的返回值就不會被快取,可以獲取到結果進行判斷 * sync:是否使用非同步模式,unless不支援 * * */ @Cacheable(cacheNames = {"emp"},key = "#id",condition = "#id>0",unless = "#result==null") public Employee getEmp(Integer id){ System.out.println("查詢id= "+id+"的員工"); return employeeMapper.getEmpById(id); } }
8、編寫controller測試
@RestController public class EmployeeController { @Autowired EmployeeService employeeService; @GetMapping("/emp/{id}") public Employee getEmp(@PathVariable("id")Integer id){ return employeeService.getEmp(id); } }
9、檢視結果。具體結果就不截圖了,快取功能開啟, 如果條件相同,只會查詢一次資料庫。
Cache註解
@CacheConfig
:主要用於配置該類中會用到的一些共用的快取配置。在這裡@CacheConfig(cacheNames = "users")
:配置了該資料訪問物件中返回的內容將儲存於名為users的快取物件中,我們也可以不使用該註解,直接通過@Cacheable
自己配置快取集的名字來定義。- 該註解是整合了Cacheable /Cacheput / CacheEvict。設定在類上,該類的方法上面的cache註解均不用設定name。
@Cacheable
:配置了findByName函式的返回值將被加入快取。同時在查詢時,會先從快取中獲取,若不存在才再發起對資料庫的訪問。該註解主要有下面幾個引數:value
、cacheNames
:兩個等同的引數(cacheNames
為Spring 4新增,作為value
的別名),用於指定快取儲存的集合名。由於Spring 4中新增了@CacheConfig
,因此在Spring 3中原本必須有的value
屬性,也成為非必需項了key
:快取物件儲存在Map集合中的key值,非必需,預設按照函式的所有引數組合作為key值,若自己配置需使用SpEL表示式,比如:@Cacheable(key = "#p0")
:使用函式第一個引數作為快取的key值,更多關於SpEL表示式的詳細內容可參考官方文件condition
:快取物件的條件,非必需,也需使用SpEL表示式,只有滿足表示式條件的內容才會被快取,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3")
,表示只有當第一個引數的長度小於3的時候才會被快取,若做此配置上面的AAA使用者就不會被快取,讀者可自行實驗嘗試。unless
:另外一個快取條件引數,非必需,需使用SpEL表示式。它不同於condition
引數的地方在於它的判斷時機,該條件是在函式被呼叫之後才做判斷的,所以它可以通過對result進行判斷。keyGenerator
:用於指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實現org.springframework.cache.interceptor.KeyGenerator
介面,並使用該引數來指定。需要注意的是:該引數與key
是互斥的cacheManager
:用於指定使用哪個快取管理器,非必需。只有當有多個時才需要使用cacheResolver
:用於指定使用那個快取解析器,非必需。需通過org.springframework.cache.interceptor.CacheResolver
介面來實現自己的快取解析器,並用該引數指定。
除了上面的兩個註解外,還有一些其他的註解。
- @Cacheput:既呼叫方法,又更新快取資料。也就是修改資料庫的某個資料,同時更新快取。步驟:先執行方法,再將目標結果快取起來。
- 如果需要使用該註解,不僅需要加入註解,還需要設定註解的key的值需要和查詢出來的key一致,否則不會改變cache快取中的資料。
- @CacheEvict:清除快取。使用key刪除快取資料。
- 特有的兩個屬性:
- allEntries = true,代表不論清除哪個key,都重新重新整理快取
- beforeInvocation=true.方法執行前,清空快取,無論發不發生異常,都會清除快取。false則是方法執行後,情況快取。預設是false,如果程式異常,就不會清除快取。
- 特有的兩個屬性:
使用Redis做集中式快取
預設的快取是在記憶體中定義HashMap,在實際的開發生產中,經常使用Redis作為快取中介軟體,而不使用cache。
Redis 是一個開源(BSD許可)的,記憶體中的資料結構儲存系統,它可以用作資料庫、快取和訊息中介軟體。也是以key-value的形式進行儲存資料的一款非關係型資料庫。
步驟:
1、安裝Redis:將安裝redis在虛擬機器中(docker)。 推薦去docker中國中去下載。
#拉取redis映象
docker pull redis
#啟動redis,並對外發開,使外部可以連線到虛擬機器中的rdeis
docker run -d -p 6379:6379 --name redis01 bfcb1f6df2db
安裝後可以使用 Redis Desktop Manager管理工具去管理redis。
2、Redis的Tempate
Redis的常用五大資料型別
String【字串】、List【列表】、Set【集合】、Hash【雜湊】、ZSet【有序集合】
分為兩種一種是StringRedisTemplate,另一種是RedisTemplate
根據不同的資料型別,大致的操作也分為這5種,以StringRedisTemplate為例
tringRedisTemplate.opsForValue() --String stringRedisTemplate.opsForList() --List stringRedisTemplate.opsForSet() --Set stringRedisTemplate.opsForHash() --Hash stringRedisTemplate.opsForZset() -Zset
a.匯入Redis依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
b.修改配置檔案
spring.redis.host=192.168.1.125
3.新增測試類
@Autowired StringRedisTemplate stringRedisTemplate;//操作字串【常用】 @Autowired RedisTemplate redisTemplate;//操作k-v都是物件 @Test public void test01(){ // stringRedisTemplate.opsForValue().append("msg", "hello"); String msg = stringRedisTemplate.opsForValue().get("msg"); System.out.println(msg); }
我們需要做的配置和測試到這裡就已經完成了。
並且Spring Boot會在偵測到存在Redis的依賴並且Redis的配置是可用的情況下,還可以使用RedisCacheManager初始化CacheManager。
3、儲存物件
- 如果要直接儲存物件,需要使用JDK序列化機制,現將物件序列化後再近些儲存Redis中。
- 將物件轉換成JSON格式,儲存到Redis中。
- 第一種方式:自己將物件轉換成JSON。(fastJson)
- 第二種方式:新建一個Redis的配置類MyRedisConfig。(redisTemplate中預設改變的序列化規則)
@Configuration public class MyRedisConfig { @Bean public RedisTemplate<Object, Employee> empRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<Employee> jsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class); template.setDefaultSerializer(jsonRedisSerializer); return template; }