一個系統在於數據庫交互的過程當中,內存的速度遠遠快於硬盤速度,當咱們重複地獲取相同數據時,咱們一次又一次地請求數據庫或遠程服務,者無疑時性能上地浪費(這會致使大量時間被浪費在數據庫查詢或者遠程方法調用上導致程序性能惡化),因而有了「緩存」。 本文將介紹在spring boot項目開發中怎樣使用spring提供的Spring Cache 與最近很火的 Redis 數據庫來實現數據的緩存。html
Spring Cache
是Spring
框架提供的對緩存使用的抽象類,支持多種緩存,好比Redis
、EHCache
等,集成很方便。同時提供了多種註解來簡化緩存的使用,可對方法進行緩存。java
Spring Cache提供了一些供咱們使用的SpEL上下文數據,下表直接摘自Spring官方文檔:git
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root對象 | 當前被調用的方法名 | #root.methodName |
method | root對象 | 當前被調用的方法 | #root.method.name |
target | root對象 | 當前被調用的目標對象 | #root.target |
targetClass | root對象 | 當前被調用的目標對象類 | #root.targetClass |
args | root對象 | 當前被調用的方法的參數列表 | #root.args[0] |
caches | root對象 | 當前方法調用使用的緩存列表(如@Cacheable(value={"cache1", "cache2"})),則有兩個cache | #root.caches[0].name |
argument name | 執行上下文 | 當前被調用的方法的參數,如findById(Long id),咱們能夠經過#id拿到參數 | #user.id |
result | 執行上下文 | 方法執行後的返回值(僅當方法執行以後的判斷有效,如‘unless’,'cache evict'的beforeInvocation=false) | #result |
其餘關於 Cache 詳細配置或註解,請參考文章基於Redis的Spring cache 緩存介紹或spring官方文檔github
Redis 是徹底開源免費的,遵照BSD協議,是一個高性能的key-value數據庫。web
Redis 與其餘 key - value 緩存產品有如下三個特色:redis
Redis 的安裝和使用請自行Google 。spring
咱們要把一個查詢函數加入緩存功能,大體須要三步。數據庫
本實例沒有存入MySQL數據庫,主要是爲了方便實踐,實際使用中你們能夠把service層中的方法改成數據庫操做代碼便可。json
<!-- springboot redis依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
複製代碼
注: 其實咱們從官方文檔能夠看到spring-boot-starter-data-redis 已經包含了jedis客戶端,咱們在使用jedis鏈接池的時候沒必要再添加jedis依賴。緩存
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @Author: MaoLin * @Date: 2019/3/26 17:04 * @Version 1.0 */ @Configuration @EnableCaching public class RedisConfig { /** * 申明緩存管理器,會建立一個切面(aspect)並觸發Spring緩存註解的切點(pointcut) * 根據類或者方法所使用的註解以及緩存的狀態,這個切面會從緩存中獲取數據,將數據添加到緩存之中或者從緩存中移除某個值 * @return */ @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return RedisCacheManager.create(redisConnectionFactory); } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { // 建立一個模板類 RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); // 將剛纔的redis鏈接工廠設置到模板類中 template.setConnectionFactory(factory); // 設置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); // 設置value的序列化器 //使用Jackson 2,將對象序列化爲JSON Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //json轉對象類,不設置默認的會將json轉成hashmap ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } } 複製代碼
server: port: 8080 spring: # redis相關配置 redis: database: 0 host: localhost port: 6379 password: 123456 jedis: pool: # 鏈接池最大鏈接數(使用負值表示沒有限制) max-active: 8 # 鏈接池最大阻塞等待時間(使用負值表示沒有限制) max-wait: -1ms # 鏈接池中的最大空閒鏈接 max-idle: 8 # 鏈接池中的最小空閒鏈接 min-idle: 0 # 鏈接超時時間(毫秒)默認是2000ms timeout: 2000ms cache: redis: ## Entry expiration in milliseconds. By default the entries never expire. time-to-live: 1d #寫入redis時是否使用鍵前綴。 use-key-prefix: true 複製代碼
import lombok.Data; import java.io.Serializable; /** * @Author: MaoLin * @Date: 2019/3/24 14:36 * @Version 1.0 */ @Data //lombok依賴,可省略get set方法 public class User implements Serializable { private int userId; private String userName; private String userPassword; public User(int userId, String userName, String userPassword) { this.userId = userId; this.userName = userName; this.userPassword = userPassword; } } 複製代碼
import com.ml.demo.entity.User; import org.springframework.stereotype.Service; /** * @Author: MaoLin * @Date: 2019/3/24 14:38 * @Version 1.0 */ @Service public class UserDao { public User getUser(int userId) { System.out.println("執行此方法,說明沒有緩存,若是沒有走到這裏,就說明緩存成功了"); User user = new User(userId, "沒有緩存_"+userId, "password_"+userId); return user; } public User getUser2(int userId) { System.out.println("執行此方法,說明沒有緩存,若是沒有走到這裏,就說明緩存成功了"); User user = new User(userId, "name_nocache"+userId, "nocache"); return user; } } 複製代碼
開箱即用
的Springimport com.ml.demo.dao.UserDao; import com.ml.demo.entity.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @Author: MaoLin * @Date: 2019/3/26 17:03 * @Version 1.0 */ @RestController public class testController { @Resource private UserDao userDao; /** * 查詢出一條數據而且添加到緩存 * * @param userId * @return */ @RequestMapping("/getUser") @Cacheable("userCache") public User getPrud(@RequestParam(required = true) String userId) { System.out.println("若是沒有緩存,就會調用下面方法,若是有緩存,則直接輸出,不會輸出此段話"); return userDao.getUser(Integer.parseInt(userId)); } /** * 刪除一個緩存 * * @param userId * @return */ @RequestMapping(value = "/deleteUser") @CacheEvict("userCache") public String deleteUser(@RequestParam(required = true) String userId) { return "刪除成功"; } /** * 添加一條保存的數據到緩存,緩存的key是當前user的id * * @param user * @return */ @RequestMapping("/saveUser") @CachePut(value = "userCache", key = "#result.userId +''") public User saveUser(User user) { return user; } /** * 返回結果userPassword中含有nocache字符串就不緩存 * * @param userId * @return */ @RequestMapping("/getUser2") @CachePut(value = "userCache", unless = "#result.userPassword.contains('nocache')") public User getUser2(@RequestParam(required = true) String userId) { System.out.println("若是走到這裏說明,說明緩存沒有生效!"); User user = new User(Integer.parseInt(userId), "name_nocache" + userId, "nocache"); return user; } @RequestMapping("/getUser3") @Cacheable(value = "userCache", key = "#root.targetClass.getName() + #root.methodName + #userId") public User getUser3(@RequestParam(required = true) String userId) { System.out.println("若是第二次沒有走到這裏說明緩存被添加了"); return userDao.getUser(Integer.parseInt(userId)); } } 複製代碼
接下來最重要的工做:跑起來
存入數據:
從緩存讀取數據:
刪除緩存:
再讀取:
此時沒有緩存,調用方法,並存入緩存
此爲cache中的條件:含有nocache字符時不存入緩存。本身去探索就好。
爲了實現緩存,在網上參考了不少博客、資料,可是都不盡人意,後來通過幾天的學習,發現Spring提供了緩存對象,我結合redis,優雅地實現了緩存。學習代碼是個艱辛的過程,我在學習這部分時看了好多書,找了好多博客資料,終於找到了合適的緩存方案,很開心,不過這還只是一小步啊,加油!!!