SpringBoot整合redis并使用Spring Cache緩存注解

SpringBoot整合redis并使用Spring Cache緩存注解

添加依賴

    1. 添加Redis依賴茉唉,在pom.xml文件中添加以下依賴:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
    1. 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保存格式如下:


image.png

為了方便大家更清晰的理解挫鸽,我本地跑起一個程序驗證一下说敏,可以看一下打印的日志信息,第一次請求的時候是從數(shù)據(jù)庫中加載出來的數(shù)據(jù)丢郊,再次請求的時候直接從緩存里面讀取了盔沫。

image.png

@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)的兩個屬性allEntriesbeforeInvocation

  • 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格式存儲緩存值车酣。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末曲稼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子湖员,更是在濱河造成了極大的恐慌贫悄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娘摔,死亡現(xiàn)場離奇詭異窄坦,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門鸭津,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彤侍,“玉大人,你說我怎么就攤上這事曙博。” “怎么了怜瞒?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵父泳,是天一觀的道長。 經常有香客問我吴汪,道長惠窄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任漾橙,我火速辦了婚禮杆融,結果婚禮上,老公的妹妹穿的比我還像新娘霜运。我一直安慰自己脾歇,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布淘捡。 她就那樣靜靜地躺著藕各,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焦除。 梳的紋絲不亂的頭發(fā)上激况,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音膘魄,去河邊找鬼乌逐。 笑死,一個胖子當著我的面吹牛创葡,可吹牛的內容都是我干的浙踢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼灿渴,長吁一口氣:“原來是場噩夢啊……” “哼成黄!你這毒婦竟也來了?” 一聲冷哼從身側響起逻杖,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤奋岁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后荸百,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闻伶,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年够话,在試婚紗的時候發(fā)現(xiàn)自己被綠了蓝翰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光绕。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖畜份,靈堂內的尸體忽然破棺而出诞帐,到底是詐尸還是另有隱情,我是刑警寧澤爆雹,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布停蕉,位于F島的核電站,受9級特大地震影響钙态,放射性物質發(fā)生泄漏慧起。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一册倒、第九天 我趴在偏房一處隱蔽的房頂上張望蚓挤。 院中可真熱鬧,春花似錦驻子、人聲如沸灿意。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脾歧。三九已至,卻和暖如春演熟,著一層夾襖步出監(jiān)牢的瞬間鞭执,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工芒粹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留兄纺,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓化漆,卻偏偏與公主長得像估脆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子座云,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容