Redis【实践篇】之RedisTemplate基本操作

news2024/11/28 2:40:12

Redis 从入门到精通【应用篇】之RedisTemplate详解

文章目录

  • Redis 从入门到精通【应用篇】之RedisTemplate详解
  • 0. 前言
  • 1. RedisTemplate 方法
    • 1. 设置RedisTemplate的序列化方式
    • 2. RedisTemplate的基本操作
  • 2. 源码浅析
    • 2.1. 构造方法
    • 2.2. 序列化方式
    • 2.3. RedisTemplate的操作方法
    • 2.4. RedisTemplate的事务
    • 2.5. RedisTemplate的执行方法
    • 2.6. RedisTemplate的回调方法
      • 代码示例
  • 3.总结
    • 3.1. 项目中如何使用 RedisTemplate 支持多个 Redis 数据库?
    • 3.2. 如何使用 RedisTemplate 支持 Redis 集群?
    • 3.3. 如何使用 RedisTemplate 实现 Redis 事务的乐观锁?
    • 3.4. 如何使用 RedisTemplate 实现 Redis 的分布式锁重入?
    • 3.5. 如何使用 RedisTemplate 实现 Redis 的分布式事务?
    • 3.6. 如何使用 RedisTemplate 实现 Redis 的分布式限速?
    • 3.7. 如何使用 RedisTemplate 实现 Redis 的分布式锁可重入性?
    • 3.8. 如何使用 RedisTemplate 实现 Redis 的分布式信号量?
    • 3.9. 如何使用 RedisTemplate 实现 Redis 的分布式缓存穿透?
    • 3.10. 如何使用 RedisTemplate 实现 Redis 的分布式缓存击穿?
  • 4. Redis从入门到精通系列文章

在这里插入图片描述

0. 前言

在SpringBoot中,可以使用RedisTemplate来操作Redis数据库。RedisTemplate是Spring Data Redis提供的一个强大的Redis客户端,它支持各种Redis数据结构,并提供了许多方便的方法来操作这些数据结构。下面是一些RedisTemplate的用法示例:

1. RedisTemplate 方法

1. 设置RedisTemplate的序列化方式

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 设置key和value的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));

        // 设置hash key和value的序列化方式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));

        return redisTemplate;
    }
}

在此示例中,创建了一个RedisTemplate对象,并设置了key和value的序列化方式为StringRedisSerializer和Jackson2JsonRedisSerializer。同时,还设置了hash key和value的序列化方式。

2. RedisTemplate的基本操作

其实在项目中我们通常会将RedisTemplate 再封装一层,作为一个Redis操作类处理,相当于提供了一层语法糖。

@Service
public class RedisService {

    // RedisTemplate是Spring提供的对Redis的操作模板类,使用泛型限定key和value的类型为String和Object
    private final RedisTemplate<String, Object> redisTemplate;

    // 构造函数,注入RedisTemplate对象
    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 设置key-value键值对
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    // 根据key获取value
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    // 根据key删除键值对
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    // 判断key是否存在
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    // 将key对应的value增加delta
    public long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    // 获取hash结构中所有键值对
    public Map<Object, Object> hashGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // 向hash结构中添加键值对
    public void hashPut(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    // 根据hash结构中的key获取对应的value
    public Object hashGet(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    // 根据hash结构中的key删除对应的hashKey
    public void hashDelete(String key, Object... hashKeys) {
        redisTemplate.opsForHash().delete(key, hashKeys);
    }

    // 判断hash结构中是否存在hashKey
    public boolean hashExists(String key, Object hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    // 获取set结构中所有元素
    public Set<Object> setGetAll(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // 向set结构中添加元素
    public void setAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    // 判断set结构中是否存在某个元素
    public boolean setExists(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    // 根据value删除set结构中的元素
    public void setDelete(String key, Object... values) {
        redisTemplate.opsForSet().remove(key, values);
    }

    // 获取list结构中所有元素
    public List<Object> listGetAll(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    // 向list结构中左侧插入元素
    public void listPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    // 从list结构中左侧弹出元素
    public Object listPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    // 获取list结构中元素的数量
    public long listSize(String key) {
        return redisTemplate.opsForList().size(key);
    }
}

2. 源码浅析

2.1. 构造方法

RedisTemplate的构造方法需要一个RedisConnectionFactory对象作为参数,它通过这个对象来获取Redis连接。RedisConnectionFactory 是 Spring Data Redis 提供的一个接口,用于创建和管理 Redis 连接。它是将 Redis 连接池(连接 Redis 数据库的客户端)与 Spring 应用程序集成的关键。

public RedisTemplate() {
    RedisConnectionFactory redisConnectionFactory = RedisConnectionConfiguration.determineConnectionFactory(redisSentinelConfiguration, redisClusterConfiguration, connectionFactory, jedisConnectionFactory);
    setConnectionFactory(redisConnectionFactory);
    afterPropertiesSet();
}

2.2. 序列化方式

RedisTemplate支持各种数据类型的序列化和反序列化,它提供了以下四种序列化方式:

  • keySerializer:key的序列化方式。
  • valueSerializer:value的序列化方式。
  • hashKeySerializer:hash key的序列化方式。
  • hashValueSerializer:hash value的序列化方式。

RedisTemplate默认使用JdkSerializationRedisSerializer作为序列化方式,但是在实际使用中,通常需要根据实际情况选择更加高效的序列化方式,如StringRedisSerializer、Jackson2JsonRedisSerializer等。

public void setKeySerializer(RedisSerializer<?> keySerializer) {
    Assert.notNull(keySerializer, "RedisSerializer must not be null!");
    this.keySerializer = keySerializer;
}

public void setValueSerializer(RedisSerializer<?> valueSerializer) {
    Assert.notNull(valueSerializer, "RedisSerializer must not be null!");
    this.valueSerializer = valueSerializer;
}

public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
    Assert.notNull(hashKeySerializer, "RedisSerializer must not be null!");
    this.hashKeySerializer = hashKeySerializer;
}

public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
    Assert.notNull(hashValueSerializer, "RedisSerializer must not be null!");
    this.hashValueSerializer = hashValueSerializer;
}

2.3. RedisTemplate的操作方法

RedisTemplate提供了各种操作方法,如opsForValue()、opsForList()、opsForSet()、opsForZSet()、opsForHash()等,它们返回的是具体数据结构的操作对象,如ValueOperations、ListOperations、SetOperations、ZSetOperations、HashOperations等。这些操作对象提供了各种操作方法,如get()、set()、push()、pop()、add()、remove()、score()、range()、increment()等,它们对应了Redis的各种操作。

public ValueOperations<K, V> opsForValue() {
    if (valueOps == null) {
        valueOps = new DefaultValueOperations<>(this);
    }
    return valueOps;
}

public ListOperations<K, V> opsForList() {
    if (listOps == null) {
        listOps = new DefaultListOperations<>(this);
    }
    return listOps;
}

public SetOperations<K, V> opsForSet() {
    if (setOps == null) {
        setOps = new DefaultSetOperations<>(this);
    }
    return setOps;
}

public ZSetOperations<K, V> opsForZSet() {
    if (zSetOps == null) {
        zSetOps = new DefaultZSetOperations<>(this);
    }
    return zSetOps;
}

public HashOperations<K, HK, HV> opsForHash() {
    if (hashOps == null) {
        hashOps = new DefaultHashOperations<>(this);
    }
    return hashOps;
}

2.4. RedisTemplate的事务

RedisTemplate支持事务,它提供了multi()、exec()和discard()三个方法来实现事务。multi()方法用于开启事务,exec()方法用于提交事务,discard()方法用于回滚事务。

public void multi() {
    RedisConnectionUtils.bindConnection(getRequiredConnectionFactory(), true);
    try {
        RedisConnectionUtils.getRequiredConnection(getConnectionFactory()).multi();
    } catch (RuntimeException ex) {
        RedisConnectionUtils.unbindConnection(getRequiredConnectionFactory());
        throw ex;
    }
}

public List<Object> exec() {
    RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
    return execute((RedisCallback<List<Object>>) connection -> {
        List<Object> results = connection.exec();
        return results != null ? results : Collections.emptyList();
    }, true);
}

public void discard() {
    RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
    execute(RedisConnectionUtils::discard, true);
}

2.5. RedisTemplate的执行方法

RedisTemplate提供了execute()方法来执行Redis操作,它需要传入一个RedisCallback对象作为参数,RedisCallback是一个函数式接口,它定义了一个回调函数,用于执行具体的Redis操作。execute()方法会获取一个Redis连接,执行RedisCallback对象的回调函数,并返回回调函数的结果。

public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
    Assert.notNull(action, "Callback object must not be null");

    RedisConnection conn = null;
    try {
        conn = getConnection(exposeConnection);
        boolean existingConnection = TransactionSynchronizationManager.hasResource(getConnectionFactory());
        RedisConnection connToUse = preProcessConnection(conn, existingConnection);
        T result = action.doInRedis(connToUse);
        return postProcessResult(result, conn, existingConnection);
    } catch (RuntimeException ex) {
        releaseConnection(conn, existingConnection);
        throw ex;
    } finally {
        if (!exposeConnection) {
            RedisConnectionUtils.releaseConnection(conn, getConnectionFactory(), false);
        }
    }
}

在execute()方法中,首先获取Redis连接,然后调用preProcessConnection()方法进行预处理,接着执行RedisCallback对象的回调函数,最后调用postProcessResult()方法进行后处理,并返回结果。如果执行过程中发生异常,会调用releaseConnection()方法释放Redis连接。

2.6. RedisTemplate的回调方法

RedisTemplate的回调方法主要有以下三个:

这里有三个方法:

  1. execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline):执行Redis操作的核心方法。接受一个RedisCallback对象,该对象封装了要在Redis上执行的操作。还有两个布尔类型的参数,一个表示是否暴露连接(exposeConnection),另一个表示是否启用pipeline(pipeline)。如果启用pipeline,将使用Redis连接对象的openPipeline()和closePipeline()方法执行操作。

  2. execute(SessionCallback<T> session):执行Redis事务的方法。接受一个SessionCallback对象,该对象封装了在 Redis 事务中执行的操作。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,最后解除绑定。

  3. executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer):执行 Redis pipeline 的方法。接受一个SessionCallback对象,该对象封装了要在 Redis pipeline 中执行的操作,以及一个可选的 RedisSerializer 对象,用于反序列化结果。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,在执行期间使用 Redis 连接对象的 openPipeline() 和 closePipeline() 方法开启和关闭 Redis pipeline。

常用场景:第一个方法执行单个Redis操作,第二个方法执行Redis事务,第三个方法执行Redis
pipeline。此外,第二个方法是为了执行多个 Redis 操作而设计的,而第一个方法和第三个方法只执行单个 Redis 操作。第三个方法需要额外的参数,用于反序列化 Redis pipeline 的结果。


    @Nullable
    public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(action, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnection conn = RedisConnectionUtils.getConnection(factory, this.enableTransactionSupport);

        Object var11;
        try {
            boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
            RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
            boolean pipelineStatus = connToUse.isPipelined();
            if (pipeline && !pipelineStatus) {
                connToUse.openPipeline();
            }

            RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
            T result = action.doInRedis(connToExpose);
            if (pipeline && !pipelineStatus) {
                connToUse.closePipeline();
            }

            var11 = this.postProcessResult(result, connToUse, existingConnection);
        } finally {
            RedisConnectionUtils.releaseConnection(conn, factory, this.enableTransactionSupport);
        }

        return var11;
    }

    public <T> T execute(SessionCallback<T> session) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        Object var3;
        try {
            var3 = session.execute(this);
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var3;
    }

    public List<Object> executePipelined(SessionCallback<?> session) {
        return this.executePipelined(session, this.valueSerializer);
    }

    public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        List var4;
        try {
            var4 = (List)this.execute((connection) -> {
                connection.openPipeline();
                boolean pipelinedClosed = false;

                List var7;
                try {
                    Object result = this.executeSession(session);
                    if (result != null) {
                        throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
                    }

                    List<Object> closePipeline = connection.closePipeline();
                    pipelinedClosed = true;
                    var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
                } finally {
                    if (!pipelinedClosed) {
                        connection.closePipeline();
                    }

                }

                return var7;
            });
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var4;
    }

代码示例

  1. 使用 execute(RedisCallback<T> action) 执行 Redis 命令
 
// 使用 execute() 方法执行 Redis 命令
String key = "myKey";
String value = redisTemplate.execute((RedisCallback<String>) connection -> {
    connection.set(redisTemplate.getStringSerializer().serialize(key), redisTemplate.getStringSerializer().serialize("Hello"));
    return redisTemplate.getStringSerializer().deserialize(connection.get(redisTemplate.getStringSerializer().serialize(key)));
});

// 输出 Redis 命令执行结果
System.out.println(value); // 输出 "Hello"

在上面的示例中, 我们使用 execute(RedisCallback<T> action) 方法将 Redis 命令封装在一个 RedisCallback 对象中,并将其传递给 execute() 方法。该命令使用 set() 方法将一个 key-value 对写入 Redis 中,然后使用 get() 方法从 Redis 中读取该 key 对应的值。

  1. 使用 execute(SessionCallback<T> session) 执行 Redis 事务
// 使用 execute(SessionCallback<T> session) 方法执行 Redis 事务
String key1 = "myKey1";
String key2 = "myKey2";
String value1 = "myValue1";
String value2 = "myValue2";
List<Object> results = redisTemplate.execute((SessionCallback<List<Object>>) session -> {
    session.multi();
    session.opsForValue().set(key1, value1);
    session.opsForValue().set(key2, value2);
    return session.exec();
});

// 输出 Redis 事务的结果
System.out.println(results); // 输出 "[true, true]"

使用 execute(SessionCallback<T> session) 方法将 Redis 事务封装在一个 SessionCallback<T> 对象中,并将其传递给 execute() 方法。该事务使用 multi() 方法开启事务,在事务中使用 opsForValue() 对象的 set() 方法将两个 key-value 对写入 Redis 中,最后使用 exec() 方法提交事务。事务执行完成后,我们可以通过 execute() 方法返回的结果列表查看每个 Redis 命令的执行结果。在上面的示例中,我们可以看到结果列表为 [true, true],表示两个 Redis 命令都成功执行。

  1. 使用 executePipelined(SessionCallback<?> session) 执行 Redis pipeline
 

// 使用 executePipelined(SessionCallback<?> session) 方法执行 Redis pipeline
String key1 = "myKey1";
String key2 = "myKey2";
List<Object> results = redisTemplate.executePipelined((SessionCallback<List<Object>>) session -> {
    session.opsForValue().get(key1);
    session.opsForValue().get(key2);
    return null;
});

// 输出 Redis pipeline 的结果
System.out.println(results); // 输出 "[Hello1, Hello2]"

使用 executePipelined(SessionCallback<?> session) 方法将 Redis pipeline 封装在一个 SessionCallback<?> 对象中,并将其传递给 executePipelined() 方法。该 pipeline 使用 opsForValue() 对象的 get() 方法获取两个 key 的值,并返回一个结果列表。在执行期间,该方法将使用 Redis 连接对象的 openPipeline()closePipeline() 方法开启和关闭 Redis pipeline,以便可以批量执行多个命令。

3.总结

看完这些基本上行只是学会了增删查看和批量操作。也就是只学会了RedisTemplate 的皮毛。其实在项目中还有更多的复杂需求,需要重新实现。

比如以下这些问题,也是很常见的,需要我们处理实际的问题。我大概做一个简答,后面将详细输出示例

3.1. 项目中如何使用 RedisTemplate 支持多个 Redis 数据库?

可以通过配置 RedisConnectionFactory 来支持多个 Redis 数据库,其中可以使用 JedisConnectionFactory 或 LettuceConnectionFactory 来创建不同的 RedisConnectionFactory 实例。详细教程可以参考我的其他博客《SpringBoot 项目配置多数据源》

3.2. 如何使用 RedisTemplate 支持 Redis 集群?

可以使用 RedisTemplate 的 ClusterRedisConnectionFactory 来支持 Redis 集群,通过配置 Redis 集群中的多个节点来实现高可用性和负载均衡。详细教程参考我的其他博客《SpringBoot 项目配置 Redis 集群》

3.3. 如何使用 RedisTemplate 实现 Redis 事务的乐观锁?

可以使用 RedisTemplate 的 watch() 方法和 multi() 方法来实现 Redis 事务的乐观锁,通过在事务开始前使用 watch() 方法监视 Redis 中的某个 key,然后在事务中使用 multi() 方法执行多个 Redis 命令,并使用 exec() 方法提交事务,如果在事务执行期间,被监视的 key 被修改,则事务会失败,从而实现乐观锁。
详细教程可以参考我的其他博客《SpringBoot 项目配置 Redis 集群》

3.4. 如何使用 RedisTemplate 实现 Redis 的分布式锁重入?

可以使用 RedisTemplate 的 ThreadLocal 方式来实现 Redis 的分布式锁重入,即在每个线程中保存一个 Redis 分布式锁的状态,并在需要重入时,检查当前线程是否已经获取了分布式锁。

3.5. 如何使用 RedisTemplate 实现 Redis 的分布式事务?

可以使用 RedisTemplate 的 execute(SessionCallback session) 方法来实现 Redis 的分布式事务,其中 SessionCallback 接口可以用来执行多个 Redis 命令,并保证这些命令以原子方式执行。

3.6. 如何使用 RedisTemplate 实现 Redis 的分布式限速?

可以使用 RedisTemplate 的 incr() 方法和 expire() 方法来实现 Redis 的分布式限速,通过在 Redis 中设置一个计数器和过期时间来实现分布式限速。

3.7. 如何使用 RedisTemplate 实现 Redis 的分布式锁可重入性?

可以使用 RedisTemplate 的 ReentrantRedisLock 类来实现 Redis 的分布式锁可重入性,该类可以在 Redis 中保存一个计数器来记录锁的重入次数,并在释放锁时,检查当前线程是否已经完全释放了锁,从而实现分布式锁的可重入性。

3.8. 如何使用 RedisTemplate 实现 Redis 的分布式信号量?

可以使用 RedisTemplate 的 RedisSemaphore 类来实现 Redis 的分布式信号量,该类可以在 Redis 中保存一个计数器来记录当前已经获得信号量的数量,并在释放信号量时,将计数器减一。

3.9. 如何使用 RedisTemplate 实现 Redis 的分布式缓存穿透?

可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和布隆过滤器来实现 Redis 的分布式缓存穿透,其中布隆过滤器可以用来过滤掉不存在的 key,从而避免缓存穿透的问题。

3.10. 如何使用 RedisTemplate 实现 Redis 的分布式缓存击穿?

可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和 Redis 分布式锁来实现 Redis 的分布式缓存击穿,其中 Redis 分布式锁可以用来防止缓存击穿,即在缓存不存在的情况下,使用 Redis 分布式锁来避免多个线程同时访问数据库。

4. Redis从入门到精通系列文章

  • 《Redis【应用篇】之RedisTemplate基本操作》
  • 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
  • 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
  • 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
  • 《Redis 从入门到精通【进阶篇】之Redis事务详解》
  • 《Redis从入门到精通【进阶篇】之对象机制详解》
  • 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
  • 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
  • 《Redis从入门到精通【进阶篇】之持久化RDB详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
  • 《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
    在这里插入图片描述大家好,我是冰点,今天的Redis【实践篇】之RedisTemplate基本操作详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回归预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现TCN-LSTM时间卷积神经网络结合长…

对C语言指针的理解

&是取地址&#xff0c;描述当前变量的内存位置 *是提取&#xff0c;描述指针变量所代表的变量的值&#xff1b;设计指针是为了节省内存空间&#xff0c;指针变量的巧妙使用可以减少内存中相同变量值的重复存储 多级指针中&#xff0c;一级指针指向普通变量的地址&#xff0…

AJAX-day03-AJAX进阶

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 同步代码和异步代码 回调函数地狱 Promise - 链式调用 Promise 链式应用 async函数和await async函…

操作系统笔记、面试八股(三)—— 系统调用与内存管理

文章目录 3. 系统调用3.1 用户态与内核态3.2 系统调用分类3.3 如何从用户态切换到内核态&#xff08;系统调用举例&#xff09; 4. 内存管理4.1 内存管理是做什么的4.1.1 为什么需要虚拟地址空间4.1.2 使用虚拟地址访问内存有什么优势 4.2 常见的内存管理机制4.3 分页管理4.3.1…

Sentinel规则持久化到nacos的实现(源码修改)

文章目录 1、Sentinel源码修改2、持久化效果测试 Sentinel规则管理有三种模式&#xff1a; 原始模式pull模式push模式 这是实现push方式&#xff1a; push模式即控制台将配置规则推送到远程配置中心&#xff0c;例如Nacos。Sentinel客户端去监听Nacos&#xff0c;获取配置变更…

通过URL对象实现简单爬虫功能

目录 一、URL类 1. URL类基本概念 2. 构造器 3. 常用方法 二、爬虫实例 1. 爬取网络图片&#xff08;简易&#xff09; 2. 爬取网页源代码 3. 爬取网站所有图片 一、URL类 1. URL类基本概念 URL&#xff1a;Uniform Resource Locator 统一资源定位符 表示统一资源定位…

Hadoop——Hive相关问题汇总

(1) 连接数据库时SSL问题 解决方法&#xff1a;useSSLfalse要放最后 (2) jdbc:hive2://localhost:10091: Failed to open new session: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.authorize.AuthorizationException): Us…

银河麒麟服务器v10 sp1 nginx 部署项目

上一篇&#xff1a;银河麒麟服务器v10 sp1 nginx开机自动启动_csdn_aspnet的博客-CSDN博客 由于项目为前后端分离&#xff0c;前端项目使用nginx部署&#xff0c;VUE项目打包后上传至银河麒麟服务器&#xff1a; 8063 为前端项目文件目录&#xff0c;修改配置 &#xff0c;默认…

计算机基础专升本笔记四 计算机系统

计算机基础专升本笔记四 计算机系统 计算机系统 计算机系统由计算机硬件系统和计算机软件系统 组成。且是按照存储程序的方式工作的。计算机硬件就是由各种电子器件按照一定逻辑连接而成&#xff0c;看的见摸得着&#xff0c;是计算机系统的物质基础&#xff0c;计算机软件系统…

港科夜闻|第二届钟南山青年科技创新奖发布仪式在香港科大(广州)成功举办...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、第二届钟南山青年科技创新奖发布仪式在香港科大&#xff08;广州&#xff09;成功举办。本次活动由中国青年科技工作者协会、中国青年创业就业基金会、钟南山青年科技创新奖评审委员会、共青团广东省委员会、广州市南沙开…

axios请求本地json文件

1.安装axios npm install axios --save 2.在main.js中引入 import { createApp } from vue import axios from axios import VueAxios from vue-axios const app createApp(App) app.config.globalProperties.$http axios; app.use(VueAxios, axios) 3.在根目录下的publi…

什么小程序需要社交相关类目?

1、陌生人交友&#xff1a;小程序内涉及提供在线陌生人交友服务&#xff0c;需补充社交-陌生人交友类目。 所需资质&#xff1a;《增值电信业务经营许可证》&#xff08;核准服务项目包含“互联网信息服务业务”&#xff09; 案例&#xff1a;如下图小程序涉及通过展示用户微…

【JAVA】云HIS系统功能菜单知识(三)

云HIS系统能帮助基层医院完成日常各类业务&#xff0c;提供病患预约挂号、病患问诊、电子病历、开药发药、会员管理、统计查询、医生工作站和护士工作站等一系列常规功能。 四、住院医生站 1.医嘱管理 登记信息、主治医生、患者信息、床位号、病案编号、住院号、身份证号、入…

从零到一nvm、npm、cnpm、yarn、vue全套安装和环境配置以及创建新项目和如何运行人家的项目大全,最详细,保姆级

NVM 1.下载 下载地址&#xff1a;Releases coreybutler/nvm-windows GitHub 可能需要开启魔法 划到下面。找到如图所示的文件下载即可2. 2.安装&#xff08;要记住安装的路径&#xff09; 基本一键默认即可&#xff0c;你要修改路径也可以&#xff0c;但不建议 下载好…

Rust之泛型、特性和生命期(四):验证有生存期的引用

开发环境 Windows 10Rust 1.71.0 VS Code 1.80.1 项目工程 这里继续沿用上次工程rust-demo 验证具有生存期的引用 生存期是我们已经在使用的另一种泛型。生存期不是确保一个类型具有我们想要的行为&#xff0c;而是确保引用在我们需要时有效。 我们在第4章“引用和借用”一…

Java将数据集合转换为PDF

这里写自定义目录标题 将数据集合转换为pdf引入包工具类测试代码导出效果 将数据集合转换为pdf 依赖itext7包将数据集合转换导出为PDF文件 引入包 <properties><itext.version>7.1.11</itext.version> </properties><dependency><groupId&…

Spring、Springboot、SpringMVC之间的关系

他们之间没有明确的区分。一个项目&#xff0c;可以说是SpringMVC,又是Sprigboot,又是Spring项目。 首先简单看一下他们的定义&#xff1a; Spring是包含众多容器的IOC(控制反转)容器&#xff0c;是一个分层的轻量级框架&#xff0c;为了简化Java程序的开发。Springboot在Spr…

实例022 非矩形窗体

实例说明 大部分Windows窗体都是一个矩形区域&#xff0c;读者是否已经厌倦了这种中规中矩的矩形窗体&#xff1f;本例中的窗体是一个打破传统矩形的异型窗体&#xff0c;运行该例会看到一个非常可爱的窗体&#xff0c;单击【X】按钮就会使窗口关闭。实例效果如图1.22所示。 …

基于MATLAB的无人机遥感数据预处理与农林植被性状估算实践

遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多维、多地等角度&#xff0c;获取大量的农情数据。数据具有面状、实时、非接触、无伤检测等显著优势&#xff0c;是智慧农业必须采用的重要技术之一。本内容主要针对农业、林业、生态、遥感背景的对无人机遥感有兴趣的…

低代码平台协同OA升级,促进金融企业信息化建设

编者按&#xff1a;数字化办公是信息化时代每个企业不可避免的&#xff0c;OA系统是数字化办公的关键环节。如何与时俱进&#xff0c;保持企业的活力&#xff0c;增强企业综合竞争力&#xff1f;本文分析了企业OA系统为什么需要升级&#xff0c;并进一步指出如何实现升级。 关…