淺析SpringBoot緩存原理探究、SpringCache常用注解介紹及如何集成Redis


一、SpringBoot 緩存原理探究

1、SpringCache 介紹

  在SpringBoot中,數據的緩存管理存儲依賴於Spring框架中cache相關的org.springframework.cache.Cache和org.springframework.cache.CacheManager緩存管理器接口。

  如果程序中沒有定義類型為CacheManager的Bean組件或者是名為cacheResolver的CacheResolver緩存解析器,Spring Boot將嘗試選擇並啟用以下緩存組件(按照指定的順序):

(1)Generic (2)JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan等) (3)EhCache 2.x (4)Hazelcast (5)Infinispan (6)Couchbase (7)Redis (8)Caffeine (9)Simple (10)NoOp

  上面按照Spring Boot緩存組件的加載順序,列舉了支持的10種緩存組件,在項目中添加某個緩存管理組件(例如Redis)后,Spring Boot項目會選擇並啟用對應的緩存管理器。

  如果項目中同時添加了多個緩存組件,且沒有指定緩存管理器或者緩存解析器(CacheManager或者cacheResolver),那么Spring Boot會按照上述順序在添加的多個緩存中優先啟用指定的緩存組件進行緩存管理。

  Spring Boot默認緩存管理中,沒有添加任何緩存管理組件能實現緩存管理。這是因為開啟緩存管理后,Spring Boot會按照上述列表順序查找有效的緩存組件進行緩存管理,如果沒有任何緩存組件,會默認使用Simple緩存組件進行管理。

  Simple緩存組件是Spring Boot默認的緩存管理組件,它默認使用內存中的ConcurrentMap進行緩存存儲,所以在沒有添加任何第三方緩存組件的情況下,可以實現內存中的緩存管理,但是我們不推薦使用這種緩存管理方式。

  當在Spring Boot默認緩存管理的基礎上引入Redis緩存組件,即在pom.xml文件中添加Spring Data Redis依賴啟動器后,SpringBoot會使用RedisCacheConfigratioin當做生效的自動配置類進行緩存相關的自動裝配,容器中使用的緩存管理器是RedisCacheManager, 這個緩存管理器創建的Cache為 RedisCache, 進而操控redis進行數據的緩存。

  依賴:org.springframework.boot   和   spring-boot-starter-data-redis

2、緩存配置類:SpringBoot提供了10個配置類,對應上面介紹的那 10 個緩存組件,其中SimpleCacheConfiguration是默認配置類

org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration // 默認
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration

3、SpringBoot各類緩存優先級

(1)默認使用默認緩存,當導入其他依賴則默認緩存失效

(2)導入多種依賴,則按照上面排序生效其中一個

// 源碼
@ConditionalOnMissingBean({CacheManager.class}) @Conditional({CacheCondition.class}) class SimpleCacheConfiguration { SimpleCacheConfiguration() { }

  源碼中,使用了注解@ConditionalOnMissingBean,其作用是:判斷當前需要注入Spring容器中的bean的實現類是否已經含有,有的話不注入,沒有就注入(被覆蓋)。當注入其他緩存后,會在容器中注入對應緩存的CacheManager,此時,由於上述注解的原因,默認緩存則不再生效。

4、緩存結構:Cache結構是一個map: ConcurrentMap<Object, Object>

5、核心思想:

  當我們調用一個方法時會把該方法的參數和返回結果作為一個鍵值對存放在緩存中,等下次利用同樣的參數來調用該方法時將不會再執行,而是直接從緩存中獲取結果進行返回。

  理解:springboot 的緩存機制是通過切面編程 aop 來實現的。

二、SpringCache 注解

  Spring Cache 提供了 @Cacheable 、@CachePut 、@CacheEvict 、@Caching 等注解,在方法上使用。

  基於注解方式SpringCache引入Redis做緩存,需要先了解@EnableCaching、@CacheConfig、@Cacheable、@CachePut、@CacheEvict、@Caching相關注解的使用。

1、@EnableCaching

  開啟緩存功能,一般放在啟動類上或者自定義的RedisConfig配置類上。

2、@CacheConfig:在類上使用,當該類有多個方法有同樣的緩存操作時使用

  當我們需要緩存的地方越來越多,可以使用@CacheConfig(cacheNames = "cacheName") 注解在 class 之上來統一指定value的值,統一管理keys,這時可省略value,如果你在你的方法依舊寫上了value,那么依然以方法的value值為准。

@Service @CacheConfig(cacheNames = "categories") public class CategoryServiceImpl implements CategoryService{}

3、@Cacheable:用於查詢數據,添加緩存

  根據方法對其返回結果進行緩存,下次請求時,如果緩存存在,則直接讀取緩存數據返回;如果緩存不存在,則執行方法,並把返回的結果存入緩存中。一般用在查詢方法上。

4、@CachePut:用於新增數據時, 自動更新緩存

  使用該注解標志的方法,每次都會執行,並將結果存入指定的緩存中。其他方法可以直接從響應的緩存中讀取緩存數據,而不需要再去查詢數據庫。

  一般用在新增方法上。 查看源碼,屬性值同上@Cacheable差不多。

5、@CacheEvict:用於修改或刪除數據時,清空緩存

  使用該注解標志的方法,會清空指定的緩存。一般用在更新或者刪除方法上。

  查看源碼,屬性值與@Cacheable差不多,獨有的兩個屬性如下:

  allEntries是否清空所有緩存,默認為false。如果指定為true,則方法調用后將立即清空所有的緩存

  beforeInvocation是否在方法執行前就清空所有緩存,默認為false。如果指定為true,則方法執行前就會清空所有的緩存

6、@Caching:該注解可以實現同一個方法上同時使用多種注解

// 可從其源碼看出
public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; }
@Caching(evict = {   @CacheEvict(cacheNames = "eventAskMaxId", key = "#ask.eventId", condition = "#ask.eventId != null"),   @CacheEvict(cacheNames = "eventAskTotal", key = "#ask.eventId", condition = "#ask.eventId != null") }) public void insert(EventAsk ask) {}

三、SpringBoot快速集成Redis

1、導入依賴

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

2、添加配置:連接池參數要選擇lettuce的(默認),不要配成jedis的,源碼中jedis很多類不存在,因此是不生效的。

3、使用:(1)緩存:直接使用SpringBoot的緩存注解即可;(2)操作:使用 RedisTemplate 或 RedisUtils 操作數據

  需要注意的是:實體類要實現Serializable(序列化)才能存入redis, 不然會報錯

public class Table implements Serializable {}

四、如何編寫Redis配置文件

1、配置文件解決核心問題:

(1)實現序列化后,redis工具中顯示亂碼:重寫redisTemplate方法,使用fastjson進行序列化解決

(2)設置默認過期時間

(3)Redis 掛后,報錯且不走數據庫:捕獲Redis連接超時的異常,使Redis掛掉后,不影響正常訪問

2、序列化問題源碼分析:

(1)默認使用的RedisTemplate設置的JDK序列化(序列化效果不好),可以自定義redisTemplate替換默認的

(2)對象的兩個泛型都是Object,需要強制轉換為<String, Object>

(3)由於String 是redis最常用的類型,所以單獨提供了StringRedisTemplate

// RedisAutoConfiguration源碼
@Bean @ConditionalOnMissingBean( name = {"redisTemplate"} )// 可以自定義redisTemplate替換默認的
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // 默認沒有做額外的序列化(使用的RedisTemplate默認序列化),redis對象都是需要序列化的 // 兩個泛型都是Object,需要強制轉換為<String, Object>
    RedisTemplate<Object, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean // 由於String 是redis最常用的類型,所以單獨提供了StringRedisTemplate
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
// RedisTemplate源碼
if (this.defaultSerializer == null) { // RedisTemplate默認配置了JDK序列化 this.defaultSerializer = new JdkSerializationRedisSerializer(...); }

3、自定義 Redis 的序列化及配置

(1)使用fastjson序列化redis

(2)Redis 配置文件

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {} @Slf4j @Configuration @EnableCaching @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) public class RedisConfig extends CachingConfigurerSupport { // 1、解決序列化問題
    @Bean(name = "redisTemplate") @SuppressWarnings("unchecked") @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); //使用fastjson序列化
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); // ......
        return template; } // 2、設置key默認過期時間
 @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), this.redisCacheConfiguration(1800),     // 默認配置
            this.initialCacheConfigurations());    // 指定key過期時間配置
 } // 3、配置redis掛掉后,捕獲異常,不影響查詢數據庫
    public CacheErrorHandler errorHandler() { CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {       ...... }; return cacheErrorHandler; } }

  注意上面是偽代碼,項目上的代碼就不方便貼咯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM