积少成多 ---- 仅以此致敬和我一样在慢慢前进的人儿
相关内容:
https://blog.51cto.com/14230003/2369413?source=dra
https://www.iteye.com/blog/jinnianshilongnian-2001040
https://www.jianshu.com/p/931484bb3fdc
https://www.jianshu.com/p/734d0e0419cb
https://www.imooc.com/article/30466
https://blog.csdn.net/LouisQMei/article/details/79941725 (spring cache 个人觉得讲的还不错)
https://www.cnblogs.com/wangxiayun/p/10221695.html(这个好像是和springBoot 视频中相互对应的)
https://www.jianshu.com/p/712368507d81(为什么要使用redis 作为缓存)
问题一: 什么是缓存,为什么要使用缓存
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(); // } // }; // } }