SpringBoot
是爲了簡化Spring
應用的建立、運行、調試、部署等一系列問題而誕生的產物,自動裝配的特性讓咱們能夠更好的關注業務自己而不是外部的XML配置,咱們只需遵循規範,引入相關的依賴就能夠輕易的搭建出一個 WEB 工程
Spring 3.1
引入了激動人心的基於註釋(annotation
)的緩存(cache
)技術,它本質上不是一個具體的緩存實現方案(例如 EHCache
或者 Redis
),而是一個對緩存使用的抽象,經過在既有代碼中添加少許它定義的各類 annotation
,即可以達到緩存方法的返回對象的效果。html
<!-- more -->java
具有至關的好的靈活性,不只可以使用 SpEL(Spring Expression Language)
來定義緩存的 key 和各類 condition,還提供開箱即用的緩存臨時存儲方案,也支持和主流的專業緩存例如 EHCache、Redis、Guava 的集成。git
annotation
便可使得現有代碼支持緩存Out-Of-The-Box
,不用安裝和部署額外第三方組件便可使用緩存Spring Express Language
,能使用對象的任何屬性或者方法來定義緩存的 key
和 condition
AspectJ
,並經過其實現任何方法的緩存支持key
和自定義緩存管理者,具備至關的靈活性和擴展性下面針對Spring Cache
使用先後給出了僞代碼部分,具體中也許比這要更加複雜,可是Spring Cache
均可以很好的應對github
使用前
咱們須要硬編碼,若是切換Cache Client
還須要修改代碼,耦合度高,不易於維護redis
public String get(String key) { String value = userMapper.selectById(key); if (value != null) { cache.put(key,value); } return value; }
使用後
基於Spring Cache
註解,緩存由開發者本身配置,但不用參與到具體編碼spring
@Cacheable(value = "user", key = "#key") public String get(String key) { return userMapper.selectById(key); }
在 pom.xml
中添加 spring-boot-starter-data-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 Boot2.x
的改動,鏈接池相關配置須要經過spring.redis.lettuce.pool
或者 spring.redis.jedis.pool
進行配置了。使用了Spring Cache
後,能指定spring.cache.type
就手動指定一下,雖然它會自動去適配已有Cache
的依賴,但前後順序會對Redis
使用有影響(JCache -> EhCache -> Redis -> Guava
)apache
spring.redis.host=localhost spring.redis.password=battcn # 通常來講是不用配置的,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
建立一個User
類,目的是爲了模擬對象存儲緩存
package com.battcn.entity; import java.io.Serializable; /** * @author Levin * @since 2018/5/11 0007 */ public class User implements Serializable { private static final long serialVersionUID = 8655851615465363473L; private Long id; private String username; private String password; // TODO 省略get set }
package com.battcn.service; import com.battcn.entity.User; /** * @author Levin * @since 2018/5/11 0011 */ 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); }
爲了方便演示數據庫操做,直接定義了一個Map<Long, User> DATABASES
,這裏的核心就是@Cacheable
、@CachePut
、@CacheEvict
三個註解安全
package com.battcn.service.impl; import com.battcn.entity.User; import com.battcn.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** * @author Levin * @since 2018/5/11 0011 */ @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
必需要加,不然spring-data-cache
相關注解不會生效...
package com.battcn; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; /** * @author Levin */ @SpringBootApplication @EnableCaching public class Chapter9Application { public static void main(String[] args) { SpringApplication.run(Chapter9Application.class, args); } }
完成準備事項後,編寫一個junit
測試類來檢驗代碼的正確性,有不少人質疑過Redis
線程安全性,故下面也提供了響應的測試案例,若有疑問歡迎指正
package com.battcn; import com.battcn.entity.User; import com.battcn.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @author Levin * @since 2018/5/10 0010 */ @RunWith(SpringRunner.class) @SpringBootTest public class Chapter9ApplicationTest { private static final Logger log = LoggerFactory.getLogger(Chapter9ApplicationTest.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); } }
啓動測試類,結果和咱們指望的一致,能夠看到增刪改查中,查詢是沒有日誌輸出的,由於它直接從緩存中獲取的數據
,而添加、修改、刪除都是會進入方法內執行具體的業務代碼,而後經過切面去刪除掉Redis
中的緩存數據。其中 # 號表明這是一個 SpEL 表達式
,此表達式能夠遍歷方法的參數對象,具體語法能夠參考 Spring 的相關文檔手冊。
2018-05-14 09:20:55.303 INFO 21176 --- [ main] com.battcn.service.impl.UserServiceImpl : 進入 saveOrUpdate 方法 2018-05-14 09:20:55.582 INFO 21176 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library 2018-05-14 09:20:55.584 INFO 21176 --- [ main] io.lettuce.core.KqueueProvider : Starting without optional kqueue library 2018-05-14 09:20:56.316 INFO 21176 --- [ main] com.battcn.Chapter9ApplicationTest : [saveOrUpdate] - [User{id=5, username='u5', password='p5'}] 2018-05-14 09:20:56.320 INFO 21176 --- [ main] com.battcn.Chapter9ApplicationTest : [get] - [User{id=5, username='u5', password='p5'}] 2018-05-14 09:20:56.322 INFO 21176 --- [ main] com.battcn.service.impl.UserServiceImpl : 進入 delete 方法
其它類型
下列的就是Redis
其它類型所對應的操做方式
根據條件操做緩存內容並不影響數據庫操做,條件表達式返回一個布爾值,
true/false,當條件爲
true,則進行緩存操做,不然直接調用方法執行的返回結果。
長度
: @CachePut(value = "user", key = "#user.id",condition = "#user.username.length() < 10")
只緩存用戶名長度少於10的數據大小
: @Cacheable(value = "user", key = "#id",condition = "#id < 10")
只緩存ID小於10的數據組合
: @Cacheable(value="user",key="#user.username.concat(##user.password)")
提早操做:
@CacheEvict(value="user",allEntries=true,beforeInvocation=true)
加上beforeInvocation=true
後,無論內部是否報錯,緩存都將被清除,默認狀況爲false
@Cacheable(根據方法的請求參數對其結果進行緩存)
@Cacheable(value="user",key="#userName")
)@Cacheable(value="user")
或者 @Cacheable(value={"user1","use2"})
)@Cacheable(value = "user", key = "#id",condition = "#id < 10")
)
@CachePut(根據方法的請求參數對其結果進行緩存,和
@Cacheable
不一樣的是,它每次都會觸發真實方法的調用)
@CachEvict(根據條件對緩存進行清空)
@CacheEvict(value = "user", key = "#id", allEntries = true)
)@CacheEvict(value = "user", key = "#id", beforeInvocation = true)
)spring-cache
文檔: https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/integration.html#cache-introductionspring-data-redis
文檔: https://docs.spring.io/spring-data/redis/docs/2.0.1.RELEASE/reference/html/#new-in-2.0.0Redis
文檔: https://redis.io/documentationRedis
中文文檔: http://www.redis.cn/commands.html
目前不少大佬都寫過關於 SpringBoot
的教程了,若有雷同,請多多包涵,本教程基於最新的 spring-boot-starter-parent:2.0.1.RELEASE
編寫,包括新版本的特性都會一塊兒介紹...
battcn
全文代碼:https://github.com/battcn/spring-boot2-learning/tree/master/chapter9