SpringBoot整合redis并使用Spring Cache緩存注解
添加依賴
- 添加Redis依賴茉唉,在pom.xml文件中添加以下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- application.yml 配置Redis連接信息
spring:
cache:
type: redis
redis:
host: 127.0.0.1 # Redis服務器地址
database: 1 # Redis數(shù)據(jù)庫索引(默認為0)
port: 6379 # Redis服務器連接端口
password: # Redis服務器連接密碼(默認為空)
指定緩存類型
redis
在Spring Boot 2.7中使用@EnableCaching注解啟用緩存功能時窍荧,如果你想使用Redis作為緩存存儲嫩痰,你需要在配置文件中指定Redis的相關配置讼油。
你可以在application.properties或application.yml文件中添加以下配置:
spring.cache.type=redis
這樣配置后,Spring Boot會自動使用Redis作為緩存存儲辞嗡。當然不狮,你也可以根據(jù)需要配置其他的Redis相關屬性欣除,比如密碼色冀、連接池等潭袱。另外,你還需要在項目的依賴中添加Redis相關的依賴锋恬,比如spring-boot-starter-data-redis屯换。這樣Spring Boot才能正確地使用Redis作為緩存存儲。
- 3.配置緩存管理器
@Configuration
@EnableCaching
public class RedisTemplateConfiguration {
/**
* 默認過期時長与学,單位:秒
*/
@Getter
private long expire = 60 * 60 * 24;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
redisTemplate.setHashValueSerializer(RedisSerializer.java());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
總之彤悔,通過@EnableCaching注解,我們可以方便地啟用緩存功能索守,并結合Redis作為緩存實現(xiàn)晕窑。同時,通過@Cacheable注解卵佛,我們可以指定緩存名稱和鍵值杨赤,實現(xiàn)緩存功能。
在使用@EnableCaching注解后级遭,我們可以在需要緩存的方法上添加@Cacheable注解望拖,以啟用緩存功能。例如:
@ApiOperation("dictType => 全局 根據(jù)字典類型查詢字典詳情數(shù)據(jù)信息")
@GetMapping(value = "/dictType/{dictType}")
@Cacheable(value = CacheConstants.DICT_DETAILS, key = "#dictType")
public R<List<SysDictDetail>> dictType(@PathVariable String dictType) {
return R.success(sysDictDetailService.selectDictDataByType(dictType));
}
redis保存格式如下:
為了方便大家更清晰的理解挫鸽,我本地跑起一個程序驗證一下说敏,可以看一下打印的日志信息,第一次請求的時候是從數(shù)據(jù)庫中加載出來的數(shù)據(jù)丢郊,再次請求的時候直接從緩存里面讀取了盔沫。
@EnableCaching,@Cacheable, @CachePut,@CacheEvict詳解
1.@Cacheable
在啟動類XXApplication.java主類中中加入注解@EnableCaching
。
@Cacheable可以標記在一個方法上枫匾,也可以標記在一個類上架诞。
- 當標記在一個方法上時表示該方法是支持緩存的
- 當標記在一個類上時則表示該類所有的方法都是支持緩存的
對于一個支持緩存的方法,Spring會在其被調用后將其返回值緩存起來干茉,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結果谴忧,而不需要再次執(zhí)行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的角虫,值就是方法的返回結果沾谓,至于鍵的話,Spring又支持兩種策略戳鹅,默認策略和自定義策略均驶,這個稍后會進行說明。需要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發(fā)緩存功能的枫虏。
@Cacheable可以指定三個基本屬性妇穴,value爬虱、key和condition。
- 1.1.1 value屬性指定Cache名稱
value屬性是必須指定的腾它,其表示當前方法的返回值是會被緩存在哪個Cache上的跑筝,對應Cache的名稱。其可以是一個Cache也可以是多個Cache瞒滴,當需要指定多個Cache時其是一個數(shù)組继蜡。
//Cache是發(fā)生在cache1上的
@Cacheable("cache1")
public User find(Integer id) {
return null;
}
//Cache是發(fā)生在cache1和cache2上的
@Cacheable({"cache1", "cache2"})
public User find(Integer id) {
return null;
}
- 1.1.2 使用key屬性自定義key
key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式逛腿。當我們沒有指定該屬性時稀并,Spring將使用默認策略生成key。我們這里先來看看自定義策略单默,至于默認策略會在后文單獨介紹碘举。
在@Cacheable注解中key的取值也可以使用SpringEL表達式來生成,內部可以嵌套方法的參數(shù)信息搁廓,例如引颈,
@Cacheable(value = "fuck",key = "#id")
public List<String> getPrud(@RequestParam("test") String id){
System.out.println("如果第二次沒有走到這里說明緩存被添加了");
List<String> list = new ArrayList<>();
list.add(id);
list.add("123");
list.add("123");
return list;
}
此處的key值生成就是使用getPrud方法中的id參數(shù)生成的,即fuck:: + 參數(shù)id境蜕,除了這種方法以外還可以使用keyGenerator策略來生成key蝙场,即實現(xiàn)KeyGenerator方法,
@Component("myKeyGenerator")
public class MyKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
return "";
}
在此方法中生成的返回值就是最終拼湊起來的test::后面的東西粱年,實現(xiàn)方法之后再需要使用生成策略的方法中使用售滤,
@Cacheable(value = "fuck",keyGenerator = "myKeyGenerator")
public List<String> getPrud(@RequestParam("test") String id){
System.out.println("如果第二次沒有走到這里說明緩存被添加了");
List<String> list = new ArrayList<>();
list.add(id);
list.add("123");
list.add("123");
return list;
}
此方法的Redis緩存key值會按照fuck:: + myKeyGenerator組件中聲明的返回值來生成。
除了上述使用方法參數(shù)作為key之外台诗,Spring還為我們提供了一個root對象可以用來生成key完箩。通過該root對象我們可以獲取到以下信息。
屬性名稱 | 描述 | 示例 |
---|---|---|
methodName | 當前方法名 | #root.methodName |
method | 當前方法 | #root.method.name |
target | 當前被調用的對象 | #root.target |
targetClass | 當前被調用的對象的class | #root.targetClass |
args | 當前方法參數(shù)組成的數(shù)組 | #root.args[0] |
caches | 當前被調用的方法使用的Cache | #root.caches[0].name |
當我們要使用root對象的屬性作為key時我們也可以將“#root”省略拉队,因為Spring默認使用的就是root對象的屬性弊知。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
- 1.1.3 condition屬性指定發(fā)生的條件
有的時候我們可能并不希望緩存一個方法所有的返回結果。通過condition屬性可以實現(xiàn)這一功能粱快。condition屬性默認為空秩彤,表示將緩存所有的調用情形。其值是通過SpringEL表達式來指定的事哭,當為true時表示進行緩存處理漫雷;當為false時表示不進行緩存處理,即每次調用該方法時該方法都會執(zhí)行一次慷蠕。如下示例表示只有當user的id為偶數(shù)時才會進行緩存珊拼。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
2.@CachePut
對于使用@Cacheable標注的方法食呻,Spring在每次執(zhí)行前都會檢查Cache中是否存在相同key的緩存元素流炕,如果存在就不再執(zhí)行該方法澎现,而是直接從緩存中獲取結果進行返回,否則才會執(zhí)行并將返回結果存入指定的緩存中每辟。
@CachePut也可以聲明一個方法支持緩存功能剑辫。與@Cacheable不同的是使用@CachePut標注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結果,而是每次都會執(zhí)行該方法渠欺,并將執(zhí)行結果以鍵值對的形式存入指定的緩存中妹蔽。
@CachePut也可以標注在類上和方法上。使用@CachePut時我們可以指定的屬性跟@Cacheable是一樣的挠将。
//每次都會執(zhí)行方法胳岂,并將結果存入指定的緩存中
@CachePut("users")
public User find(Integer id) {
returnnull;
}
3.@CacheEvict
@CacheEvict是用來標注在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作舔稀。
@CacheEvict可以指定的屬性有value乳丰、key、condition内贮、allEntries和beforeInvocation产园。其中value、key和condition的語義與@Cacheable對應的屬性類似夜郁。即value表示清除操作是發(fā)生在哪些Cache上的(對應Cache的名稱)什燕;key表示需要清除的是哪個key,如未指定則會使用默認策略生成的key竞端;condition表示清除操作發(fā)生的條件屎即。
此注解的使用方法與@Cacheable基本一致,也有三個基本屬性事富,value剑勾、key和condition。不同的是在此注解中篩選出來的key值將被清除掉赵颅,例如:
@CacheEvict(value = "fuck",key = "1")
public String test1(){
return "fuck";
}
同時@CacheEvict也有自己的獨有的屬性操作虽另,下面我們來介紹一下新出現(xiàn)的兩個屬性allEntries
和beforeInvocation
。
- 3.1 allEntries屬性
allEntries是boolean類型饺谬,表示是否需要清除緩存中的所有元素捂刺。默認為false,表示不需要募寨。當指定了allEntries為true時族展,Spring Cache將忽略指定的key。有的時候我們需要Cache一下清除所有的元素拔鹰,這比一個一個清除元素更有效率仪缸。
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
- 3.2 beforeInvocation屬性
清除操作默認是在對應方法成功執(zhí)行之后觸發(fā)的,即方法如果因為拋出異常而未能成功返回時也不會觸發(fā)清除操作列肢。使用beforeInvocation可以改變觸發(fā)清除操作的時間恰画,當我們指定該屬性值為true時宾茂,Spring會在調用該方法之前清除緩存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
4.Caching
@Caching注解可以讓我們在一個方法或者類上同時指定多個Spring Cache相關的注解拴还。其擁有三個屬性:cacheable跨晴、put和evict,分別用于指定@Cacheable片林、@CachePut和@CacheEvict端盆。
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
return null;
}
5.使用自定義注解
Spring允許我們在配置可緩存的方法時使用自定義的注解,前提是自定義的注解上必須使用對應的注解進行標注费封。如我們有如下這么一個使用@Cacheable進行標注的自定義注解焕妙。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}
那么在我們需要緩存的方法上使用@MyCacheable進行標注也可以達到同樣的效果。
@MyCacheable
public User findById(Integer id) {
System.out.println("find user by id: " + id);
User user = new User();
user.setId(id);
user.setName("Name" + id);
return user;
}
常見問題:
1弓摘、緩存的值沒有實現(xiàn)Serializable接口
Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [cn.harry.common.api.R]
這個錯誤通常是因為在使用Spring Cache時访敌,緩存的值沒有實現(xiàn)Serializable接口,導致無法序列化衣盾。解決這個問題的方法是讓緩存的值實現(xiàn)Serializable接口寺旺,以便在序列化時能夠正確地將對象轉換為字節(jié)流。下面是一個示例:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Cacheable(value = "user_details", key = "#id")
public Serializable getUserById(Long id) {
User user = userDao.getUserById(id);
return (Serializable) user;
}
}
在上面的示例中势决,我們在getUserById()方法上添加了@Cacheable注解阻塑,并指定了緩存名稱為"user_details",緩存鍵為方法參數(shù)id果复。同時陈莽,我們將返回值類型改為Serializable,并將User對象強制轉換為Serializable類型虽抄,以便在序列化時能夠正確地將對象轉換為字節(jié)流走搁。
總之,讓緩存的值實現(xiàn)Serializable接口是解決這個問題的關鍵迈窟。如果緩存的值無法實現(xiàn)Serializable接口私植,可以考慮使用其他的緩存實現(xiàn)方式,例如使用JSON格式存儲緩存值车酣。