Spring Cache 抽象(緩存抽象) Redis 緩存

                                                                   聚沙成塔 ----  僅以此致敬和我同樣在慢慢前進的人兒html

相關內容:java

    https://blog.51cto.com/14230003/2369413?source=dra
git

               https://www.iteye.com/blog/jinnianshilongnian-2001040github

               https://www.jianshu.com/p/931484bb3fdcredis

    https://www.jianshu.com/p/734d0e0419cb算法

              https://www.imooc.com/article/30466spring

               https://blog.csdn.net/LouisQMei/article/details/79941725  (spring cache 我的以爲講的還不錯)sql

              http://www.javashuo.com/article/p-bvbyffmz-mn.html(這個好像是和springBoot 視頻中相互對應的)數據庫

              https://www.jianshu.com/p/712368507d81(爲何要使用redis 做爲緩存)json

 

問題一: 什麼是緩存,爲何要使用緩存 

            1.  緩存是用於拉近使用者和數據之間的距離(這個好像是buffer的做用),使得用戶在訪問相同的數據的時候,不須要每次都從數據庫中取出來而是在緩存中取

                 出來(固然是由於存在緩存中的數據的讀取速度很快相對於從數據庫中讀取出來)

            2. 使用緩存主要目的在於高性能高併發(見連接一)

 

問題二: spring 中自帶緩存機制嗎? 如何實現的? 

  spring 中使用的緩存使用concurrentMap實現的,

  若是是第三方使用緩存的話,只要實現CacheMannerger 接口便可

spring 本身內部設置了 spring cache, spring cachemanager 這樣的接口, spring 自身也提供了一種實現, 是經過對 cachemanager 的實現,用concurrentMap

實現的, 第三方cache 緩存進來以後, 只須要實現spring cachemanager 這個接口便可, 如 redis, ehcache

 

 

 

問題三:什麼是序列化 

  衆所周知,類的對象會隨着程序的終止而被垃圾收集器銷燬。若是要在不從新建立對象的狀況下調用該類,該怎麼作?這就能夠經過序列化將數據轉換爲字節序列

        因此序列化是方便對象的持久化,便於存儲。 把一個對象序列化後生成字節序列, 而後想再次使用這個對象的時候,直接讀取字節序列,而後反序列化,就能夠獲得這個對象了

  上面序列化和反序列化都是由JVM 獨立執行的, 因此字節序列能夠在任何環境下面,經過JVM 反序列化爲對象

因此有兩個點:

      什麼是序列化: 序列化是經過*** 將對象轉化爲字節序列的過程, 這個字節序列裏面包含了對象的一些信息***

      什麼是反序列化: 反序列化是經過**** 將字節序列轉化爲一個對象的過程

問題: 那麼什麼樣的類(對象)能夠進行序列化

      常規的類,實現Serializable接口,就能夠

      (你上面說的,我好像見過,可是我在類的裏面又看到serialVersionUID = ** 這個, 這個是幹什麼的)

    serialVersionUID 這個是爲了保證反序列化成功的, 保證反序列化後生成的對象跟原來的對象是惟一的,用於輔助序列化的

   具體的執行過程是: 對象在序列化的時候,也會將serialVersionUID 的信息也添加到字節序列中去, 而後在反序列化的時候,JVM會將字節流裏面的serialVersionUID 

   與你要生成的對象的serialVersionUID 進行比較,若是相同,則進行反序列化,反之則反序列化失敗(這個可能說的不對)

   

問題四: spring 自帶的緩存機制中是否存在的問題? 

  spring 中的自帶的緩存中,將對象序列化的時候,是將對象序列化爲字節序列,因此在查看緩存的時候就帶來了問題, 不易於查看。

    

 

 

問題五:爲何不用spring自帶的,而使用 Redis作 緩存? 

         大概是由於其支持的數據類型比較多吧,如list、set、sortedset、hashmap、string,而memecache只能支持簡單的數據類型。

        另外redis能夠完成一部份數據的持久化,而memecache徹底將數據保存在內存中,不進行持久化,若是服務器出問題,數據

       將所有丟失,另外,多是redis底層實現優化比memecache好吧

        注1:有一些sql語句,好比sql語句中含有自定義函數、自定義變量、含有不肯定數據[NOW()、CURRENT_DATE()等]、還有

                  其餘一些sql語句的查詢語句是不會被加入到查詢緩存中的,查詢時也不會通過查詢緩存

 

問題六: 導入redis緩存依賴以後, springBoot如何進配置的? 

   在導入spring-redis-starter 依賴以後,這個依賴裏面已經存在了不少的自動配置類 如 RedisAutoConfig,自動配置了redisCachemanager ,

        以及註冊了 Redistemplate, StringRedisTemplate 用於對redis 進行操做,取代了客戶端的做用。

        由於springBoot 加載緩存的配置類的時候,有一套順序, 即優先級高的配置類,在ioc 容器中,則springBoot 加載優先級高的配置。

       由於導入 spring-redis-starter 依賴 自動配置的redis 緩存的配置的順序(優先級)高於springBoot 默認使用的緩存,因此如今springBoot 就切換到redis 進行緩存

 

問題六:什麼是FastJson 

  fastjson是一個java語言編寫的高性能且功能完善的JSON庫,它採用一種「假定有序快速匹配」的算法,把JSON Parse 的性能提高到了極致。

      它的接口簡單易用,已經被普遍使用在緩存序列化,協議交互,Web輸出等各類應用場景中。

      fastJson:用於將對象序列化爲json字符串,而且能夠將json字符串轉化對象

鑑於上述: 可使用fastJson  序列化工具, 替換原來的springBoot 中 redis 自動配置中的序列化方法

 

 

問題七:配置 Redis CacheManger 的流程 

  

package com.qf.springboot01.config;

import com.alibaba.fastjson.parser.ParserConfig;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.qf.springboot01.util.FastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import javax.crypto.KeyGenerator;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 重寫Redis序列化方式,使用Json方式:
     * 當咱們的數據存儲到Redis的時候,咱們的鍵(key)和值(value)都是經過Spring提供的Serializer序列化到數據庫的。RedisTemplate默認使用的是JdkSerializationRedisSerializer,StringRedisTemplate默認使用的是StringRedisSerializer。
     * Spring Data JPA爲咱們提供了下面的Serializer:
     * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
     * 在此咱們將本身配置RedisTemplate並定義Serializer。
     *
     */

// 存入redis時,默認使用的是JdkSerializationRedisSerializer,使得存入的數據所有序列化了,所需自定義一個RedisTemplate,使用其餘序列化方式


    //當redis依賴包導入的時候,默認的cache便可自動變成redis模式;若是隻是導入cache的依賴,則默認的是simpleCacheManager;
// 使用redis緩存時,RedisCacheManager生成RedisCache後生成緩存時默認使用JdkSerializationRedisSerializer序列化(cache存儲的時候)

    //當ioc容器內沒有自定義的緩存管理器的時候---默認使用自帶的;
    //當經過@Bean在ioc容器中注入瞭如下管理器,則會使用自定義的管理器;

//    @Bean
//    public CacheManager cacheManager(RedisConnectionFactory factory) {
//        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();  // 生成一個默認配置,經過config對象便可對緩存進行自定義配置
//        config = config.entryTtl(Duration.ofMinutes(1))     // 設置緩存的默認過時時間,也是使用Duration設置
//                .disableCachingNullValues();     // 不緩存空值
//
//        // 設置一個初始化的緩存空間set集合
//        Set<String> cacheNames =  new HashSet<>();
//        cacheNames.add("my-redis-cache1");
//        cacheNames.add("my-redis-cache2");
//
//        // 對每一個緩存空間應用不一樣的配置
//        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
//        configMap.put("my-redis-cache1", config);
//        configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
//
//        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)     // 使用自定義的緩存配置初始化一個cacheManager
//                .initialCacheNames(cacheNames)  // 注意這兩句的調用順序,必定要先調用該方法設置初始化的緩存名,再初始化相關的配置
//                .withInitialCacheConfigurations(configMap)
//                .build();
//        return cacheManager;
//    }

    @Bean
    @Primary//當有多個管理器的時候,必須使用該註解在一個管理器上註釋:表示該管理器爲默認的管理器
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        //初始化一個RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        //序列化方式1
        //設置CacheManager的值序列化方式爲JdkSerializationRedisSerializer,但其實RedisCacheConfiguration默認就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,因此如下(4行)註釋代碼爲默認實現
//        ClassLoader loader = this.getClass().getClassLoader();
//        JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
//        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
//        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
        //序列化方式1---另外一種實現方式
        //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//該語句至關於序列化方式1

        //序列化方式2: 使用fastJson
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);//JSONObject
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

        //序列化方式3
        //Jackson2JsonRedisSerializer serializer=new Jackson2JsonRedisSerializer(Object.class);
        //RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer);
        //RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

        defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(100));//設置過時時間 //        //設置默認超過時時間是30秒
//        defaultCacheConfig.entryTtl(Duration.ofSeconds(30));

        //初始化RedisCacheManager
        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);

        //設置白名單---很是重要********
        /*
        使用fastjson的時候:序列化時將class信息寫入,反解析的時候,
        fastjson默認狀況下會開啓autoType的檢查,至關於一個白名單檢查,
        若是序列化信息中的類路徑不在autoType中,
        反解析就會報com.alibaba.fastjson.JSONException: autoType is not support的異常
        可參考 https://blog.csdn.net/u012240455/article/details/80538540
         */
        ParserConfig.getGlobalInstance().addAccept("com.qf.springboot01.entities");
        return cacheManager;
    }


    /**
     *  設置 redis 數據默認過時時間
     *  設置@cacheable 序列化方式
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(30));
        return configuration;
    }


    @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);
        // value值的序列化採用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        // key的序列化採用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }


//    @Bean
//    public KeyGenerator KeyGenerator() {
//        return new KeyGenerator(){
//            public Object generate(Object target, Method method, Object... params) {
//                StringBuilder sb = new StringBuilder();
//                sb.append(target.getClass().getName());
//                sb.append(method.getName());
//                for (Object obj : params) {
//                    sb.append(obj.toString());
//                }
//                return sb.toString();
//            }
//        };
//    }

}
相關文章
相關標籤/搜索