• spring缓存的使用


    Spring缓存使用

    缓存注解

    对于Spring,缓存组件例如EhCache是可拔插的,而缓存注解是通用的。

    @Cacheable

    标记在方法或者类上,标识该方法或类支持缓存。Spring调用注解标识方法后会将返回值缓存到redis,以保证下次同条件调用该方法时直接从缓存中获取返回值。这样就不需要再重新执行该方法的业务处理过程,提高效率。
    @Cacheable常用的三个参数如下:
    cacheNames 缓存名称
    key 缓存的key,需要注意key的写法哈
    condition 缓存执行的条件,返回true时候执行
    cacheManager 使用的cacheManager的bean名称
    sync 如果多个请求同时来访问同一个key的数据,则sync表示加锁同步,等第一个请求返回数据后,其他请求直接获取缓存里的数据。

    @Cacheable(value = "DICT", key = "'getDictById'+#id", sync = true)
    
    • 1
    @CachePut

    标记在方法或者类上,标识该方法或类支持缓存。每次都会执行目标方法,并将执行结果以键值对的形式存入指定的缓存中。

    @CachePut(value = "DICT", key = "'getDictById'+#model.getId()")
    
    • 1
    @CacheEvict

    @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即
    value表示清除操作是发生在哪些Cache上的(对应Cache的名称);
    key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;
    condition表示清除操作发生的条件。
    allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。
    beforeInvocation 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

    @CacheEvict(value = "DICT",key = "'getDictById'+#model.getId()",beforeInvocation=false)
    
    • 1

    使用Ehcache作为缓存

    pom坐标
      <dependency>
       <groupId>net.sf.ehcachegroupId>
       <artifactId>ehcacheartifactId>
       <version>2.8.3version>
      dependency>
       
       <dependency>
           <groupId>org.springframeworkgroupId>
           <artifactId>spring-context-supportartifactId>
           <version>5.1.5.RELEASEversion>
       dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    ehcache.xml 配置文件
    
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
        <diskStore path="java.io.tmpdir/Tmp_EhCache" />
        
        
        
        
        
        
        
        
        
        
        <defaultCache
            eternal="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />
     
      
      
        <cache
            name="local"  
            eternal="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />    
    ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    EhcacheConfiguration.java
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.ehcache.EhCacheCacheManager;
    import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ClassPathResource;
     
    @Configuration
    @EnableCaching
    public class EhcacheConfiguration {
     @Bean
     public EhCacheManagerFactoryBean cacheManagerFactoryBean(){
      EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
      bean.setConfigLocation(new ClassPathResource("ehcache.xml"));
      bean.setShared(true);
      return bean;
     }
     @Bean
        public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){
          return new EhCacheCacheManager(bean.getObject());
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    使用
    @Service
    public class EhcahceServiceImpl implements EhcahceService {
     
     private static final String CACHE_STRATEGY = "local";
     
     @CachePut(value=CACHE_STRATEGY,key="#root.methodName+#info.getProduct_id()")
     @Override
     public ProductInfo saveProductInfo(ProductInfo info) throws Exception {
      return info;
     }
    
     @Cacheable(value=CACHE_STRATEGY,key="#root.methodName+#id")
     @Override
     public ProductInfo getProductInfoById(Long id) throws Exception {
      return null;
     }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用redis作为缓存

    pom坐标
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redisartifactId>
                <exclusions>
                    <exclusion>
                        <groupId>io.lettucegroupId>
                        <artifactId>lettuce-coreartifactId>
                    exclusion>
                exclusions>
            dependency>
            <dependency>
                <groupId>redis.clientsgroupId>
                <artifactId>jedisartifactId>
            dependency>
            <dependency>
                <groupId>org.apache.commonsgroupId>
                <artifactId>commons-pool2artifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    yml配置
    spring:
      # redis配置
      redis:
        password: 123456
        # 集群模式
        cluster:
          nodes: 127.0.0.1:1000,127.0.0.1:2000
        # 哨兵模式
        #sentinel:
          #master: sentinel #哨兵的名字 #下面是所有哨兵集群节点
          #nodes: 11.11.11.111:26379,11.11.11.111:26380
        jedis:
          pool:
            #最大连接数
            max-active: 200
            #最大阻塞等待时间(负数表示没限制)
            max-wait: -1
            #最大空闲
            max-idle: 8
            #最小空闲
            min-idle: 0
            #连接超时时间
        expireSecond: 604800 #7天
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    redisCacheManage配置

    此处设计了两种不同cacheName的redis缓存

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    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.cache.RedisCacheWriter;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.time.Duration;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    @EnableCaching
    @Slf4j
    public class CacheConfig {
        @Value("${spring.redis.expireSecond}")
        private Integer expireSecond;
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            return new RedisCacheManager(
                    RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                    this.getRedisCacheConfigurationWithTtl(600), // 默认策略,未配置的 key 会使用这个
                    this.getRedisCacheConfigurationMap() // 指定 key 策略
            );
        }
    
        private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
            Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
            redisCacheConfigurationMap.put("Dict", this.getRedisCacheConfigurationWithTtl(600));
            redisCacheConfigurationMap.put("COL_KEY", this.getRedisCacheConfigurationWithTtl(expireSecond));
    
            return redisCacheConfigurationMap;
        }
    
        private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
            RedisCacheConfiguration config = RedisCacheConfiguration
                    .defaultCacheConfig()
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                    .entryTtl(Duration.ofSeconds(seconds));
    
            return config;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    使用同上

    同时使用两种缓存

    配置
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.ehcache.EhCacheCacheManager;
    import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.ClassPathResource;
    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.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.time.Duration;
    
    @Configuration
    @EnableCaching
    @Slf4j
    public class CacheConfig {
        @Value("${spring.redis.expireSecond}")
        private Integer expireSecond;
    
        @Bean
        public EhCacheManagerFactoryBean cacheManagerFactoryBean() {
            EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
            bean.setConfigLocation(new ClassPathResource("ehcache.xml"));
            bean.setShared(true);
            return bean;
        }
    
        @Bean
        @Primary
        public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean) {
            return new EhCacheCacheManager(bean.getObject());
        }
    
        @Bean
        public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
            // 使用缓存的默认配置
            RedisCacheConfiguration config = RedisCacheConfiguration
                    .defaultCacheConfig()
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                    .entryTtl(Duration.ofSeconds(expireSecond));
            RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config);
            return builder.build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    指定缓存使用
        @Cacheable(value = CACHE_NAME, key = "#root.methodName+#code",cacheManager = "redisCacheManager")
    
    • 1

    当redis出现问题时,如何禁用redis,直连数据库

    1.yml配置文件忽略掉redis配置 
      spring.autoconfigure.exclude:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
    2.根据条件加载CacheConfig类,当spring.redis.useRedis=true时加载,否则不加载
          @ConditionalOnProperty(prefix = "spring.redis", value = "useRedis", havingValue = "true", matchIfMissing = true)
    public class CacheConfig{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    Redis工具类
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnection;
    import org.springframework.data.redis.core.RedisConnectionUtils;
    import org.springframework.stereotype.Service;
    import redis.clients.jedis.Jedis;
    
    import javax.annotation.Resource;
    
    @Service
    @ConditionalOnProperty(prefix = "spring.redis", value = "useRedis", havingValue = "true", matchIfMissing = true)
    public class RedisService {
    
        @Resource
        private RedisConnectionFactory redisConnectionFactory;
        //会出现循环依赖---Circular reference
        //RedisService引用JedisPool--JedisPool在RedisService,只有创建RedisService的实例才可以获取JedisPool的bean
        //所以需要单独拿出JedisPool的bean
    
        /**
         * 获取单个对象
         *
         * @param prefix
         * @param key
         * @param data
         * @return
         */
        public <T> T get(KeyPrefix prefix, String key, Class<T> data) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                //生成真正的key  className+":"+prefix;  BasePrefix:id1
                String realKey = prefix.getPrefix() + key;
                String sval = jedis.get(realKey);
                //将String转换为Bean入后传出
                T t = stringToBean(sval, data);
                return t;
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 移除对象,删除
         *
         * @param prefix
         * @param key
         * @return
         */
        public boolean delete(KeyPrefix prefix, String key) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                long ret = jedis.del(realKey);
                return ret > 0;//删除成功,返回大于0
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 设置单个、多个对象
         *
         * @param prefix
         * @param key
         * @param value
         * @return
         */
        public <T> boolean set(KeyPrefix prefix, String key, T value) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                String s = beanToString(value);
                if (s == null || s.length() <= 0) {
                    return false;
                }
                int seconds = prefix.getExpireSeconds();
                if (seconds <= 0) { //永不过期
                    jedis.set(realKey, s);
                } else {
                    jedis.setex(realKey, seconds, s);
                }
                return true;
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 减少值
         *
         * @param prefix
         * @param key
         * @return
         */
        public <T> Long decr(KeyPrefix prefix, String key) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                return jedis.decr(realKey);
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 增加值
         *
         * @param prefix
         * @param key
         * @return
         */
        public <T> Long incr(KeyPrefix prefix, String key) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                return jedis.incr(realKey);
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 获取key的过期时间
         */
        public <T> Long ttl(KeyPrefix prefix, String key) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                return jedis.ttl(realKey);
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 设置key的过期时间
         */
        public <T> void setExpire(KeyPrefix prefix, String key, int expire) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                jedis.expire(realKey, expire);
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 检查key是否存在
         *
         * @param prefix
         * @param key
         * @return
         */
        public <T> boolean exitsKey(KeyPrefix prefix, String key) {
            JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true);
            Jedis jedis = null;
            try {
                jedis = jedisConnection.getNativeConnection();
                String realKey = prefix.getPrefix() + key;
                return jedis.exists(realKey);
            } finally {
                returnToPool(jedisConnection);
            }
        }
    
        /**
         * 将字符串转换为Bean对象
         * 

    * parseInt()返回的是基本类型int 而valueOf()返回的是包装类Integer * Integer是可以使用对象方法的 而int类型就不能和Object类型进行互相转换 。 * int a=Integer.parseInt(s); * Integer b=Integer.valueOf(s); */ public static <T> T stringToBean(String s, Class<T> clazz) { if (s == null || s.length() == 0 || clazz == null) { return null; } if (clazz == int.class || clazz == Integer.class) { return ((T)Integer.valueOf(s)); } else if (clazz == String.class) { return (T)s; } else if (clazz == long.class || clazz == Long.class) { return (T)Long.valueOf(s); } else { JSONObject json = JSON.parseObject(s); return JSON.toJavaObject(json, clazz); } } /** * 将Bean对象转换为字符串类型 * * @param */ public static <T> String beanToString(T value) { //如果是null if (value == null) { return null; } //如果不是null Class<?> clazz = value.getClass(); if (clazz == int.class || clazz == Integer.class) { return "" + value; } else if (clazz == String.class) { return "" + value; } else if (clazz == long.class || clazz == Long.class) { return "" + value; } else { return JSON.toJSONString(value); } } private void returnToPool(JedisConnection jedisConnection) { if (jedisConnection != null) { RedisConnectionUtils.releaseConnection(jedisConnection, redisConnectionFactory); } } public <T> boolean set(String key, T value) { JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true); Jedis jedis = null; try { jedis = jedisConnection.getNativeConnection(); //将T类型转换为String类型 String s = beanToString(value); if (s == null) { return false; } jedis.set(key, s); return true; } finally { returnToPool(jedisConnection); } } public <T> T get(String key, Class<T> data) { JedisConnection jedisConnection = (JedisConnection)RedisConnectionUtils.getConnection(redisConnectionFactory, true); Jedis jedis = null; try { jedis = jedisConnection.getNativeConnection(); String sval = jedis.get(key); //将String转换为Bean入后传出 T t = stringToBean(sval, data); return t; } finally { returnToPool(jedisConnection); } } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
  • 相关阅读:
    CH395实现主动ping对端功能(代码及说明)
    力扣371周赛
    SpringBoot项目--电脑商城【增加/减少购物车商品数量】
    【随笔记】C++ condition_variable 陷阱
    BootLoader-UDS刷写流程
    Canvas相关概念集合
    【单片机】13-实时时钟DS1302
    Typora导出的PDF目录标题自动加编号
    【微服务37】分布式事务Seata源码解析五:@GlobalTransactional如何开启全局事务【云原生】
    学习ftp
  • 原文地址:https://blog.csdn.net/growing1224/article/details/136221976