Spring Boot 整合 Redis 使用教程

作为开发者,相信大家都知道 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 {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Testpublic 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 {@Beanpublic 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;}@Beanpublic 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 {@Autowiredprivate IPostTagService postTagService;@Autowiredprivate 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.GenericObjectPoolConfigat 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 {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic void set(String key, Object value, long time) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);}@Overridepublic void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}@Overridepublic Object get(String key) {return redisTemplate.opsForValue().get(key);}@Overridepublic Boolean del(String key) {return redisTemplate.delete(key);}@Overridepublic Long del(List<String> keys) {return redisTemplate.delete(keys);}@Overridepublic Boolean expire(String key, long time) {return redisTemplate.expire(key, time, TimeUnit.SECONDS);}@Overridepublic Long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}@Overridepublic Boolean hasKey(String key) {return redisTemplate.hasKey(key);}@Overridepublic Long incr(String key, long delta) {return redisTemplate.opsForValue().increment(key, delta);}@Overridepublic Long decr(String key, long delta) {return redisTemplate.opsForValue().increment(key, -delta);}@Overridepublic Object hGet(String key, String hashKey) {return redisTemplate.opsForHash().get(key, hashKey);}@Overridepublic Boolean hSet(String key, String hashKey, Object value, long time) {redisTemplate.opsForHash().put(key, hashKey, value);return expire(key, time);}@Overridepublic void hSet(String key, String hashKey, Object value) {redisTemplate.opsForHash().put(key, hashKey, value);}@Overridepublic Map<Object, Object> hGetAll(String key) {return redisTemplate.opsForHash().entries(key);}@Overridepublic Boolean hSetAll(String key, Map<String, Object> map, long time) {redisTemplate.opsForHash().putAll(key, map);return expire(key, time);}@Overridepublic void hSetAll(String key, Map<String, Object> map) {redisTemplate.opsForHash().putAll(key, map);}@Overridepublic void hDel(String key, Object... hashKey) {redisTemplate.opsForHash().delete(key, hashKey);}@Overridepublic Boolean hHasKey(String key, String hashKey) {return redisTemplate.opsForHash().hasKey(key, hashKey);}@Overridepublic Long hIncr(String key, String hashKey, Long delta) {return redisTemplate.opsForHash().increment(key, hashKey, delta);}@Overridepublic Long hDecr(String key, String hashKey, Long delta) {return redisTemplate.opsForHash().increment(key, hashKey, -delta);}@Overridepublic Set<Object> sMembers(String key) {return redisTemplate.opsForSet().members(key);}@Overridepublic Long sAdd(String key, Object... values) {return redisTemplate.opsForSet().add(key, values);}@Overridepublic Long sAdd(String key, long time, Object... values) {Long count = redisTemplate.opsForSet().add(key, values);expire(key, time);return count;}@Overridepublic Boolean sIsMember(String key, Object value) {return redisTemplate.opsForSet().isMember(key, value);}@Overridepublic Long sSize(String key) {return redisTemplate.opsForSet().size(key);}@Overridepublic Long sRemove(String key, Object... values) {return redisTemplate.opsForSet().remove(key, values);}@Overridepublic List<Object> lRange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}@Overridepublic Long lSize(String key) {return redisTemplate.opsForList().size(key);}@Overridepublic Object lIndex(String key, long index) {return redisTemplate.opsForList().index(key, index);}@Overridepublic Long lPush(String key, Object value) {return redisTemplate.opsForList().rightPush(key, value);}@Overridepublic Long lPush(String key, Object value, long time) {Long index = redisTemplate.opsForList().rightPush(key, value);expire(key, time);return index;}@Overridepublic Long lPushAll(String key, Object... values) {return redisTemplate.opsForList().rightPushAll(key, values);}@Overridepublic Long lPushAll(String key, Long time, Object... values) {Long count = redisTemplate.opsForList().rightPushAll(key, values);expire(key, time);return count;}@Overridepublic Long lRemove(String key, long count, Object value) {return redisTemplate.opsForList().remove(key, count, value);}@Overridepublic int countKey(String keyPrefix) {return redisTemplate.keys(keyPrefix).size();}
}

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

@Controller
@Api(tags = "标签")
@RequestMapping("/postTag")
public class PostTagController {@Autowiredprivate IPostTagService postTagService;@Autowiredprivate IPostTagRelationService postTagRelationService;@Autowiredprivate 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.hqwc.cn/news/454374.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5G智能卷烟工厂数字孪生可视化平台,推进烟草行业数字化转型

5G智能卷烟工厂数字孪生可视化平台&#xff0c;推进烟草行业数字化转型。随着5G技术的不断发展&#xff0c;智能卷烟工厂数字孪生可视化平台成为了推进烟草行业数字化转型的重要手段。该平台将5G技术与数字孪生技术相结合&#xff0c;实现了对卷烟生产全过程的实时监控、数据分…

好用的大学生教材搜题软件?帮助大学生解决问题的实用搜题工具 #知识分享#知识分享#经验分享

对于大学生来说&#xff0c;每天面对各式各样的学习任务和问题&#xff0c;寻找合适的学习资源和工具成了我们的迫切需求。幸运的是&#xff0c;现如今有许多高效且实用的日常搜题和学习软件可以满足我们的需求&#xff0c;助力我们取得更好的学习成果。 1.Forest专注森林 Fo…

蓝桥杯Web应用开发-CSS 基础语法3(文本属性)

CSS 基础语法-文本属性 专栏持续更新中 文本属性 文本属性用于定义文本的样式&#xff0c;通过文本属性&#xff0c;可以改变文本的颜色、字间距、对齐方式、文本修饰和文本缩进等。常用文本属性如下表所示&#xff1a; 属 性可取值描述line-heightnormal、number、length、…

【PTA编程题】7-1 保持链表有序

对于输入的若干学生的信息&#xff0c;按学号顺序从小到大建立有序链表&#xff0c;最后遍历链表&#xff0c;并按顺序输出学生信息。 输入格式: 首先输入一个正整数T&#xff0c;表示测试数据的组数&#xff0c;然后是T组测试数据。每组测试数据首先输入一个正整数n&#xf…

忘记 RAG:拥抱Agent设计,让 ChatGPT 更智能更贴近实际

RAG&#xff08;检索增强生成&#xff09;设计模式通常用于开发特定数据领域的基于实际情况的ChatGPT。 然而&#xff0c;重点主要是改进检索工具的效率&#xff0c;如嵌入式搜索、混合搜索和微调嵌入&#xff0c;而不是智能搜索。 这篇文章介绍了一种新的方法&#xff0c;灵感…

文献速递:肿瘤分割---- ALA-Net:用于3D结直肠肿瘤分割的自适应病变感知注意力网络

文献速递&#xff1a;肿瘤分割---- ALA-Net&#xff1a;用于3D结直肠肿瘤分割的自适应病变感知注意力网络 01 文献速递介绍 结直肠癌&#xff08;CRC&#xff09;在全球范围内与高发病率和死亡率相关&#xff0c;。肿瘤的预后高度依赖于诊断时疾病的阶段。准确检测和分割肿瘤…

【已解决】c++ qt选中该行为什么该列部分变色

笔者开启了QTableView中交替行改变颜色&#xff0c;发现笔者自定义绘制的水平滚动条&#xff0c;在选中后不发生颜色改变&#xff0c;这让笔者很疑惑。笔者查阅资料后发现&#xff0c;自定义绘制的控件&#xff0c;要自身设置颜色。当笔者解决了这个问题时&#xff0c;顺手就将…

2.5学习总结

2.5 1.传纸条 2.装箱问题 3.开心的金明 4.传球游戏 5.修改数组 6.对局匹配 7.刷题统计 传纸条https://www.luogu.com.cn/problem/P1006 题目描述 小渊和小轩是好朋友也是同班同学&#xff0c;他们在一起总有谈不完的话题。一次素质拓展活动中&#xff0c;班上同学安排坐成一…

halcon圆形、残缺圆形检测

代码中关键知识&#xff1a; 1、循环读取图片 2、基本图像处理&#xff1a;开运算、二值化、内部填充、 3、获取图像边缘线&#xff0c;分割曲线&#xff0c;然后计算曲线的circularity_xld圆率范围&#xff0c;和area_center_xld的面积大小&#xff0c;做阈值筛选 4、然后…

Jmeter接口自动化测试 —— Jmeter断言之Json断言

json断言可以让我们很快的定位到响应数据中的某一字段&#xff0c;当然前提是响应数据是json格式的&#xff0c;所以如果响应数据为json格式的话&#xff0c;使用json断言还是相当方便的。 还是以之前的接口举例 Url: https://data.cma.cn/weatherGis/web/weather/weatherFcst…

代码生成器(新):mybatis-plus-generator使用指南

代码生成器&#xff08;新&#xff09;官网 后端代码&#xff1a;点击查看 LearnElementUiAndSpringBoot 提醒&#xff1a;LearnElementUiAndSpringBoot下载完后&#xff0c;在运行调试 Main.java里的main方法之前&#xff0c;除了utils包和Main.java文件&#xff0c;其他包需…

SpringBoot security 安全认证(三)——自定义注解实现接口放行配置

背景&#xff1a;通过Security实现了安全管理&#xff0c;可以配置哪些接口可以无token直接访问。但一个麻烦就是每增加一个匿名访问接口时都要去修改SecurityConfig配置&#xff0c;从程序设计上讲是不太让人接受的。 本节内容&#xff1a;即是解决以上问题&#xff0c;增加一…