前言
前面有介紹過spring整合redis和redis的一些注意點,在實際開發中,spring cache方便簡化的緩存操作的大部分內容。通過註解的方式實現緩存。
緩存抽象的核心是將緩存應用於Java方法,從而根據緩存中可用的信息減少執行次數。也就是說,每次調用目標方法時,抽象都會應用一種緩存行為,該行為檢查該方法是否已針對給定參數執行。
- 如果已執行,則返回緩存的結果,而不必執行實際方法。
- 如果該方法尚未執行,則執行該方法,並將結果緩存並返回給用戶,以便下次調用該方法時,返回緩存的結果。
- 這樣,對於給定的一組參數,昂貴的方法(無論是CPU還是IO)只能執行一次,並且重用結果而不必再次實際執行該方法。
在緩存框架出現之前,要麼公司自己寫緩存框架造輪子,要麼在每個service中手動修改調用redis進行存儲或者更新(刪除).一旦業務邏輯代碼有變,將造成不小的維護負擔。並且代碼量也很大,使得項目看起來很臃腫!
而spring cache的出現用於簡化對緩存的操作,你只需要配置緩存的cacheManager,然後按照一些簡單的序列化配置,在service層即可使用註解的形式對項目邏輯無侵入的調用緩存。
方法介紹
@Cacheable:
觸發緩存填充。即:如果有緩存從緩存中直接拿數據(就不走service了),如果沒緩存那麼就執行service。將service中的值傳到緩存中(執行service)。
多個名稱:緩存可能有一個名字或者多個名字,那麼你可以@Cacheable("books")或者@Cacheable({"books", "isbns"})表示。
自定義key:大家對同一個接口進行查詢,可能有多個參數,那麼你可以自定義key。類似@Cacheable(value = "getuser",key = "#username")這樣寫法,這個規則和mybatis註解很像,但也有點區別也可以自行拼湊。
條件緩存:有些數據你若不想所有都想走緩存,當然也可以設置條件緩存,比如你若想讓兩個字的姓名緩存,那麼你可以@Cacheable(value = "getuser",key = "#username",condition="#username.length() < 3")這樣寫法
上鎖:@Cacheable(value = "getuser",key = "#username",sync = true)對有些數據,你可能不想讓他併發執行,那麼可以假設sync關鍵字。當然,如果是分佈式或者多臺服務器需要考慮分佈式鎖實現。
@Cacheable(value = "getalluser")
public List<user> getalluser()/<user>
{
/*
假設數據從數據庫查出啦。有這麼兩條
*/
user user1=new user("bigsai","12345","man");
user user2=new user("給我star","謝謝","man");
List<user>list=new ArrayList<>();/<user>
list.add(user1);
list.add(user2);
return list;
}
//獲取user 如果沒有緩存將執行log
@Cacheable(value = "getuser",key = "#username")
public user getuserbyname(String username)
{
//假設該數據從mysql查詢結果如下
user user=new user(username,"123","women");
logger.info("執行方法cacheable,從mysql查詢");
return user;
}
@CacheEvict:
觸發緩存逐出。即刪除緩存。執行之後將刪除緩存。
用法和前面相似@CacheEvict(value = "getuser",key = "#username")
- 但是若想刪除getuser下所有緩存目錄,那麼加個參數@CacheEvict(value = "getuser",key = "#username",allEntries = true)其中allEntries就是刪除所有的意思。當需要清除整個緩存區域時,此選項會派上用場。而不是逐出每個條目。
@CachePut:
更新緩存而不會干擾方法執行。即:不管有沒有緩存。每次都執行方法,將方法得到的數據更新到緩存之中。起到update的功能。
需要注意的是,在更新過程中可能有併發的存在。你可以設置鎖.參考Cacheable用法
@Caching:
重新組合要應用於方法的多個緩存操作。
有時,需要指定相同類型(例如@CacheEvict或 @CachePut)的多個註釋- 例如,因為不同高速緩存之間的條件或鍵表達式不同。@Caching允許多個嵌套 @Cacheable,@CachePut和@CacheEvict註解相同的方法來使用。以下示例使用兩個@CacheEvict註釋:
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
此操作可能是一個方法操作更新、刪除不同的緩存。具體使用需要考慮情景。
@CacheConfig:
在類級別共享一些常見的緩存相關設置。
此方法針對同一個類中一些共同操作,簡便操作和代碼量。
到目前為止,我們已經看到緩存操作提供了許多自定義選項,您可以為每個操作設置這些選項。但是,如果某些自定義選項適用於該類的所有操作,則它們可能會很繁瑣。例如,指定用於類的每個高速緩存操作的高速緩存的名稱可以由單個類級定義替換。這是@CacheConfig 發揮作用的地方。以下示例用於@CacheConfig設置緩存的名稱:
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
具體整合
step1: 創建springboot工程,省略部分截圖
step2:添加maven依賴
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-data-redis/<artifactid>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-cache/<artifactid>
<dependency>
<groupid>org.apache.commons/<groupid>
<artifactid>commons-pool2/<artifactid>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-web/<artifactid>
step3: 配置application.properties
spring.redis.host=127.0.0.1
spring.redis.password=
spring.redis.port= 6379
spring.redis.timeout=10000
spring.cache.type=redis
logging.level.com.redisCache=debug
spring.redis.database=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
step4: config目錄下建立配置文件(緩存注入對象配置,以及序列化相關配置,和前文redis差不多)
- 這裡面需要注意的是裡面有關於redis的一些配置。在項目中防止多個項目公用一個redis可以在redis前面加個前綴(通常是項目名)。
- 過期時間一定要設置,並且過期策略還需要根據項目需求具體踩坑設置。
package com.redisCache.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
import java.time.Duration;
import java.util.*;
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfiguration {
@Bean
public RedisTemplate<string> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {/<string>
RedisTemplate<string> template = new RedisTemplate<>();/<string>
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean(name = "cacheManager")
@Primary
public CacheManager cacheManager( RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// .entryTtl(Duration.ofSeconds(50))設置過期時間
.disableCachingNullValues()
.computePrefixWith(cacheName -> "rediscache".concat(":").concat(cacheName).concat(":"))//rediscache可以改成你的項目名
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
}
step5:創建pojo對象和controller
pojo:
package com.redisCache.pojo;
import java.io.Serializable;
public class user implements Serializable {
private String name;
private String password;
private String sex;
public user(String name,String password,String sex)
{
this.name=name;
this.password=password;
this.sex=sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
service:
package com.redisCache.service;
import com.redisCache.pojo.user;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
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.ArrayList;
import java.util.List;
@Service
public class redisService {
Logger logger= LoggerFactory.getLogger(redisService.class);
@Cacheable(value = "getalluser")
public List<user> getalluser()/<user>
{
/*
假設數據從數據庫查出啦。有這麼兩條
*/
user user1=new user("bigsai","12345","man");
user user2=new user("給我star","謝謝","man");
List<user>list=new ArrayList<>();/<user>
list.add(user1);
list.add(user2);
return list;
}
//獲取user 如果沒有緩存將執行log
@Cacheable(value = "getuser",key = "#username")
public user getuserbyname(String username)
{
//假設該數據從mysql查詢結果如下
user user=new user(username,"123","women");
logger.info("執行方法cacheable,從mysql查詢");
return user;
}
//更新user。每次都執行
@CachePut(value = "getuser",key = "#username")
public user updateuser(String username,String password)
{
//假設更新用戶賬號密碼re
user user=new user(username,"123","women");
user.setPassword(password);
logger.info("執行方法cacheput,再數據庫更新返回");
return user;
}
//刪除緩存,其中condition可加可不加,本句意思是隻有當姓名為bigsai
@CacheEvict(value = "getuser",key = "#username",allEntries = true)
public String deleteuser(String username)
{
return "移除成功";
}
}
controller:
package com.redisCache.controller;
import com.redisCache.pojo.user;
import com.redisCache.service.redisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class redisController {
private final static Logger log= LoggerFactory.getLogger(redisController.class);
@Autowired(required = false)
redisService redisService;
@GetMapping("getalluser")
public List<user>getalluser()/<user>
{
return redisService.getalluser();
}
@GetMapping("getuser/{username}")
public user getuser(@PathVariable String username)
{
return redisService.getuserbyname(username);
}
@GetMapping("updateuser/{username}/{password}")
public user updateuser(@PathVariable String username,@PathVariable String password)
{
return redisService.updateuser(username,password);
}
@GetMapping("deleteuser/{username}")
public String deleteuser(@PathVariable String username)
{
return redisService.deleteuser(username);
}
}
step6:在啟動類加上允許緩存註解
測試與總結
getalluser:
getuser:
update:更新上一條
delete:整個移除(前面講到allentries參數作用,不妨自己測試)
可以看得到緩存的一些操作正常完成了
上文只是一個簡單的整合過程,具體使用還需要自己踩坑才行。而緩存的深度遠遠不是這麼簡單,還需要自己挖掘。
看完的朋友 求點贊!求轉發!求關注!
另外需要Java/架構的學習資料學習的朋友可以看一下我的其他文章,可結合自身情況有選擇下方資料:
全網最新架構實戰文檔:高併發+分佈式+微服務+SpringBoot+Nginx
SB+SC+Nginx+Docker+分佈式 2020最新實戰文檔PDF
2020最新一線大廠名企面試題大全
除此之外小編蒐集整理一份Java核心知識點:其中包含有基礎知識、Java集合、JVM、多線程併發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java算法、數據庫、Zookeeper、分佈式緩存、數據結構等等
每篇文章下方都有領取方式,歡迎獲取! 注意:免費分享!
閱讀更多 互聯網架構小馬哥 的文章