尚硅谷-尚庭公寓知识点

article/2025/6/23 18:52:18

文章目录

  • 尚庭公寓知识点
    • 1、转换器(Converter)
    • 2、全局异常
    • 3、定时任务
      • 1. 核心步骤
        • (1) 启用定时任务
        • (2) 创建定时任务
      • 2. `@Scheduled` 参数详解
      • 3. Cron 表达式语法
      • 4. 配置线程池(避免阻塞)
      • 5. 动态控制任务(高级用法)
      • 6. 注意事项
    • 4、MyBatisPlus的修改策略
      • 1. **更新策略类型(FieldStrategy 枚举)**
      • 2. **配置方式**
        • (1) **字段级配置(注解方式:局部配置)**
        • (2) **全局配置(application.yml)**
      • 3. **使用场景示例**
        • 场景 1:允许更新为 null 值
        • 场景 2:空值不覆盖数据库原有值
        • 场景 3:创建时间永不更新
      • 4. **更新操作行为说明**
      • 5. **动态更新策略技巧**
      • 6. **注意事项**
    • 5、图形化验证码 EasyCaptcha(Gitee)
      • 1、导入依赖
      • 2、使用示例
    • 6、线程本地(ThreadLocal)储存个人信息
    • 7、Mybatis-Plus分页插件注意事项
    • 8、异步调用(@Async)
      • 异步调用本质
      • 实现原理
      • 使用示例
        • 启动异步调用
        • 声明异步处理的方法(@Async)


尚庭公寓知识点

1、转换器(Converter)

场景:当数据库中存在多个状态字段,且都使用了枚举类进行了封装,此时就需要转换器(p99集)

在讲解之前我们需要知道请求参数从发起到响应的流程:

  • 请求流程
    在这里插入图片描述

说明

  • SpringMVC中的WebDataBinder组件负责将HTTP的请求参数绑定到Controller方法的参数,并实现参数类型的转换。

  • Mybatis中的TypeHandler用于处理Java中的实体对象与数据库之间的数据类型转换。

  • 响应流程
    在这里插入图片描述

    说明

    • SpringMVC中的HTTPMessageConverter组件负责将Controller方法的返回值(Java对象)转换为HTTP响应体中的JSON字符串,或者将请求体中的JSON字符串转换为Controller方法中的参数(Java对象),例如下一个接口保存或更新标签信息
      在这里插入图片描述

WebDataBinder枚举类型转换

WebDataBinder依赖于Converter实现类型转换,若Controller方法声明的@RequestParam参数的类型不是StringWebDataBinder就会自动进行数据类型转换。SpringMVC提供了常用类型的转换器,例如StringIntegerStringDateStringBoolean等等,其中也包括String到枚举类型,但是String到枚举类型的默认转换规则是根据实例名称(“APARTMENT”)转换为枚举对象实例(ItemType.APARTMENT)。若想实现code属性到枚举对象实例的转换,需要自定义Converter,代码如下,具体内容可参考官方文档。

  • web-admin模块自定义com.atguigu.lease.web.admin.custom.converter.StringToItemTypeConverter
@Component
public class StringToItemTypeConverter implements Converter<String, ItemType> {@Overridepublic ItemType convert(String code) {for (ItemType value : ItemType.values()) {if (value.getCode().equals(Integer.valueOf(code))) {return value;}}throw new IllegalArgumentException("code非法");}
}

注册上述的StringToItemTypeConverter,在web-admin模块创建com.atguigu.lease.web.admin.custom.config.WebMvcConfiguration
内容如下:

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate StringToItemTypeConverter stringToItemTypeConverter;/*注册stringToItemTypeConverter*/@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(stringToItemTypeConverter);}
}

addFormatters的工作原理:

Client DispatcherServlet HandlerMethod FormatterRegistry DateFormatter 发送请求 /user?birth=2023/10/01 解析请求参数 查找匹配的Formatter 返回DateFormatter 转换字符串为Date对象 返回Date实例 执行控制器方法 返回响应 Client DispatcherServlet HandlerMethod FormatterRegistry DateFormatter

但是我们有很多的枚举类型都需要考虑类型转换这个问题,按照上述思路,我们需要为每个枚举类型都定义一个Converter,并且每个Converter的转换逻辑都完全相同,针对这种情况,我们使用ConverterFactory接口更为合适,这个接口可以将同一个转换逻辑应用到一个接口的所有实现类,因此我们可以定义一个BaseEnum接口,然后另所有的枚举类都实现该接口,然后就可以自定义ConverterFactory,集中编写各枚举类的转换逻辑了。具体实现如下:

  • model模块定义com.atguigu.lease.model.enums.BaseEnum接口

    public interface BaseEnum {Integer getCode();String getName();
    }
    
  • 令所有com.atguigu.lease.model.enums包下的枚举类都实现BaseEnun接口

  • web-admin模块自定义com.atguigu.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory

/*** 统一将String转换为BaseEnum* 执行逻辑为:当SpringMvc要进行String -> BaseEnum(包括子类)型转换时就会执行Converter()方法*/
@Component
public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {/*** 当状态类的枚举过多时可以使用converterFactory(转换器工厂)进行统一转换(前提是原数据类型相同【String】,转换的数据类型都有统一的继承【BaseEnum】)* @param targetType 【转化的类型】* @return 返回值可以直接使用new Converter...进行设置* @param <T> 表示继承于BaseEnum的所有对象*/@Overridepublic <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {/*Converter(转换器)*/return new Converter<String, T>() {@Overridepublic T convert(String source) {/*** targetType.getEnumConstants:遍历枚举常量*/for (T enumConstant : targetType.getEnumConstants()) {/*判断code是否与参数source相等,相等代表这个枚举常量是目标则返回*/if (enumConstant.getCode().equals(Integer.parseInt(source))) {return enumConstant;}}throw new RuntimeException("code:" + source + "code非法");}};}
}
  • 注册上述的ConverterFactory,在web-admin模块创建com.atguigu.lease.web.admin.custom.config.WebMvcConfiguration,内容如下:
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverterFactory(this.stringToBaseEnumConverterFactory);}
}

此时请求参数已经可以转化为对应枚举对象了,但是但是还需要配置MyBatisPlus映射到数据库的类型转化

  • ypeHandler枚举类型转换**

    Mybatis预置的TypeHandler可以处理常用的数据类型转换,例如StringIntegerDate等等,其中也包含枚举类型,但是枚举类型的默认转换规则是枚举对象实例(ItemType.APARTMENT)和实例名称(“APARTMENT”)相互映射。若想实现code属性到枚举对象实例的相互映射,需要自定义TypeHandler

    不过MybatisPlus提供了一个通用的处理枚举类型的TypeHandler。其使用十分简单,只需在ItemType枚举类的code属性上增加一个注解@EnumValue,Mybatis-Plus便可完成从ItemType对象到code属性之间的相互映射,具体配置如下。

public enum ItemType {APARTMENT(1, "公寓"),ROOM(2, "房间");@EnumValueprivate Integer code;private String name;ItemType(Integer code, String name) {this.code = code;this.name = name;}
}
  • HTTPMessageConverter枚举类型转换

    HttpMessageConverter依赖于Json序列化框架(默认使用Jackson)。其对枚举类型的默认处理规则也是枚举对象实例(ItemType.APARTMENT)和实例名称(“APARTMENT”)相互映射。不过其提供了一个注解@JsonValue,同样只需在ItemType枚举类的code属性上增加一个注解@JsonValue,Jackson便可完成从ItemType对象到code属性之间的互相映射。具体配置如下,详细信息可参考Jackson官方文档。

@Getter
public enum ItemType {APARTMENT(1, "公寓"),ROOM(2, "房间");@EnumValue@JsonValueprivate Integer code;private String name;ItemType(Integer code, String name) {this.code = code;this.name = name;}
}

2、全局异常

官网地址:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html
可以把他理解为对项目异常的拦截器,每当报某一个Exception时便可以自定以操作(例如:直接响应Result.fail)

使用创建一个标记注解@ControllerAdvice

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)/*将返回值序列化为Http响应体*/@ResponseBodypublic Result exceptionHanler(Exception e) {e.printStackTrace();return Result.fail();}@ExceptionHandler(LeaseException.class)@ResponseBodypublic Result LeaseExceptionHandler(LeaseException e) {return Result.handler(e.getMessage(), e.getCode());}
}

3、定时任务

当想要完成一个根据时间动态的去执行某一个逻辑,这时就可以使用springboot中提供的定时任务

1. 核心步骤

(1) 启用定时任务

在主配置类或任意 @Configuration 类上添加 @EnableScheduling

@SpringBootApplication
@EnableScheduling // 启用定时任务支持
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}
(2) 创建定时任务

在 Bean 的方法上使用 @Scheduled 注解:

@Component
public class MyTaskScheduler {// 固定频率执行(每 5 秒)@Scheduled(fixedRate = 5000) // 单位:毫秒public void task1() {System.out.println("固定频率任务执行: " + LocalDateTime.now());}// 固定延迟执行(上次任务结束后延迟 3 秒)@Scheduled(fixedDelay = 3000)public void task2() {System.out.println("固定延迟任务执行: " + LocalDateTime.now());}// 使用 Cron 表达式(每天 12:00 执行)@Scheduled(cron = "0 0 12 * * ?") public void task3() {System.out.println("Cron 任务执行: " + LocalDateTime.now());}
}

2. @Scheduled 参数详解

参数说明示例
fixedRate固定频率(毫秒),上次开始时间后间隔执行fixedRate = 5000
fixedDelay固定延迟(毫秒),上次结束时间后间隔执行fixedDelay = 3000
initialDelay首次延迟(毫秒),需配合 fixedRate/fixedDelay 使用initialDelay = 10000
cronCron 表达式(支持秒级)cron = "0 15 10 * * ?"
zone时区(默认为服务器时区)zone = "Asia/Shanghai"

3. Cron 表达式语法

Spring Boot 支持 6位 Cron 表达式(包含秒):

秒  分  时  日  月  周

常用示例:

  • 0 * * * * ?:每分钟的 0 秒执行
  • 0 0 10 * * ?:每天 10:00 执行
  • 0 0/5 14 * * ?:每天 14:00-14:55,每 5 分钟执行
  • 0 15 10 ? * MON-FRI:周一至周五 10:15 执行

4. 配置线程池(避免阻塞)

默认所有任务在单线程中执行。若需并发,需自定义线程池:

@Configuration
public class SchedulerConfig implements SchedulingConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();taskScheduler.setPoolSize(5); // 线程池大小taskScheduler.setThreadNamePrefix("my-scheduler-");taskScheduler.initialize();taskRegistrar.setTaskScheduler(taskScheduler);}
}

5. 动态控制任务(高级用法)

通过 ScheduledTaskRegistrar 动态注册/取消任务:

@Service
public class DynamicTaskService {@Autowiredprivate ScheduledTaskRegistrar taskRegistrar;private ScheduledFuture<?> future;// 启动任务public void startTask() {future = taskRegistrar.getScheduler().schedule(() -> System.out.println("动态任务执行: " + LocalDateTime.now()),new CronTrigger("0/10 * * * * ?") // 每 10 秒);}// 停止任务public void stopTask() {if (future != null) {future.cancel(true); // 取消任务}}
}

6. 注意事项

  1. 避免长时间阻塞:任务执行时间 > 间隔时间会导致任务堆积(需优化或异步处理)。
  2. 单线程问题:默认单线程顺序执行,耗时任务需配置线程池。
  3. 分布式环境:集群中多个节点会同时执行任务(需额外方案如分布式锁或 Quartz 集成)。

4、MyBatisPlus的修改策略

使用场景:
当使用MyBatisPlus提供的update的APi时会发现,
他会做一个判断当某一个字段的值为null时便不会更新此字段,
而若是想要更新就需要去配置MyBatisPlus中的修改策略(update-strategy)

在 MyBatis-Plus 中,字段的更新策略(update-strategy)主要通过 @TableField 注解的 updateStrategy 属性或全局配置来控制。以下是详细说明和配置方法:


1. 更新策略类型(FieldStrategy 枚举)

策略说明
IGNORED忽略判断:无论字段值是什么,都会更新到数据库(即使为 null)
NOT_NULL非 NULL 判断:字段值不为 null 时才更新(默认值)
NOT_EMPTY非空判断:字段值不为 null 且不为空(如字符串长度>0)才更新
NEVER永不更新:该字段永远不会被更新到数据库
DEFAULT跟随全局配置

2. 配置方式

(1) 字段级配置(注解方式:局部配置)

在实体类字段上使用 @TableField 注解:

public class User {@TableField(updateStrategy = FieldStrategy.IGNORED)private String email;  // 总是更新@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)private String nickname;  // 非空时才更新@TableField(updateStrategy = FieldStrategy.NEVER)private LocalDateTime createTime;  // 永不更新
}
(2) 全局配置(application.yml)
mybatis-plus:global-config:db-config:update-strategy: not_null  # 全局默认策略# 可选值: ignored, not_null, not_empty, never, default

优先级:字段注解 > 全局配置


3. 使用场景示例

场景 1:允许更新为 null 值
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String phone;  // 可更新为 null
场景 2:空值不覆盖数据库原有值
@TableField(updateStrategy = FieldStrategy.NOT_NULL)
private Integer age;  // 若 age=null 则跳过更新
场景 3:创建时间永不更新
@TableField(updateStrategy = FieldStrategy.NEVER)
private LocalDateTime createTime;

4. 更新操作行为说明

  • 使用 updateById()
User user = new User();
user.setId(1L);
user.setEmail(null);  // 策略=IGNORED → 更新为 null
user.setNickname(""); // 策略=NOT_EMPTY → 跳过更新
userMapper.updateById(user);

生成 SQL:

UPDATE user SET email = null WHERE id = 1;
  • 使用 updateWrapper
    通过 Wrapper 更新的字段不受策略影响,会直接更新:
new UpdateWrapper<User>().set("nickname", null)  // 直接更新为 null.eq("id", 1);

5. 动态更新策略技巧

通过条件构造器实现动态更新:

public void updateUserConditional(Long id, String name, String email) {UpdateWrapper<User> wrapper = new UpdateWrapper<User>().eq("id", id);if (name != null) {wrapper.set("name", name);  // 只有非 null 才更新 name}wrapper.set("email", email);    // 总是更新 emailuserMapper.update(null, wrapper);
}

6. 注意事项

  1. 策略仅影响非 null 字段
  • 如果字段在更新时未被设置(值为 null),则根据策略决定是否更新
  • 如果显式设置了值(即使为 null),则受 IGNORED/NEVER 等策略控制
  1. 插入策略 vs 更新策略
  • insertStrategy 控制插入行为(通过 @TableField(insertStrategy=...) 配置)
  • updateStrategy 仅控制更新行为
  1. 全局默认值变更
  • MyBatis-Plus 3.x 开始全局默认策略从 NOT_NULL 改为 DEFAULT(即不处理)
  • 建议显式配置全局策略

5、图形化验证码 EasyCaptcha(Gitee)

EasyCaptcha此项目使用的是在Gitee开源开源项目,
他可以自动生成一个图形化验证码,其支持多种类型的验证码,例如gif、中文、算术等。

使用方式:结合redis可以做一个图形化验证码(指定时间过期)的逻辑,

1、导入依赖

<!--captcha-->
<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId>
</dependency>
<!--redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置redis

spring:data:redis:
#      主机地址host: <hostname>
#      端口port: <port>
#      库database: 0

2、使用示例

@Override
public CaptchaVo getCaptcha() {/*创建随机的一个验证码图像(二进制)*/SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);/*图像对应的值,并将值转化为小写(实现不区分大小写)*/String code = specCaptcha.text().toLowerCase();/*为图像拼接标识唯一id,方便存储到redis中*/String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();/*存到redis中,并设置超时时间*/stringRedisTemplate.opsForValue().set(key, code, RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);/*.toBase64()返回的值,前端可以直接使用赋值给img标签中src属性,自动显示一个验证码*/String image = specCaptcha.toBase64();return new CaptchaVo(image, key);
}
/*配置redis常量*/
public class RedisConstant {public static final String ADMIN_LOGIN_PREFIX = "admin:login:";public static final Integer ADMIN_LOGIN_CAPTCHA_TTL_SEC = 60;
}

6、线程本地(ThreadLocal)储存个人信息

当JWT中存储着个人信息(id、userName),
此时想要查询详细信息便可以:解析JWT获取id->根据id查询信息->返回

此方案可以完成业务,但是在拦截器中我们也会对JWT进行解析判断是否合法,会重复的解析JWT,此时便可以使用本地线程(ThreadLocal)储存个人信息

实现步骤:

  • 创建个人信息的对象
public class LoginUser {private String userName;private Long userId;
}
  • 请求的数据结构
    按理说,前端若想获取当前登录用户的个人信息,需要传递当前用户的id到后端进行查询。但是由于请求中携带的JWT中就包含了当前登录用户的id,故请求个人信息时,就无需再传递id

  • 编写ThreadLocal工具类

    理论上我们可以在Controller方法中,使用@RequestHeader获取JWT,然后在进行解析,如下

    @Operation(summary = "获取登陆用户个人信息")
    @GetMapping("info")
    public Result<SystemUserInfoVo> info(@RequestHeader("access-token") String token) {Claims claims = JwtUtil.parseToken(token);Long userId = claims.get("userId", Long.class);SystemUserInfoVo userInfo = service.getLoginUserInfo(userId);return Result.ok(userInfo);
    }
    

    上述代码的逻辑没有任何问题,但是这样做,JWT会被重复解析两次(一次在拦截器中,一次在该方法中)。为避免重复解析,通常会在拦截器将Token解析完毕后,将结果保存至ThreadLocal中,这样一来,我们便可以在整个请求的处理流程中进行访问了。

ThreadLocal概述

ThreadLocal的主要作用是为每个使用它的线程提供一个独立的变量副本,使每个线程都可以操作自己的变量,而不会互相干扰,其用法如下图所示。

在这里插入图片描述

创建本地线程的工具类:

public class LoginUserHolder {/*创建本地线程*/public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();/*获取本地线程资源*/public static LoginUser getLoginUser() {return threadLocal.get();}/*添加本地线程的资源*/public static void setThreadLocal(LoginUser loginUser) {threadLocal.set(loginUser);}/*清除线程占用的资源*/public static void clear() {threadLocal.remove();}
}
  • 修改AuthenticationInterceptor拦截器
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {/*获取jwt*/String accessToken = request.getHeader("access-token");/*解析jwt*/Claims claims = JwtUtil.parseJwt(accessToken);/*获取个人信息对象*/LoginUser loginUser = new LoginUser(claims.get("userName", String.class), claims.get("userId", Long.class));/*添加本地线程资源*/LoginUserHolder.setThreadLocal(loginUser);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {/*释放线程资源*/LoginUserHolder.clear();}
}

调用本地线程的属性:

Long userId = LoginUserHolder.getLoginUser().getUserId();
String userName = LoginUserHolder.getLoginUser().getUserName();

7、Mybatis-Plus分页插件注意事项

使用Mybatis-Plus的分页插件进行分页查询时,如果结果需要使用<collection>
进行映射,只能使用 嵌套查询(Nested Select for Collection),而不能使用
嵌套结果映射(Nested Results for Collection)。
嵌套查询
嵌套结果映射**是Collection映射的两种方式,下面通过一个案例进行介绍
例如有room_infograph_info两张表,其关系为一对多,如下

知识点

  • xml文件<>的转义
    由于xml文件中的<>是特殊符号,需要转义处理。
原符号转义符号
<&lt;
>&gt;

在这里插入图片描述

现需要查询房间列表及其图片信息,期望返回的结果如下

[{"id": 1,"number": 201,"rent": 2000,"graphList": [{"id": 1,"url": "http://","roomId": 1},{"id": 2,"url": "http://","roomId": 1}]},{"id": 2,"number": 202,"rent": 3000,"graphList": [{"id": 3,"url": "http://","roomId": 2},{"id": 4,"url": "http://","roomId": 2}]}
]

为得到上述结果,可使用以下两种方式

  • 嵌套结果映射
<select id="selectRoomPage" resultMap="RoomPageMap">select ri.id room_id,ri.number,ri.rent,gi.id graph_id,gi.url,gi.room_idfrom room_info rileft join graph_info gi on ri.id=gi.room_id
</select><resultMap id="RoomPageMap" type="RoomInfoVo" autoMapping="true"><id column="room_id" property="id"/><collection property="graphInfoList" ofType="GraphInfo" autoMapping="true"><id column="graph_id" property="id"/></collection>
</resultMap>

这种方式的执行原理如下图所示

在这里插入图片描述

  • 嵌套查询
<select id="selectRoomPage" resultMap="RoomPageMap">select id,number,rentfrom room_info
</select><resultMap id="RoomPageMap" type="RoomInfoVo" autoMapping="true"><id column="id" property="id"/><collection property="graphInfoList" ofType="GraphInfo" select="selectGraphByRoomId" 				 	column="id"/>
</resultMap><select id="selectGraphByRoomId" resultType="GraphInfo">select id,url,room_idfrom graph_infowhere room_id = #{id}
</select>

这种方法使用两个独立的查询语句来获取一对多关系的数据。首先,
Mybatis会执行主查询来获取room_info列表,然后对于每个room_info
Mybatis都会执行一次子查询来获取其对应的graph_info

在这里插入图片描述
若现在使用MybatisPlus的分页插件进行分页查询,
假如查询的内容是第1页,每页2条记录,
则上述两种方式的查询结果分别是

  • 嵌套结果映射
    在这里插入图片描述
  • 嵌套查询
    在这里插入图片描述
    显然嵌套结果映射的分页逻辑是存在问题的。

8、异步调用(@Async)

当执行一个业务是只需要后台内部完成的操作,不需要响应给前端(列如保存浏览记录),
便可以进行异步调用从而提升用户体验

异步调用本质

  1. 非阻塞执行:主线程提交任务后立即返回,不等待任务完成
  2. 线程池管理:任务由线程池中的工作线程执行
  3. 结果处理:通过 Future 或回调机制获取执行结果

实现原理

主线程 线程池 Worker线程 提交异步任务 立即返回Future/void 分配任务 执行任务 返回结果(可选) 主线程 线程池 Worker线程

使用示例

启动异步调用
@SpringBootApplication
/*支持异步调用*/
@EnableAsync
public class MainApp {public static void main(String[] args) {SpringApplication.run(MainApp.class, args);}
}
声明异步处理的方法(@Async)
@Override
/*异步调用*/
@Async
public void saveHistory(Long userId, Long roomId) {
//  ...
}

此时当主线程调用此方法时便不会等待此方法执行完再返回而是直接返回。


本文档和代码以上传gitee上:https://gitee.com/banhuayue/shangting-apartment.git
大家可以直接克隆。
本文档参考《尚硅谷-尚庭公寓》


http://www.hkcw.cn/article/DBIYKTAOtI.shtml

相关文章

字符串~~~

字符串~~ KMP例题1.无线传输2.删除字符串3.二叉树中的链表 AC自动机Manacher例题 扩展KMP字符串哈希 KMP &#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09; 经典例题 https://leetcode.cn/problems/find-the-index-of-the-first-occurre…

WEB3——简易NFT铸造平台之nft.storage

&#x1f9e0; 1. nft.storage 是什么&#xff1f; https://nft.storage 是 一个免费的去中心化存储平台&#xff0c;由 Filecoin 背后的 Protocol Labs 推出。 它的作用是&#xff1a; ✅ 接收用户上传的文件&#xff08;图片、JSON 等&#xff09; ✅ 把它们永久存储到 IPFS…

MCP架构全解析:从核心原理到企业级实践

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

注销微软账户

若你需要注销微软账号&#xff0c;请点击下方超链接。 点击此处

华为OD机试真题——生成哈夫曼树(2025A卷:100分)Java/python/JavaScript/C/C++/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《生成…

Python实现P-PSO优化算法优化BP神经网络分类模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 随着人工智能技术的快速发展&#xff0c;神经网络在分类任务中展现了强大的性能。BP&#xff08;Back Propagation&…

学习海康VisionMaster之表面缺陷滤波

一&#xff1a;进一步学习了 今天学习下VisionMaster中的表面缺陷滤波&#xff1a;简单、无纹理背景的表面缺陷检测&#xff0c;可以检测表面的异物&#xff0c;缺陷&#xff0c;划伤等 二&#xff1a;开始学习 1&#xff1a;什么表面缺陷滤波&#xff1f; 表面缺陷滤波的核心…

34.x64汇编写法(一)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;33.第二阶段x64游戏实战-InLineHook 首先打开 Visual Studio&#xff0c;然后创…

Java网络编程实战:TCP/UDP Socket通信详解与高并发服务器设计

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 内容&#xff1a; socket(套接字)TCP和UDP差别UDP编程方法使用简单服务器实现 TCP编程方法Socket和ServerSocket之间的关系使用简…

算法:滑动窗口

1.长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 运用滑动窗口&#xff08;同向双指针&#xff09;来解决&#xff0c;因为这些数字全是正整数&#xff0c;在left位置确定的下&#xff0c;right这个总sum会越大&#xff0c;所以我们先让num…

AI笔记 - 网络模型 - mobileNet

网络模型 mobileNet mobileNet V1网络结构深度可分离卷积空间可分![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 参考 mobileNet V1 网络结构 MobileNetV1可以理解为VGG中的标准卷积层换成深度可分离卷积 可分离卷积主要有…

新中地三维GIS开发智慧城市效果和应用场景

近年来&#xff0c;随着科技的发展和城市化进程的加速&#xff0c;智慧城市成为了全球各大城市的一个重要发展方向。 在这一背景下&#xff0c;三维GIS技术以其独特的优势&#xff0c;成为构建智慧城市不可或缺的工具。新中地GIS开发特训营正是在这样的大环境下应运而生&#…

Linux笔记---线程

1. 线程的介绍 1.1 线程的概念 基本定义&#xff1a; 线程&#xff08;Thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程&#xff08;Process&#xff09;之中&#xff08;或者说是进程的一部分、对进程的划分&#xff09;&#xff0c;是进程中的实际…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 一&#xff1a;体系结构 二&#xff…

antddesign使用iconfont的字体库和图标库

antddesign使用iconfont 使用iconfont自定义字体 1️⃣选择一种需要的字体&#xff0c;点击【字体包下载】&#xff1a; 2️⃣下载好的字体放到项目目录下&#xff1a;src/assets/fonts&#xff1a; 3️⃣新建styles/font.css文件&#xff1a; /* src/styles/fonts.css */ f…

LearnOpenGL-笔记-其十二

今天我们来将LearnOpenGL的高级光照部分彻底完结&#xff1a; Bloom 泛光是一个非常常见的用于改善图像质量的手段&#xff0c;其主要做法就是将某个高亮度区域的亮度向四周发善以实现该区域更亮的视觉效果&#xff08;因为显示器的亮度范围有限&#xff0c;需要通过泛光来体…

第十二节:第一部分:集合框架:概述、Collection集合的常用方法

集合体系结构 Collection集合体系 Collection的常用方法 代码&#xff1a; 代码一&#xff1a;认识Collection体系的特点 package com.itheima.day17_Collection;import java.util.ArrayList; import java.util.HashSet;/* * 目标:认识Collection体系的特点。 * */ public cl…

C++哈希表:unordered系列容器详解

本节目标 1.unordered系列关联式容器 2.底层结构 3.模拟实现 4.哈希的应用 5.海量数据处理面试题 unordered系列关联式容器 在c98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可以达到logN&#xff0c;即最差的情况下需要比较红…

非常有趣的桌面萌宠互动软件

软件介绍 这里要介绍的软件是一款在主播直播领域十分实用的萌系插件&#xff0c;它能让主播的直播更具趣味性和吸引力。 软件开发者与特性 该软件由国外高中生kuroni开发&#xff0c;是一款开源软件。其最大的亮点在于&#xff0c;能让手鼓猫的手臂跟随鼠标和按键操作做出相…

InfluxQL 数据分析实战:聚合、过滤与关联查询全解析

InfluxQL 作为时序数据库的专用查询语言&#xff0c;在处理时间序列数据时展现出独特优势。本文深入探讨 聚合计算、数据过滤和跨测量关联 三大核心操作&#xff0c;通过真实代码示例展示如何从海量时序数据中提取关键洞察。文中涵盖从基础平均值计算到复杂多维度分析的完整流程…