Spring Boot 整合 Redis 使用教程

news2024/11/27 16:28:53

作为开发者,相信大家都知道 Redis 的重要性。Redis 是使用 C 语言开发的一个高性能键值对数据库,是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。

Redis 以超高的性能、完美的文档、简洁的源码著称,国内外很多大型互联网公司都在用,比如说阿里、腾讯、GitHub、Stack Overflow 等等。当然了,中小型公司也都在用。

安装 Redis

Redis 的官网提供了各种平台的安装包,Linux、macOS、Windows 的都有。

官方地址:https://redis.io/docs/install/

完成安装后执行 redis-server 就可以启动 Redis 服务了。

顺带安装一下 Redis 客户端工具,推荐 GitHub 星标 20k+ 的 AnotherRedisDesktopManager,一款 🚀🚀🚀 更快、更好、更稳定的Redis桌面(GUI)管理客户端,支持 Windows、macOS 和 Linux,性能出众,可以轻松加载海量键值。

https://github.com/qishibo/AnotherRedisDesktopManager

安装完成后,链接 Redis 服务

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

Redis 教程:Redis 字符串(String)_redis教程

Spring Boot 整合 Redis

第一步,在 pom.xml 文件中添加 Redis 的 starter。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

第二步,在 application.yml 文件中添加 Redis 的配置信息

spring:
  redis:
    host: xxx.xxx.99.232 # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: xxx # Redis服务器连接密码(默认为空)

第三步,在测试类中添加以下代码。

@SpringBootTest
class CodingmoreRedisApplicationTests {
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testRedis() {
        // 添加
        redisTemplate.opsForValue().set("name","欧尼甲");
        // 查询
        System.out.println(redisTemplate.opsForValue().get("name"));
        // 删除
        redisTemplate.delete("name");
        // 更新
        redisTemplate.opsForValue().set("name","哈哈好傻");
        // 查询
        System.out.println(redisTemplate.opsForValue().get("name"));

        // 添加
        stringRedisTemplate.opsForValue().set("name","欧尼甲");
        // 查询
        System.out.println(stringRedisTemplate.opsForValue().get("name"));
        // 删除
        stringRedisTemplate.delete("name");
        // 更新
        stringRedisTemplate.opsForValue().set("name","哈哈好傻");
        // 查询
        System.out.println(stringRedisTemplate.opsForValue().get("name"));

    }

}

RedisTemplate 和 StringRedisTemplate 都是 Spring Data Redis 提供的模板类,可以对 Redis 进行操作,后者针对键值对都是 String 类型的数据,前者可以是任何类型的对象。

RedisTemplate 和 StringRedisTemplate 除了提供 opsForValue 方法来操作字符串之外,还提供了以下方法:

  • opsForList:操作 list
  • opsForSet:操作 set
  • opsForZSet:操作有序 set
  • opsForHash:操作 hash

运行测试类后可以在控制台看到相关信息。

也可以通过 AnotherRedisDesktopManager 客户端查看 Redis 数据库中的数据

编程喵整合 Redis

编程喵是一个 Spring Boot + Vue 的前后端分离项目,要整合 Redis 的话,最好的方式是使用 Spring Cache,仅仅通过 @Cacheable、@CachePut、@CacheEvict、@EnableCaching 等注解就可以轻松使用 Redis 做缓存了

1)@EnableCaching ,开启缓存功能。

2)@Cacheable ,调用方法前,去缓存中找,找到就返回,找不到就执行方法,并将返回值放到缓存中。

3)@CachePut ,方法调用前不会去缓存中找,无论如何都会执行方法,执行完将返回值放到缓存中。

4)@CacheEvict ,清理缓存中的一个或多个记录。

Spring Cache 是 Spring 提供的一套完整的缓存解决方案,虽然它本身没有提供缓存的实现,但它提供的一整套接口、规范、配置、注解等,可以让我们无缝衔接 Redis、Ehcache 等缓存实现。

Spring Cache 的注解(前面提到的四个)会在调用方法之后,去缓存方法返回的最终结果;或者在方法调用之前拿缓存中的结果,当然还有删除缓存中的结果。

这些读写操作不用我们手动再去写代码实现了,直接交给 Spring Cache 来打理就 OK 了,是不是非常贴心?

第一步,在 pom.xml 文件中追加 Redis 的 starter。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

第二步,在 application.yml 文件中添加 Redis 链接配置。

spring:
    redis:
        host: 118.xx.xx.xxx # Redis服务器地址
        database: 0 # Redis数据库索引(默认为0)
        port: 6379 # Redis服务器连接端口
        password: xx # Redis服务器连接密码(默认为空)
        timeout: 1000ms # 连接超时时间(毫秒)

第三步,新增 RedisConfig.java 类,通过 RedisTemplate 设置 JSON 格式的序列化器,这样的话存储到 Redis 里的数据将是有类型的 JSON 数据,例如:

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

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

        // 通过 Jackson 组件进行序列化
        RedisSerializer<Object> serializer = redisSerializer();

        // key 和 value
        // 一般来说, redis-key采用字符串序列化;
        // redis-value采用json序列化, json的体积小,可读性高,不需要实现serializer接口。
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisSerializer<Object> redisSerializer() {
        //创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // https://www.cnblogs.com/shanheyongmu/p/15157378.html
        // objectMapper.enableDefaultTyping()被弃用
        objectMapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

}

通过 RedisCacheConfiguration 设置超时时间,来避免产生很多不必要的缓存数据。

@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
    //设置Redis缓存有效期为1天
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
    return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}

第四步,在标签更新接口中添加 @CachePut 注解,也就是说方法执行前不会去缓存中找,但方法执行完会将返回值放入缓存中。

@Controller
@Api(tags = "标签")
@RequestMapping("/postTag")
public class PostTagController {

    @Autowired
    private IPostTagService postTagService;
    @Autowired
    private IPostTagRelationService postTagRelationService;

    @RequestMapping(value = "/update", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation("修改标签")
    @CachePut(value = "codingmore", key = "'codingmore:postags:'+#postAddTagParam.postTagId")
    public ResultObject<String> update(@Valid PostTagParam postAddTagParam) {
        if (postAddTagParam.getPostTagId() == null) {
            return ResultObject.failed("标签id不能为空");
        }
        PostTag postTag = postTagService.getById(postAddTagParam.getPostTagId());
        if (postTag == null) {
            return ResultObject.failed("标签不存在");
        }
        QueryWrapper<PostTag> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("description", postAddTagParam.getDescription());
        int count = postTagService.count(queryWrapper);
        if (count > 0) {
            return ResultObject.failed("标签名称已存在");
        }
        BeanUtils.copyProperties(postAddTagParam, postTag);
        return ResultObject.success(postTagService.updateById(postTag) ? "修改成功" : "修改失败");
    }
}

注意看 @CachePut 注解这行代码:

@CachePut(value = "codingmore", key = "'codingmore:postags:'+#postAddTagParam.postTagId")
  • value:缓存名称,也就是缓存的命名空间,value 这里应该换成 namespace 更好一点;
  • key:用于在命名空间中缓存的 key 值,可以使用 SpEL 表达式,比如说 'codingmore:postags:'+#postAddTagParam.postTagId
  • 还有两个属性 unless 和 condition 暂时没用到,分别表示条件符合则不缓存,条件符合则缓存。

第五步,启动服务器端,启动客户端,修改标签进行测试

使用 Redis 连接池

Redis 是基于内存的数据库,本来是为了提高程序性能的,但如果不使用 Redis 连接池的话,建立连接、断开连接就需要消耗大量的时间。

用了连接池,就可以实现在客户端建立多个连接,需要的时候从连接池拿,用完了再放回去,这样就节省了连接建立、断开的时间。

要使用连接池,我们得先了解 Redis 的客户端,常用的有两种:Jedis 和 Lettuce。

  • Jedis:Spring Boot 1.5.x 版本时默认的 Redis 客户端,实现上是直接连接 Redis Server,如果在多线程环境下是非线程安全的,这时候要使用连接池为每个 jedis 实例增加物理连接;
  • Lettuce:Spring Boot 2.x 版本后默认的 Redis 客户端,基于 Netty 实现,连接实例可以在多个线程间并发访问,一个连接实例不够的情况下也可以按需要增加连接实例。

它俩在 GitHub 上都挺受欢迎的,大家可以按需选用。

我这里把两种客户端的情况都演示一下,方便小伙伴们参考。

1)Lettuce

第一步,修改 application-dev.yml,添加 Lettuce 连接池配置(pool 节点)。

spring:
    redis:
        lettuce:
          pool:
            max-active: 8 # 连接池最大连接数
            max-idle: 8 # 连接池最大空闲连接数
            min-idle: 0 # 连接池最小空闲连接数
            max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制

第二步,在 pom.xml 文件中添加 commons-pool2 依赖,否则会在启动的时候报 ClassNotFoundException 的错。这是因为 Spring Boot 2.x 里默认没启用连接池。

Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 153 common frames omitted

添加 commons-pool2 依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

重新启动服务,在 RedisConfig 类的 redisTemplate 方法里对 redisTemplate 打上断点,debug 模式下可以看到连接池的配置信息(redisConnectionFactory→clientConfiguration→poolConfig)。如下图所示。

如果在 application-dev.yml 文件中没有添加 Lettuce 连接池配置的话,是不会看到

2)Jedis

第一步,在 pom.xml 文件中添加 Jedis 依赖,去除 Lettuce 默认依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

第二步,修改 application-dev.yml,添加 Jedis 连接池配置。

spring:
    redis:
        jedis:
          pool:
            max-active: 8 # 连接池最大连接数
            max-idle: 8 # 连接池最大空闲连接数
            min-idle: 0 # 连接池最小空闲连接数
            max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制

启动服务后,观察 redisTemplate 的 clientConfiguration 节点,可以看到它的值已经变成 DefaultJedisClientConfiguration 对象了。

当然了,也可以不配置 Jedis 客户端的连接池,走默认的连接池配置。因为 Jedis 客户端默认增加了连接池的依赖包,在 pom.xml 文件中点开 Jedis 客户端依赖可以查看到。

自由操作 Redis

Spring Cache 虽然提供了操作 Redis 的便捷方法,比如我们前面演示的 @CachePut 注解,但注解提供的操作非常有限,比如说它只能保存返回值到缓存中,而返回值并不一定是我们想要保存的结果。

与其保存这个返回给客户端的 JSON 信息,我们更想保存的是更新后的标签。那该怎么自由地操作 Redis 呢?

第一步,增加 RedisService 接口:

package com.codingmore.service;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * redis操作Service

 */
public interface IRedisService {

    /**
     * 保存属性
     */
    void set(String key, Object value, long time);

    /**
     * 保存属性
     */
    void set(String key, Object value);

    /**
     * 获取属性
     */
    Object get(String key);

    /**
     * 删除属性
     */
    Boolean del(String key);

    /**
     * 批量删除属性
     */
    Long del(List<String> keys);

    /**
     * 设置过期时间
     */
    Boolean expire(String key, long time);

    /**
     * 获取过期时间
     */
    Long getExpire(String key);

    /**
     * 判断是否有该属性
     */
    Boolean hasKey(String key);

    /**
     * 按delta递增
     */
    Long incr(String key, long delta);

    /**
     * 按delta递减
     */
    Long decr(String key, long delta);

    /**
     * 获取Hash结构中的属性
     */
    Object hGet(String key, String hashKey);

    /**
     * 向Hash结构中放入一个属性
     */
    Boolean hSet(String key, String hashKey, Object value, long time);

    /**
     * 向Hash结构中放入一个属性
     */
    void hSet(String key, String hashKey, Object value);

    /**
     * 直接获取整个Hash结构
     */
    Map<Object, Object> hGetAll(String key);

    /**
     * 直接设置整个Hash结构
     */
    Boolean hSetAll(String key, Map<String, Object> map, long time);

    /**
     * 直接设置整个Hash结构
     */
    void hSetAll(String key, Map<String, Object> map);

    /**
     * 删除Hash结构中的属性
     */
    void hDel(String key, Object... hashKey);

    /**
     * 判断Hash结构中是否有该属性
     */
    Boolean hHasKey(String key, String hashKey);

    /**
     * Hash结构中属性递增
     */
    Long hIncr(String key, String hashKey, Long delta);

    /**
     * Hash结构中属性递减
     */
    Long hDecr(String key, String hashKey, Long delta);

    /**
     * 获取Set结构
     */
    Set<Object> sMembers(String key);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, Object... values);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, long time, Object... values);

    /**
     * 是否为Set中的属性
     */
    Boolean sIsMember(String key, Object value);

    /**
     * 获取Set结构的长度
     */
    Long sSize(String key);

    /**
     * 删除Set结构中的属性
     */
    Long sRemove(String key, Object... values);

    /**
     * 获取List结构中的属性
     */
    List<Object> lRange(String key, long start, long end);

    /**
     * 获取List结构的长度
     */
    Long lSize(String key);

    /**
     * 根据索引获取List中的属性
     */
    Object lIndex(String key, long index);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value, long time);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Object... values);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Long time, Object... values);

    /**
     * 从List结构中移除属性
     */
    Long lRemove(String key, long count, Object value);

    /**
     * 获取数量
     * @param keyPrefix
     * @return
     */
    int countKey(String keyPrefix);
}

第二步,增加 RedisServiceImpl 实现类:

package com.codingmore.service.impl;

import com.codingmore.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis操作实现类
 
 */
@Service
public class RedisServiceImpl implements IRedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void set(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean del(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long del(List<String> keys) {
        return redisTemplate.delete(keys);
    }

    @Override
    public Boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        return expire(key, time);
    }

    @Override
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    @Override
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        return expire(key, time);
    }

    @Override
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public void hDel(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    @Override
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    @Override
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Long lPush(String key, Object value, long time) {
        Long index = redisTemplate.opsForList().rightPush(key, value);
        expire(key, time);
        return index;
    }

    @Override
    public Long lPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }

    @Override
    public int countKey(String keyPrefix) {
        return redisTemplate.keys(keyPrefix).size();
    }
}

第三步,在标签 PostTagController 中增加 Redis 测试用接口 simpleTest :

@Controller
@Api(tags = "标签")
@RequestMapping("/postTag")
public class PostTagController {
    @Autowired
    private IPostTagService postTagService;
    @Autowired
    private IPostTagRelationService postTagRelationService;

    @Autowired
    private RedisService redisService;

    @RequestMapping(value = "/simpleTest", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation("修改标签/Redis 测试用")
    public ResultObject<PostTag> simpleTest(@Valid PostTagParam postAddTagParam) {
        if (postAddTagParam.getPostTagId() == null) {
            return ResultObject.failed("标签id不能为空");
        }
        PostTag postTag = postTagService.getById(postAddTagParam.getPostTagId());
        if (postTag == null) {
            return ResultObject.failed("标签不存在");
        }
        QueryWrapper<PostTag> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("description", postAddTagParam.getDescription());
        int count = postTagService.count(queryWrapper);
        if (count > 0) {
            return ResultObject.failed("标签名称已存在");
        }
        BeanUtils.copyProperties(postAddTagParam, postTag);

        boolean successFlag = postTagService.updateById(postTag);

        String key = "redis:simple:" + postTag.getPostTagId();
        redisService.set(key, postTag);

        PostTag cachePostTag = (PostTag) redisService.get(key);
        return ResultObject.success(cachePostTag);
    }

}

第四步,重启服务,使用 Knife4j 测试该接口 :

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

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

相关文章

【AI视野·今日Sound 声学论文速览 第四十八期】Mon, 15 Jan 2024

AI视野今日CS.Sound 声学论文速览 Mon, 15 Jan 2024 Totally 5 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers LCB-net: Long-Context Biasing for Audio-Visual Speech Recognition Authors Fan Yu, Haoxu Wang, Xian Shi, Shiliang Zhang在线会议和…

VUE PC端可拖动悬浮按钮

一、实现效果&#xff1a; 二、FloatButton.vue <template><div><div class"sssss"><div class"callback float" mousedown"down" touchstart"down" mousemove"move" touchmove"move" mous…

Python爬虫学习之解析_jsonpath

一、jsonpath的基本使用 import json import jsonpath obj json.load(open(json文件,r,encodingutf-8)) ret jsonpath.jsonpath(obj,jsonpath语法) 二、jsonpath语法 e.g. import json import jsonpathobj json.load(open(jsonpath.json,r,encodingutf-8))#书店所有的书的作…

HashCat 恢复Excel、Word、PPT密码保姆教程

HashCat 恢复Excel、Word、PPT密码 一、流程 整体需要两个步骤 先用office2john.py获取下文件的hash值 python office2john.py 1.xlsx > hash这个命令需要你电脑有python环境&#xff0c;然后在cmd命令窗口中执行此命令就行 文件链接&#xff1a;https://github.com/magnu…

Spring-集成Web

一、引子 前面我们在Spring集成Junit中为读者引出了Spring善于集成其它框架的优势&#xff0c;而Spring项目不可能仅限于小范围的某个方法的测试&#xff0c;终究会落脚于Web项目上。于是&#xff0c;我们就从这里正式进入Spring集成Web的话题。由于笔者会从原生的Java Web开发…

Kotlin中的内置函数-apply、let

在使用Kotlin的过程中会经常用到其内置函数&#xff0c;包括apply&#xff0c;let&#xff0c;run&#xff0c;with&#xff0c;also&#xff0c;takeIf,takeUnless函数等&#xff0c;想要更好熟悉Kotlin&#xff0c;这些函数必须烂熟于心&#xff0c;接下来让我们来逐步了解&a…

7.1、一致公钥密码系统

7.1、一致公钥密码系统 一、引言 在本节中&#xff0c;我们将描述一个真实公钥密码系统的玩具模型。这个模型与维数为 2 的格有着意想不到的联系&#xff0c;由于维数太低&#xff0c;因此存在致命漏洞。不过&#xff0c;它也是一个具有启发性的例子&#xff0c;说明了即使基…

【Pytorch】CNN中的Attention

目录 更大层面上的Attention在attention中&#xff0c;怎么分区channel-wise还是spatial-wise举一个Spatial-Channel Attention的例子 使用广泛的Dot-product Attentionattention机制中的query,key,value的概念解释Attention的一个例子 更大层面上的Attention 在attention中&a…

网络服务综合实验项目

目录 实验要求 运行环境 基础配置 业务需求 实验步骤 一、基础配置 1.1、配置静态IP 1.1.1、 在192.168.159.130中配置 1.1.2、 在192.168.159.131中配置 ​编辑 1.2、修改主机名及hosts映射 1.2.1、在192.168.159.130中配置 1.2.2、 编辑配置hosts文件 1.2.3、重启…

1. 两数之和(C++)(map)(哈希算法)

✨欢迎来到脑子不好的小菜鸟的文章✨ &#x1f388;创作不易&#xff0c;麻烦点点赞哦&#x1f388; 所属专栏&#xff1a;刷题 我的主页&#xff1a;脑子不好的小菜鸟 文章特点&#xff1a;关键点和步骤讲解放在 代码相应位置 前提&#xff1a; 看本文章之前&#xff0c;建…

前端开发项目技术总结

前端 vue框架简单学了一遍&#xff0c;也做了demo&#xff0c;主要学了创建项目、路由、状态管理、UI组件库等。 最近在做前端的项目&#xff0c;总结一些开发遇到的问题和解决方案 数据绑定模型 vue框架的标签 :data 和prop标签 data绑定对应的模型&#xff0c;prop是对应…

【C++杂货铺】详解类和对象 [下]

个人博客&#xff1a;代码菌-CSDN博客 专栏&#xff1a;C杂货铺_代码菌的博客-CSDN博客 目录 &#x1f308;前言&#x1f308; &#x1f4c1; 初始化列表&#xff08;灰常重要&#xff09; &#x1f4c2; 引入 &#x1f4c2; 概念 &#x1f4c2; 特性 &#x1f4c1; 拓展构…

【计算机二级考试C语言】C递归

目录 C 递归 数的阶乘 实例 斐波那契数列 实例 C 递归 递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从…

关闭idea之后,项目还在运行,端口被占用

今天在写项目的时候&#xff0c;中途安装了一个插件&#xff0c;而且插件显示需要重启idea&#xff0c;重启的时候项目正在运行&#xff0c;重启之后发现idea没有显示有项目正在运行&#xff0c;当我要开启项目的时候&#xff0c;发现无法开启&#xff0c;显示端口被占用了&…

您企业的(AI)人工智能处于哪个准备阶段?

全新AI准备度评估工具现已推出 根据Gartner 2020年CIO调查报告&#xff0c;仅19%的首席信息官声称其AI项目已投产。这意味着&#xff0c;如果要将AI计划部署到生产过程中&#xff0c;高达80%的企业无法实现。我们发现&#xff0c;澳鹏客户取得成功的几率要高出三倍&#xff0c…

@Test Annotation属性- dependsOnMethods属性

目录 语法 如果test1&#xff08;&#xff09;失败了怎么办&#xff1f;test2&#xff08;&#xff09;测试会发生什么&#xff0c;因为它依赖于test1&#xff1f; 如果我们在test1&#xff08;&#xff09;方法上不使用Test注释&#xff0c;但在test 2&#xff08;&#xf…

放弃Nacos作为配置中心,转而选择这款神器~

目前&#xff0c;我们所有微服务的配置中心都没有采用Nacos&#xff0c;而是选择了另一款携程开源的分布式配置中心Apollo&#xff0c;今天就跟大家详细介绍一下这款神级配置中心 1. 基本概念 由于 Apollo 概念比较多&#xff0c;刚开始使用比较复杂&#xff0c;最好先过一遍…

YOLOv5改进系列(29)——添加DilateFormer(MSDA)注意力机制(中科院一区顶刊|即插即用的多尺度全局注意力机制)

【YOLOv5改进系列】前期回顾&#xff1a; YOLOv5改进系列&#xff08;0&#xff09;——重要性能指标与训练结果评价及分析 YOLOv5改进系列&#xff08;1&#xff09;——添加SE注意力机制 YOLOv5改进系列&#xff08;2&#xff09;——添加CBAM注意力机制 YOLOv5改进系列&…

【Java 数据结构】栈和队列

栈和队列 1. 栈(Stack)1.1 概念1.2 栈的使用1.3 栈的模拟实现1.4 栈的应用场景1.5 概念区分 2. 队列(Queue)2.1 概念2.2 队列的使用2.3 队列模拟实现2.4 循环队列 3. 双端队列 (Deque)4. 面试题 1. 栈(Stack) 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在…

STM32入门教程-2023版【5-1】NVIC

关注 点赞 不错过精彩内容 大家好&#xff0c;我是硬核王同学&#xff0c;最近在做免费的嵌入式知识分享&#xff0c;帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 四、NVIC &#xff08;1&#xff09;NVIC基本结构 外部中断的整体结构图如下&#xff1a; 这个NVI…