文章來源 https://blog.csdn.net/u010588262/article/details/81003493
1. pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2. Springboot配置文件
spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=5 spring.redis.password=123456 # 連接超時時間 單位 ms(毫秒) spring.redis.timeout=3000 #=========redis線程池設置========= # 連接池中的最大空閑連接,默認值也是8。 spring.redis.pool.max-idle=10 #連接池中的最小空閑連接,默認值也是0。 spring.redis.pool.min-idle=0 # 如果賦值為-1,則表示不限制;pool已經分配了maxActive個jedis實例,則此時pool的狀態為exhausted(耗盡)。 spring.redis.pool.max-active=10 # 等待可用連接的最大時間,單位毫秒,默認值為-1,表示永不超時 spring.redis.pool.max-wait=1000
3. Redis配置文件
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * @Auther: hugeo.wang * @Date: 2018/7/11 11:07 * @Description: */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(60)) .disableCachingNullValues(); return RedisCacheManager.builder(factory) .cacheDefaults(config) .transactionAware() .build(); } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); RedisSerializer keySerializer = new StringRedisSerializer(); // 設置key序列化類,否則key前面會多了一些亂碼 template.setKeySerializer(keySerializer); setValueSerializer(template);//設置value序列化 template.afterPropertiesSet(); template.setEnableTransactionSupport(true); return template; } private void setValueSerializer(StringRedisTemplate template) { @SuppressWarnings({"rawtypes", "unchecked"}) Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); } }
4. 使用三個注解開始玩耍
@Cacheable
@Cacheable可以標記在一個方法上,也可以標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類所有的方法都是支持緩存的。
如果一個方法上添加了@Cacheable標記,Spring會在其被調用后將其返回值緩存起來,以保證下次利用同樣的參數來執行該方法時可以直接從緩存中獲取結果,而不需要再次執行該方法。
緩存是以鍵值對進行的,值就是方法的返回結果,至於鍵的話,Spring又支持兩種策略,默認策略和自定義策略,需要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發緩存功能的。
@Cacheable可以指定三個屬性,value、key和condition。
value 指定Cache名稱
value
必須指定,表示當前方法的返回值會被緩存在哪個Cache上,對應Cache的名稱。可以是一個Cache也可以是多個Cache,當需要指定多個Cache時其是一個數組。
@Cacheable("cache1")//Cache是發生在cache1上的 public User find(Integer id) { return null; } @Cacheable({"cache1", "cache2"})//Cache是發生在cache1和cache2上的 public User find(Integer id) { return null; }
key 自定義緩存的鍵
用來指定Spring緩存時對應的key的。該屬性支持SpringEL表達式。當我們沒有指定該屬性時,Spring將使用默認策略生成key。
自定義策略是指我們可以通過Spring的EL表達式來指定我們的key。這里的EL表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用#參數名或者#p參數index。下面是幾個使用參數作為key的示例。
// 如果要用固定字符串加上參數的屬性記得加單引號 @Cacheable(value="users", key="'helloworld'+#p0.id") public User find(User user) { return null; } @Cacheable(value="users", key="#id") public User find(Integer id) { return null; } @Cacheable(value="users", key="#p0") public User find(Integer id) { return null; } @Cacheable(value="users", key="#user.id") public User find(User user) { return null; } @Cacheable(value="users", key="#p0.id") public User find(User user) { return null; }
除了上述使用方法參數作為key之外,Spring還為我們提供了一個root對象可以用來生成key。通過該root對象我們可以獲取到以下信息。
屬性名稱 | 描述 | 示例 |
methodName | 當前方法名 | root.methodName |
method | 當前方法 | root.method.name |
target | 當前被調用的對象 | root.target |
targetClass | 當前被調用的對象的 | class root.targetClass |
args | 當前方法參數組成的數組 | 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; }
有的時候我們可能並不希望緩存一個方法所有的返回結果。通過condition屬性可以實現這一功能。condition屬性默認為空,表示將緩存所有的調用情形。其值是通過SpringEL表達式來指定的,當為true時表示進行緩存處理;當為false時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次判斷。如下示例表示只有當user的id為偶數時才會進行緩存。
@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; }
@CacheEvict
這個@CacheConfig注解用來清除緩存,在注解里指定cachename,key和condition,spring會把符合這三個要求的緩存刪除掉
總共有5個屬性:value,key,condition,allentries,beforeInvocation
前三個注解不說了
allentries
缺省為false,將這個屬性設置為true的話就不用設置key了,會刪除value指定的cachename下所有的緩存
beforeInvocation
缺省為false,默認spring是會在調用方法成功之后清除緩存的,如果方法里面拋錯了自然也就不清除了,但是把此值設置為true的話spring會在調用方法前就刪除緩存,也就是說不管方法執行結果如何緩存都刪。
@CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 緩存 public void updateAccount(Account account) { updateDB(account); } @CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 緩存 public void reload() { reloadAll() }
@CachePut
這個注解容易與@Cacheable搞混,對於@Cacheable標注的方法,Spring在每次執行前都會檢查Cache中是否存在相同key的緩存元素,如果存在就不再執行該方法,而是直接從緩存中獲取結果進行返回,否則才會執行並將返回結果存入指定的緩存中。
@CachePut也可以聲明一個方法支持緩存功能。不同的是使用@CachePut標注的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。
@CachePut("users")//每次都會執行方法,並將結果存入指定的緩存中 public User find(Integer id) { return null; }
@CacheConfig
這是一個類級別的注解,比如一個類里所有的緩存都是放在cachename為books
的緩存中,那么每個@Cacheable中都要寫value=”books”,現在有個更方便的方法了
@CacheConfig(value="books") public class BookRepositoryImpl implements BookRepository { @Cacheable(key="'book'") public Book findBook(ISBN isbn) {...} }
@Caching
有時候我們可能組合多個Cache注解使用;比如用戶新增成功后,我們要添加id–>user;username—>user;email—>user的緩存;此時就需要@Caching組合多個注解標簽了。
@Caching(put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.email") }) public User save(User user) {}
自定義緩存注解
剛剛那個@Caching組合,會讓方法上的注解顯得整個代碼比較亂,此時可以使用自定義注解把這些注解組合到一個注解中,如:
@Caching(put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.email") }) @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface UserSaveCache { }
這樣我們在方法上使用如下代碼即可,整個代碼顯得比較干凈。
@UserSaveCache public User save(User user)
綜合實例
//@Cacheable 在執行方法之前判斷condition,如果返回true,則查緩存; @Cacheable(value = "user", key = "#id", condition = "#id lt 10") public User conditionFindById(final Long id) {} //@CachePut 在執行完方法后判斷condition,如果返回true,則放入緩存; @CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'") public User conditionSave(final User user) {} //@CachePut將在執行完方法后判斷unless,如果返回false,則放入緩存;(即跟condition相反) @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'") public User conditionSave2(final User user) {} //@CacheEvict, beforeInvocation=false表示在方法執行之后調用,判斷condition,如果返回true,則移除緩存; @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'") public User conditionDelete(final User user) {}