前言

在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理。

接下来分别针对基于API的Redis缓存实现和基于注解的Redis缓存实现中的数据序列化机制进行介绍,并自定义JSON格式的数据序列化方式进行数据缓存管理。

基于API的Redis缓存实现——自定义RedisTemplate

1、Redis API默认序列化方式源码解析

基于API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,查看RedisTemplate的源码信息:

  1. public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
  2.  
  3. private boolean enableTransactionSupport = false;
  4. private boolean exposeConnection = false;
  5. private boolean initialized = false;
  6. private boolean enableDefaultSerializer = true;
  7. private @Nullable RedisSerializer<?> defaultSerializer;
  8. private @Nullable ClassLoader classLoader;
  9.  
  10. // 声明了key、value的各种序列化方式,初始值为空
  11. @SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
  12. @SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
  13. @SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
  14. @SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
  15. ...
  16.  
  17. /*
  18. * 进行默认序列化方式设置,设置为JDK序列化方式
  19. * (non-Javadoc)
  20. * @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
  21. */
  22. @Override
  23. public void afterPropertiesSet() {
  24.  
  25. super.afterPropertiesSet();
  26.  
  27. boolean defaultUsed = false;
  28.  
  29. if (defaultSerializer == null) {
  30.  
  31. defaultSerializer = new JdkSerializationRedisSerializer(
  32. classLoader != null ? classLoader : this.getClass().getClassLoader());
  33. }
  34.  
  35. if (enableDefaultSerializer) {
  36.  
  37. if (keySerializer == null) {
  38. keySerializer = defaultSerializer;
  39. defaultUsed = true;
  40. }
  41. if (valueSerializer == null) {
  42. valueSerializer = defaultSerializer;
  43. defaultUsed = true;
  44. }
  45. if (hashKeySerializer == null) {
  46. hashKeySerializer = defaultSerializer;
  47. defaultUsed = true;
  48. }
  49. if (hashValueSerializer == null) {
  50. hashValueSerializer = defaultSerializer;
  51. defaultUsed = true;
  52. }
  53. }
  54.  
  55. if (enableDefaultSerializer && defaultUsed) {
  56. Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
  57. }
  58.  
  59. if (scriptExecutor == null) {
  60. this.scriptExecutor = new DefaultScriptExecutor<>(this);
  61. }
  62.  
  63. initialized = true;
  64. }
  65. ...
  66. }

从上述RedisTemplate核心源码可以看出,在RedisTemplate内部声明了缓存数据key、value的各种序列化方式,各种初始值都为空;在afterPropertiesSet()方法中,判断如果默认序列化参数defaultSerializer为空,则将数据的默认序列化方式设置为JdkSerializationRedisSerializer。

根据上述源码信息可得出以下两个重要结论:

(1)使用RedisTemplate进行Redis数据缓存操作时,内部默认使用的是JdkSerializationRedisSerializer序列化方式,所以进行数据缓存的实体类必须实现JDK自带的序列化接口(例如Serializable);

(2)使用RedisTemplate进行Redis数据缓存操作时,如果自定义了缓存序列化方式defaultSerializer,那么将使用自定义的序列化方式。

另外,在RedisTemplate类的源码中,看到的缓存数据key、value的各种序列化类型都是RedisSerializer。进入RedisSerializer查看RedisSerializer支持的序列化方式:

可以看到,RedisSerializer是一个Redis序列化接口,默认有6个实现类,这6个实现类代表了6种不同的数据序列化方式。其中,JdkSerializationRedisSerializer是JDK自带的,也是RedisTemplate内部默认使用的序列化方式,开发者可以根据需要选择其他支持的序列化方式(例如JSON方式)。

2、自定义RedisTemplate序列化机制

在项目中引入Redis依赖后,SpringBoot提供的RedisAutoConfiguration自动配置会生效。打开RedisAutoConfiguration类,查看内部源码中关于RedisTemplate的定义方式:

  1. package org.springframework.boot.autoconfigure.data.redis;
  2.  
  3. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  4. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  5. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  6. import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
  7. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.context.annotation.Import;
  11. import org.springframework.data.redis.connection.RedisConnectionFactory;
  12. import org.springframework.data.redis.core.RedisOperations;
  13. import org.springframework.data.redis.core.RedisTemplate;
  14. import org.springframework.data.redis.core.StringRedisTemplate;
  15.  
  16. /**
  17. * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
  18. *
  19. * @author Dave Syer
  20. * @author Andy Wilkinson
  21. * @author Christian Dupuis
  22. * @author Christoph Strobl
  23. * @author Phillip Webb
  24. * @author Eddú Meléndez
  25. * @author Stephane Nicoll
  26. * @author Marco Aust
  27. * @author Mark Paluch
  28. * @since 1.0.0
  29. */
  30. @Configuration(proxyBeanMethods = false)
  31. @ConditionalOnClass(RedisOperations.class)
  32. @EnableConfigurationProperties(RedisProperties.class)
  33. @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
  34. public class RedisAutoConfiguration {
  35.  
  36. @Bean
  37. @ConditionalOnMissingBean(name = "redisTemplate")
  38. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
  39. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  40. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  41. template.setConnectionFactory(redisConnectionFactory);
  42. return template;
  43. }
  44.  
  45. ...
  46.  
  47. }

从上述RedisAutoConfiguration核心源码中可以看出,在Redis自动配置类中,通过Redis连接工厂RedisConnectionFactory初始化了一个RedisTemplate;在该方法上方添加了一个@ConditionalOnMissingBean注解(顾名思义,当某个Bean不存在时生效),用来表明如果开发者自定义了一个名为redisTemplate的Bean,那么该默认初始化的RedisTemplate就不会生效。

如果要使用自定义序列化方式的RedisTemplate进行数据缓存操作,可以参考上述核心代码创建一个名为redisTemplate的Bean组件,并在该组件中设置对应的序列化方式即可。

接下来,在项目中创建名为com.hardy.springbootdatacache.config的包,在该包下创建一个Redis自定义配置类RedisConfig,并按照上述思路自定义名为redisTemplate的Bean组件:

  1. package com.hardy.springbootdatacache.config;
  2.  
  3. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  4. import com.fasterxml.jackson.annotation.PropertyAccessor;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.data.redis.connection.RedisConnectionFactory;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  11.  
  12. /**
  13. * @Author: HardyYao
  14. * @Date: 2021/6/24
  15. */
  16. @Configuration
  17. public class RedisConfig {
  18.  
  19. @Bean
  20. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  21. RedisTemplate<Object, Object> template = new RedisTemplate();
  22. template.setConnectionFactory(redisConnectionFactory);
  23. // ֵ使用JSON格式序列化对象,对缓存数据key和value进行转换
  24. Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
  25. // 解决查询缓存转换异常的问题
  26. ObjectMapper om = new ObjectMapper();
  27. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  28. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  29. jacksonSeial.setObjectMapper(om);
  30. // 设置RedisTemplate模板API的序列化方式为JSON
  31. template.setDefaultSerializer(jacksonSeial);
  32. return template;
  33. }
  34.  
  35. }

上述代码通过@Configuration注解定义了一个RedisConfig配置类,并使用@Bean注解注入了一个默认名称为方法名的redisTemplate的Bean组件(注意:该Bean组件名称必须是redisTemplate)。在定义的Bean组件中,自定义了一个RedisTemplate,使用自定义的Jackson2JsonRedisSerializer数据序列化方式;在定制序列化方式中,定义了一个ObjectMapper用于进行数据转换设置。

3、效果测试

启动项目,通过浏览器访问:http://localhost:8080/api/findCommentById?id=2(连续访问三次),查看网页返回信息及控制台消息:

根据控制台打印消息可知,执行findById()方法正确查询出了用户评论信息Comment,重复进行同样的查询操作,数据库也不会重复执行SQL语句,这表明定制的Redis缓存生效了。

使用Redis客户端可视化管理工具Redis Desktop Manager查看缓存数据:

执行findById()方法查询到的用户评论信息Comment正确存储到了Redis缓存库中,且缓存到Redis服务的数据已经使用了JSON格式的数据存储展示,查看和管理也十分方便,这说明自定义的Redis API模板工具RedisTemplate生效了。

基于注解的Redis缓存实现——自定义RedisCacheManager

刚刚针对基于API方式的RedisTemplate进行了自定义序列化方式的改进,从而实现了JSON序列化方式缓存数据,但是这种自定义的RedisTemplate对于基于注解的Redis缓存来说,是没有作用的。

接下来,针对基于注解的Redis缓存机制和自定义序列化方式进行讲解。

1、Redis注解默认序列化机制

打开SpringBoot整合Redis组件提供的缓存自动配置类RedisCacheConfiguration(org.springframework.boot.autoconfigure.cache包下的),查看该类的源码信息,其核心代码如下:

  1. package org.springframework.boot.autoconfigure.cache;
  2.  
  3. import java.util.LinkedHashSet;
  4. import java.util.List;
  5.  
  6. import org.springframework.beans.factory.ObjectProvider;
  7. import org.springframework.boot.autoconfigure.AutoConfigureAfter;
  8. import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis;
  9. import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
  10. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  11. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  12. import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
  13. import org.springframework.cache.CacheManager;
  14. import org.springframework.context.annotation.Bean;
  15. import org.springframework.context.annotation.Conditional;
  16. import org.springframework.context.annotation.Configuration;
  17. import org.springframework.core.io.ResourceLoader;
  18. import org.springframework.data.redis.cache.RedisCacheManager;
  19. import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
  20. import org.springframework.data.redis.connection.RedisConnectionFactory;
  21. import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
  22. import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
  23.  
  24. @Configuration(proxyBeanMethods = false)
  25. @ConditionalOnClass(RedisConnectionFactory.class)
  26. @AutoConfigureAfter(RedisAutoConfiguration.class)
  27. @ConditionalOnBean(RedisConnectionFactory.class)
  28. @ConditionalOnMissingBean(CacheManager.class)
  29. @Conditional(CacheCondition.class)
  30. class RedisCacheConfiguration {
  31.  
  32. @Bean
  33. RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
  34. ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
  35. ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
  36. RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
  37. RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
  38. determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
  39. List<String> cacheNames = cacheProperties.getCacheNames();
  40. if (!cacheNames.isEmpty()) {
  41. builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
  42. }
  43. if (cacheProperties.getRedis().isEnableStatistics()) {
  44. builder.enableStatistics();
  45. }
  46. redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
  47. return cacheManagerCustomizers.customize(builder.build());
  48. }
  49.  
  50. private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
  51. CacheProperties cacheProperties,
  52. ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
  53. ClassLoader classLoader) {
  54. return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
  55. }
  56.  
  57. private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
  58. CacheProperties cacheProperties, ClassLoader classLoader) {
  59. Redis redisProperties = cacheProperties.getRedis();
  60. org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
  61. .defaultCacheConfig();
  62. // 默认也是使用JdkSerializationRedisSerializer作为序列化方式
  63. config = config.serializeValuesWith(
  64. SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
  65. if (redisProperties.getTimeToLive() != null) {
  66. config = config.entryTtl(redisProperties.getTimeToLive());
  67. }
  68. if (redisProperties.getKeyPrefix() != null) {
  69. config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
  70. }
  71. if (!redisProperties.isCacheNullValues()) {
  72. config = config.disableCachingNullValues();
  73. }
  74. if (!redisProperties.isUseKeyPrefix()) {
  75. config = config.disableKeyPrefix();
  76. }
  77. return config;
  78. }
  79.  
  80. }

从上述核心源码可看出,同RedisAutoConfiguration源码(其中定义的RedisTemplate)类似,RedisCacheConfiguration内部同样通过Redis连接工厂RedisConnectionFactory定义了一个缓存管理器RedisCacheManager;同时定制RedisCacheManager时,也默认使用了JdkSerializationRedisSerializer序列化方式。

如果想要使用自定义序列化方式的RedisCacheManager进行数据缓存操作,可以参考上述核心源码创建一个名为cacheManager的Bean组件,并在该组件中设置对应的序列化方式即可。

注意:在SpringBoot 2.X版本中,RedisCacheManager是单独进行构建的。因此,在SpringBoot 2.X版本中,对RedisTemplate进行自定义序列化机制构建后,仍然无法对RedisCacheManager内部默认序列化机制进行覆盖(这也就解释了基于注解的Redis缓存实现仍然会使用JDK默认序列化机制的原因),想要基于注解的Redis缓存实现也是用自定义序列化机制。想要自定义RedisCacheManager。

2、自定义RedisCacheManager

在项目的Redis配置类RedisConfig,按照上一步分析的定制方法自定义名为cacheManager的Bean组件:

  1. package com.hardy.springbootdatacache.config;
  2.  
  3. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  4. import com.fasterxml.jackson.annotation.PropertyAccessor;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  9. import org.springframework.data.redis.cache.RedisCacheManager;
  10. import org.springframework.data.redis.connection.RedisConnectionFactory;
  11. import org.springframework.data.redis.core.RedisTemplate;
  12. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  13. import org.springframework.data.redis.serializer.RedisSerializationContext;
  14. import org.springframework.data.redis.serializer.RedisSerializer;
  15. import org.springframework.data.redis.serializer.StringRedisSerializer;
  16.  
  17. import java.time.Duration;
  18.  
  19. /**
  20. * @Author: HardyYao
  21. * @Date: 2021/6/24
  22. */
  23. @Configuration
  24. public class RedisConfig {
  25.  
  26. ...
  27.  
  28. @Bean
  29. public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
  30. // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
  31. RedisSerializer<String> strSerializer = new StringRedisSerializer();
  32. Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer(Object.class);
  33. // 解决查询缓存转换异常问题
  34. ObjectMapper om = new ObjectMapper();
  35. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  36. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  37. jacksonSerial.setObjectMapper(om);
  38. // 定制缓存数据序列化方式及时效
  39. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
  40. .entryTtl(Duration.ofDays(1))
  41. .serializeKeysWith(RedisSerializationContext.SerializationPair
  42. .fromSerializer(strSerializer))
  43. .serializeValuesWith(RedisSerializationContext.SerializationPair
  44. .fromSerializer(jacksonSerial))
  45. .disableCachingNullValues();
  46. RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
  47. return cacheManager;
  48. }
  49.  
  50. }

上述代码中,在RedisConfig配置类中使用@Bean注解注入了一个默认名称为方法名的cacheManager组件。在定义的Bean组件中,通过RedisCacheConfiguration对缓存数据的key和value分别进行了序列化方式的定制,其中缓存数据的key定制为StringRedisSerializer(即String格式),而value定制了Jackson2JsonRedisSerializer(即JSON格式),同时还是用entryTtl(Duration.ofDays(1))方式将缓存数据有效期设置为1天。

完成基于注解的Redis缓存管理器RedisCacheManager定制后,可以对该缓存管理器的效果进行测试。(记得要开启SpringBoot基于注解的缓存管理支持,即在启动类上添加@EnableCaching注解。另外,使用自定义序列化机制的RedisCacheManager测试时,实体类可以不用实现序列化接口)。

启动项目,通过浏览器访问:http://localhost:8080/findCommentById?id=2(连续访问三次),查看网页返回信息及控制台消息:

根据控制台打印消息可知,执行findById()方法正确查询出了用户评论信息Comment,重复进行同样的查询操作,数据库也不会重复执行SQL语句,这表明定制的Redis缓存生效了。

使用Redis客户端可视化管理工具Redis Desktop Manager查看缓存数据:

可以看到用户评论信息Comment正确存储到了Redis缓存库中,且缓存到Redis服务的数据已经使用了JSON格式的数据存储展示,这说明自定义的基于注解的Redis缓存管理器RedisCacheManager生效了。

SpringBoot缓存管理(三) 自定义Redis缓存序列化机制的更多相关文章

  1. SpringBoot缓存篇Ⅱ --- 整合Redis以及序列化机制

    一.Redis环境搭建 系统默认是使用ConcurrentMapCacheManager,然后获取和创建ConcurrentMapCache类型的缓存组件,再将数据保存在ConcurrentMap中 ...

  2. Spring Boot自定义Redis缓存配置,保存value格式JSON字符串

    Spring Boot自定义Redis缓存,保存格式JSON字符串 部分内容转自 https://blog.csdn.net/caojidasabi/article/details/83059642 ...

  3. 使用本地缓存快还是使用redis缓存好?

    使用本地缓存快还是使用redis缓存好? Redis早已家喻户晓,其性能自不必多说. 但是总有些时候,我们想把性能再提升一点,想着redis是个远程服务,性能也许不够,于是想用本地缓存试试!想法是不错 ...

  4. SpringBoot 集成Shiro之使用Redis缓存授权认证信息

    因为用户认证与授权需要从数据库中查询并验证信息,但是对于权限很少改变的情况,这样不断从数据库中查询角色验证权限,对整个系统的开销很大,对数据库压力也随之增大.因此可以将用户认证和授权信息都缓存起来,第 ...

  5. Spring自定义缓存管理及配置Ehcache缓存

    spring自带缓存.自建缓存管理器等都可解决项目部分性能问题.结合Ehcache后性能更优,使用也比较简单. 在进行Ehcache学习之前,最好对Spring自带的缓存管理有一个总体的认识. 这篇文 ...

  6. 实例讲解Springboot以Template方式整合Redis及序列化问题

    1 简介 之前讲过如何通过Docker安装Redis,也讲了Springboot以Repository方式整合Redis,建议阅读后再看本文效果更佳: (1) Docker安装Redis并介绍漂亮的可 ...

  7. 【完结】利用 Composer 完善自己的 PHP 框架(三)——Redis 缓存

    本教程示例代码见 https://github.com/johnlui/My-First-Framework-based-on-Composer 回顾 上两篇文章中我们完成了 View 视图加载类和 ...

  8. SpringBoot整合NoSql--(三)Redis集群

    (1)集群原理 在Redis集群中,所有的Redis节点彼此互联,节点内部使用二进制协议优化传输速度和带宽. 当一个节点挂掉后,集群中超过半数的节点检测失效时才认为该节点已失效.不同于Tomcat集群 ...

  9. Redis系列(三):Redis的持久化机制(RDB、AOF)

    本篇博客是Redis系列的第3篇,主要讲解下Redis的2种持久化机制:RDB和AOF. 本系列的前2篇可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. Redis系列(二): ...

随机推荐

  1. NVIDIA CUDA-X AI

    NVIDIA CUDA-X AI 面向数据科学和 AI 的 NVIDIA GPU 加速库 数据科学是推动 AI 发展的关键力量之一,而 AI 能够改变各行各业. 但是,驾驭 AI 的力量是一个复杂挑战 ...

  2. 适用于CUDA GPU的Numba例子

    适用于CUDA GPU的Numba例子 矩阵乘法 这是使用CUDA内核的矩阵乘法的简单实现: @cuda.jit def matmul(A, B, C): """Perf ...

  3. 【SQLite】SQLite文件突然变大怎么办?瘦身办法

    使用VACUUM命令即可: VACUUM 命令通过复制主数据库中的内容到一个临时数据库文件,然后清空主数据库,并从副本中重新载入原始的数据库文件.这消除了空闲页,把表中的数据排列为连续的,另外会清理数 ...

  4. [Azure DevOps] 编译时自动修改版本号

    1. 需求 在使用 Pipeline 自动化 CI/CD 流程的过程中,我还还需要自动修改程序集的版本号.这个功能 EdiWang 和LeoLaw 都写文章讲解过做法.不过我的项目基本都是 .Net ...

  5. SpringCloud Alibaba实战(6:nacos-server服务搭建)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 大家好,我是三分恶. 这一节我们来学习SpringCloud Alibaba体系中一 ...

  6. Reactor3 中文文档(用户手册)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  7. NOIP模拟测试29「爬山·学数数·七十和十七」

    爬山题解不想写了 学数数 离散化然后找到以每一个值为最大值的连续子段有多少个,然后开个桶维护 那么怎么找以每一个值为最大值的连续子段个数 方法1(我的极笨的方法) 考试时我的丑陋思路, 定义极左值为左 ...

  8. 重新整理 .net core 实践篇—————工作单元模式[二十六]

    前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...

  9. 关于MySql数据库误操作数据找回的办法

    先讲个事,前段时间,系统长时间不用的一个功能被开放出来了,想当然的我没有在测试平台上测试,直接操作了正式系统(的确是我不严谨),导致好多数据异常,页面展示错乱了.于是我想到的第一个就是进行备份还原.项 ...

  10. Docker笔记--ubuntu安装docker

    Docker笔记--ubuntu安装docker 1.更换国内软件源,推荐中国科技大学的源,稳定速度快(可选) sudo cp /etc/apt/sources.list /etc/apt/sourc ...