1. 程式人生 > >springboot整合spring @Cache和Redis

springboot整合spring @Cache和Redis

轉載請註明出處:https://www.cnblogs.com/wenjunwei/p/10779450.html

spring基於註解的快取

對於快取宣告,spring的快取提供了一組java註解:

  • @Cacheable:觸發快取寫入。
  • @CacheEvict:觸發快取清除。
  • @CachePut:更新快取(不會影響到方法的執行)。
  • @Caching:重新組合要應用於方法的多個快取操作。
  • @CacheConfig:設定類級別上共享的一些常見快取設定。

@Cacheable註解

顧名思義,@Cacheable可以用來進行快取的寫入,將結果儲存在快取中,以便於在後續呼叫的時候可以直接返回快取中的值,而不必再執行實際的方法。 最簡單的使用方式,註解名稱=快取名稱,使用例子如下:

  @Cacheable("books")
    public Book findBook(ISBN isbn) {...}

一個方法可以對應兩個快取名稱,如下:

    @Cacheable({"books", "isbns"})
    public Book findBook(ISBN isbn) {...} 

@Cacheable的快取名稱是可以配置動態引數的,比如選擇傳入的引數,如下: (以下示例是使用SpEL宣告,如果您不熟悉SpEL,可以閱讀Spring Expression Language)

    @Cacheable(cacheNames="books", key="#isbn")
    public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
    
    @Cacheable(cacheNames="books", key="#isbn.rawNumber")
    public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
    
    @Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
    public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 

@Cacheable還可以設定根據條件判斷是否需要快取

  • condition:取決於給定的引數是否滿足條件
  • unless:取決於返回值是否滿足條件

以下是一個簡單的例子:

    @Cacheable(cacheNames="book", condition="#name.length() < 32") 
    public Book findBook(String name)
    
    @Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") 
    public Book findBook(String name) 

@Cacheable還可以設定:keyGenerator(指定key自動生成方法),cacheManager(指定使用的快取管理),cacheResolver(指定使用快取的解析器)等,這些引數比較適合全域性設定,這裡就不多做介紹了。

@CachePut註解

@CachePut:當需要更新快取而不干擾方法的執行時 ,可以使用該註解。也就是說,始終執行該方法,並將結果放入快取,註解引數與@Cacheable相同。 以下是一個簡單的例子:

    @CachePut(cacheNames="book", key="#isbn")
    public Book updateBook(ISBN isbn, BookDescriptor descriptor) 

通常強烈建議不要對同一方法同時使用@CachePut和@Cacheable註解,因為它們具有不同的行為。可能會產生不可思議的BUG哦。

@CacheEvict註解

@CacheEvict:刪除快取的註解,這對刪除舊的資料和無用的資料是非常有用的。這裡還多了一個引數(allEntries),設定allEntries=true時,可以對整個條目進行批量刪除。 以下是個簡單的例子:

    @CacheEvict(cacheNames="books") 
    public void loadBooks(InputStream batch)
    
    //對cacheNames進行批量刪除
    @CacheEvict(cacheNames="books", allEntries=true) 
    public void loadBooks(InputStream batch) 

@Caching註解

@Caching:在使用快取的時候,有可能會同時進行更新和刪除,會出現同時使用多個註解的情況.而@Caching可以實現。 以下是個簡單的例子:

    @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
    public Book importBooks(String deposit, Date date) 

@CacheConfig註解

@CacheConfig:快取提供了許多的註解選項,但是有一些公用的操作,我們可以使用@CacheConfig在類上進行全域性設定。 以下是個簡單的例子:

    @CacheConfig("books") 
    public class BookRepositoryImpl implements BookRepository {
    
        @Cacheable
        public Book findBook(ISBN isbn) {...}
    } 

可以共享快取名稱,統一配置KeyGenerator,CacheManager,CacheResolver。

例項

來看看我們在springboot中怎麼使用redis來作為快取吧.

為spring cache配置redis作為快取

1.在pom.xml引入redis依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 

2.springboot整合redis配置檔案(在本地啟動的redis),在springboot中使用redis,只要配置檔案寫有redis配置,程式碼就可以直接使用了。

spring:
  redis:
    database: 0 # Database index used by the connection factory.
    url: redis://user:@127.0.0.1:6379 # Connection URL. Overrides host, port, and password. User is ignored. Example: redis://user:[email protected]:6379
    host: 127.0.0.1 # Redis server host.
    password: # Login password of the redis server.
    port: 6379 # Redis server port.
    ssl: false # Whether to enable SSL support.
    timeout: 5000 # Connection timeout. 

3.redis快取配置類CacheConfig,這裡對spring的快取進行了配置,包括KeyGenerator,CacheResolver,CacheErrorHandler,CacheManager,還有redis序列化方式。

/**
 * @author wwj
 */
@Configuration
public class CacheConfig extends CachingConfigurerSupport {

    @Resource
    private RedisConnectionFactory factory;

    /**
     * 自定義生成redis-key
     *
     * @return
     */
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()).append(".");
            sb.append(method.getName()).append(".");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            System.out.println("keyGenerator=" + sb.toString());
            return sb.toString();
        };
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        return redisTemplate;
    }

    @Bean
    @Override
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(cacheManager());
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 用於捕獲從Cache中進行CRUD時的異常的回撥處理器。
        return new SimpleCacheErrorHandler();
    }

    @Bean
    @Override
    public CacheManager cacheManager() {
        RedisCacheConfiguration cacheConfiguration =
                defaultCacheConfig()
                        .disableCachingNullValues()
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();

    }
} 

程式碼使用

測試@Cacheable方法

    @Test
    public void findUserTest() {
        for (int i = 0; i < 3; i++) {
            System.out.println("第" + i + "次");
            User user = userService.findUser();
            System.out.println(user);
        }
    }

    @Override
    @Cacheable(value = {"valueName", "valueName2"}, key = "'keyName1'")
    public User findUser() {
        System.out.println("執行方法...");
        return new User("id1", "張三", "深圳", "1234567", 18);
    } 

執行結果

只有一次輸出了'執行方法...',後面直接從快取獲取,不會再進入方法。

第0次
執行方法...
User{id='id1', name='張三', address='深圳', tel='1234567', age=18}
第1次
User{id='id1', name='張三', address='深圳', tel='1234567', age=18}
第2次
User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

 

測試@CachePut方法:對快取進行了修改

    @Test
    public void updateUserTest() {
        userService.updateUser();
        User user = userService.findUser();
        System.out.println(user);
    }
    
    @Override
    @CachePut(value = "valueName", key = "'keyName1'")
    public User updateUser() {
        System.out.println("更新使用者...");
        return new User("id1", "李四", "北京", "1234567", 18);
    } 

執行結果

對快取進行了更新,獲取值的時候取了新的值

更新使用者...
User{id='id1', name='李四', address='北京', tel='1234567', age=18}

 

 

測試@CacheEvict方法:快取被清空,再次findUser的時候又重新執行了方法。

    @Test
    public void clearUserTest() {
        userService.clearUser();
        User user = userService.findUser();
        System.out.println(user);
    }

    @Override
    @CacheEvict(value = "valueName",allEntries = true)
    public void clearUser() {
        System.out.println("清除快取...");
    } 

執行結果

這裡清除了快取,為什麼還是沒有執行方法呢?因為這個方法我們定了兩個value值,清了一個還有一個

清除快取...
User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

 

最後貼一下程式碼吧

User.java

package com.wwj.springboot.model;

import java.io.Serializable;

/**
 * @author wwj
 */
public class User implements Serializable {

    public User() {
    }

    private String id;
    private String name;
    private String address;
    private String tel;
    private Integer age;

    //省略get,set,tostring
}

CacheTest.java

package com.wwj.springboot.cache;

import com.wwj.springboot.model.User;
import com.wwj.springboot.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

/**
 * @author wwj
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableCaching
public class CacheTest {

    @Resource
    private UserService userService;

    @Test
    public void findUserTest() {
        for (int i = 0; i < 3; i++) {
            System.out.println("第" + i + "次");
            User user = userService.findUser();
            System.out.println(user);
        }
    }

    @Test
    public void updateUserTest() {
        userService.updateUser();
        User user = userService.findUser();
        System.out.println(user);
    }

    @Test
    public void clearUserTest() {
        userService.clearUser();
        User user = userService.findUser();
        System.out.println(user);
    }

}

UserService.java

package com.wwj.springboot.service;

import com.wwj.springboot.model.User;

import java.util.List;

/**
 * @author wwj
 */
public interface UserService {

    /**
     * 獲取使用者
     * @return user
     */
    User findUser();

    /**
     * 更新使用者資訊
     * @return user
     */
    User updateUser();

    /**
     * 清除快取的使用者資訊
     */
    void clearUser();

}

UserServiceImpl.java

package com.wwj.springboot.service.impl;

import com.wwj.springboot.model.User;
import com.wwj.springboot.service.UserService;
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;

/**
 * @author wwj
 */
@Service
@CacheConfig(cacheNames = "CacheConfigName")
public class UserServiceImpl implements UserService {

    @Override
    @Cacheable(value = {"valueName", "valueName2"}, key = "'keyName1'")
    public User findUser() {
        System.out.println("執行方法...");
        return new User("id1", "張三", "深圳", "1234567", 18);
    }

    @Override
    @CachePut(value = "valueName", key = "'keyName1'")
    public User updateUser() {
        System.out.println("更新使用者...");
        return new User("id1", "李四", "北京", "1234567", 18);
    }

    @Override
    @CacheEvict(value = "valueName",allEntries = true)
    public void clearUser() {
        System.out.println("清除快取...");
    }


}

 

本文歡迎各位轉載,但是轉載文章之後必須在文章開頭給出原文連結。感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點個“推薦”支援一下。

&n