Spring中的数据校验--进阶
分组校验
场景描述
在实际开发中经常会遇到这种情况:添加用户时,id是由后端生成的,不需要校验id是否为空,但是修改用户时就需要校验id是否为空。如果在接收参数的User实体类的id属性上添加NotNull
,显然无法实现。这时候就可以定义分组,在需要校验id的时候校验,不需要的时候不校验。
定义分组
校验的分组通过接口的形式定义。
代码准备
/*** @author lihz* @date 2023/2/18*/
@RestController
@RequestMapping("/group/validation/")
public class GroupValidationController {@PostMapping("/insert")public String testInsert(@RequestBody @Validated({UserValidGroup.Insert.class}) UserInfo userInfo) {System.out.println(userInfo);return "OK";}@PostMapping("/update")public String testUpdate(@RequestBody @Validated({UserValidGroup.Update.class}) UserInfo userInfo) {System.out.println(userInfo);return "OK";}@PostMapping("/delete")public String testDelete(@RequestBody @Validated({UserValidGroup.Delete.class}) UserInfo userInfo) {System.out.println(userInfo);return "OK";}
}@Data
class UserInfo {@Min(value = 1, message = "ID不能小于1", groups = {UserValidGroup.Delete.class, UserValidGroup.Update.class})private int id;@NotBlank(message = "用户名不能为空", groups = {UserValidGroup.Update.class, UserValidGroup.Insert.class})private String username;@NotBlank(message = "密码不能为空", groups = {UserValidGroup.Update.class, UserValidGroup.Insert.class})@Length(min = 8, max = 20, message = "密码长度在8-20之间", groups = {UserValidGroup.Update.class, UserValidGroup.Insert.class})private String password;
}class UserValidGroup {public interface Insert {}public interface Update {}public interface Delete {}@GroupSequence({Insert.class, Update.class, Delete.class})public interface All {}
}
数据准备
insert测试
{"id": null,"username": "demon","password": "123456"
}
输出:
{"code": 1,"msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.GroupValidationController.testInsert(com.jurassic.cloud.project.controller.UserInfo): [Field error in object 'userInfo' on field 'password': rejected value [12345]; codes [Length.userInfo.password,Length.password,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.password,password]; arguments []; default message [password],20,8]; default message [密码长度在8-20之间]] ","data": null
}
注意:没有校验 id 属性。
update测试
{"id": null,"username": "demon","password": "123456"
}
输出:
{"code": 1,"msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.GroupValidationController.testUpdate(com.jurassic.cloud.project.controller.UserInfo) with 2 errors: [Field error in object 'userInfo' on field 'id': rejected value [0]; codes [Min.userInfo.id,Min.id,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.id,id]; arguments []; default message [id],1]; default message [ID不能小于1]] [Field error in object 'userInfo' on field 'password': rejected value [123456]; codes [Length.userInfo.password,Length.password,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.password,password]; arguments []; default message [password],20,8]; default message [密码长度在8-20之间]] ","data": null
}
注意:校验了 id 属性。
delete测试
{"id": null,"username": "demon","password": "123456"
}
输出:
{"code": 1,"msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.GroupValidationController.testDelete(com.jurassic.cloud.project.controller.UserInfo): [Field error in object 'userInfo' on field 'id': rejected value [0]; codes [Min.userInfo.id,Min.id,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.id,id]; arguments []; default message [id],1]; default message [ID不能小于1]] ","data": null
}
注意:仅验证 id 属性。
总结
如果未指定分组,则是Default
组,不属于Default
组的属性不会验证。
指定了分组,则仅验证指定的分组涉及的约束。
分组的高级特性
见其他JSR 380文档。
i18n
在进行约束声明时,会指定message
属性,用于设置约束校验失败之后的提示,如果需要支持多语言,则不能得到期望的结果。
不指定message属性
如果不指定message,则会采用框架的默认值,会提供主流的语言,
框架解析message默认值的相关逻辑在 org.hibernate.validator.internal.engine.ValidationContext
中
private String interpolate(String messageTemplate,Object validatedValue,ConstraintDescriptor<?> descriptor,Map<String, Object> messageParameters,Map<String, Object> expressionVariables) {MessageInterpolatorContext context = new MessageInterpolatorContext(descriptor,validatedValue,getRootBeanClass(),messageParameters,expressionVariables);try {//使用 MessageInterpolator 解析return validatorScopedContext.getMessageInterpolator().interpolate(messageTemplate,context); }catch (ValidationException ve) {throw ve;}catch (Exception e) {throw LOG.getExceptionOccurredDuringMessageInterpolationException( e );}}
在约束定义时,会设置message
的默认值,是个消息插值。例如:@NotNull
,{javax.validation.constraints.NotNull.message}
,定义了消息参数,在Resource Bundle
:ValidationMessages
中作为Key获取 获取属性值。
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {String message() default "{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Documented@interface List {NotNull[] value();}
}
ValidationMessages.properties
:
javax.validation.constraints.NotBlank.message = must not be blank
javax.validation.constraints.NotEmpty.message = must not be empty
javax.validation.constraints.NotNull.message = must not be null
javax.validation.constraints.Null.message = must be null
AbstractMessageInterpolator
消息解析主要是通过MessageInterpolator
的实现类,默认都继承AbstractMessageInterpolator
。 此类默认会根据JVM的Locale来获取对应的i18n消息(源码中的 defaultLocale = Locale.getDefault()
),不能根据request传递来的Locale来显示对应的消息。
缺陷
只能显示JVM的locale对应的消息。
自定义MessageInterpolator
自定义一个 MessageInterpolator
实现并改写其第一个interpolate
方法,可以根据request传递的locale进行动态显示。
@Configuration
public class I18nConstrainValidator {@Beanpublic Validator validator() {return Validation.byDefaultProvider().configure().messageInterpolator(new ParameterMessageInterpolator() {@Overridepublic String interpolate(String message, Context context) {return interpolate(message, context, Locale.getDefault());}@Overridepublic String interpolate(String message, Context context, Locale locale) {// 获取当前请求所指定的语言对应的LocaleLocale requestLocale = I18nUtil.getLocaleFromCurrentRequest();if (null == requestLocale) {requestLocale = locale;}return super.interpolate(message, context, requestLocale);}}).buildValidatorFactory().getValidator();}}
缺陷
当request指定了一种框架中不存在的语种时无法得到准确的对应语种的消息而是得到JVM Locale
对应的消息。
语种不存在时会获取Locale.getDefault()
对应的Locale
ResourceBundleMessageInterpolator
@Slf4j
@Configuration
public class ConstrainValidatorConfig {@Value("${spring.messages.basename}")private String[] baseNames;@Beanpublic Validator validator() {return Validation.byDefaultProvider().configure().messageInterpolator(new RequesLocaleAwareMessageInterpolator(// 提供AggregateResourceBundleLocator使得除了用框架提供的Validation ConstrainViolation// Message外,还可以用自己指定的或覆盖框架提供的。new AggregateResourceBundleLocator(Arrays.asList(baseNames)))).buildValidatorFactory().getValidator();}/*** 自定义ResourceBundleMessageInterpolator的若干方法,使得可根据request指定的语言返回对应语种的Validation* ConstrainViolation Message*/public static class RequesLocaleAwareMessageInterpolator extends ResourceBundleMessageInterpolator {public RequesLocaleAwareMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) {super(userResourceBundleLocator);}@Overridepublic String interpolate(String message, Context context) {return interpolate(message, context, Locale.getDefault());}@Overridepublic String interpolate(String message, Context context, Locale locale) {// 获取当前请求所指定的语言对应的LocaleLocale requestLocale = LocaleContextHolder.getLocale();log.debug("locale for javax.validation.Validator resolved: {}", requestLocale);if (null == requestLocale) {requestLocale = locale;}return super.interpolate(message, context, requestLocale);}}}
若注解的message
未指定——即用的是框架默认值(如 {javax.validation.constraints.Size.message}
),则对于框架未提供的i18n语种(如 zh_CHS),在你自己项目的i18n文件里补充相应值即可(如在messages_zh_CHS.properties
文件里增加 javax.validation.constraints.Size.message = 长度不能超过{max}
);
若注解的message不用默认值,而是自己指定message,如 message=“{custom.constraints.Size.message.name}
” ,则在你自己项目的i18n文件里补充相应值即可(如 custom.constraints.Size.message.name = 姓名的长度不能超过{max}
)。此时,注解的message仍支持EL表达式。实际使用中推荐用此方案,因为这种方案不仅支持EL表达式、i18n消息、还支持返回可直接弹窗显示给用户的i18n字段名。
设置的properties文件,可以不叫
ValidationMessages
,可以是任何文件名。
设置语言的方式
1、设置header: Accept-Language
,基于 AcceptHeaderLocaleResolver
实现
2、设置session / cookie:基于 CookieLocaleResolver
,SessionLocaleResolver
实现。自定义参数的名称,需要用到LocaleChangeInterceptor
(需要启用,默认参数名为locale
),用于监控哪个属性(可自定义)切换语言。
public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE";
public static final String LOCALE_REQUEST_ATTRIBUTE_NAME = CookieLocaleResolver.class.getName() + ".LOCALE";
CookieLocaleResolver
,会把locale放到cookie中,cookieName:org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE
3、固定locale。FixedLocaleResolver
。
spring.mvc.locale=zh_CN
//或者
spring:web:locale: zh_CNlocale-resolver: fixed
spring.web.locale-resolver
优先级比spring.mvc.locale-resolver
高一些。
spring.web.locale
、spring.mvc.locale
这两个配置属性,假如存在,就会成为AcceptHeaderLocaleResolver
的默认的Locale 区域对象。 并在请求响应的请求头中没有Accept-Language
这个属性时,成为AcceptHeaderLocaleResolver
返回的Locale 区域对象。
Spring实现原理
Spring会在启动时通过AOP
对使用@Validated
或@Valid
的类或其子类的对象生成一个代理对象。在代理对象中调用目标hanlder
方法前后会分别进行参数、返回值的JSR校验。
MethodValidationPostProcessor
切面创建的相关逻辑在MethodValidationPostProcessor
。
//org.springframework.validation.beanvalidation
public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessorimplements InitializingBean {private Class<? extends Annotation> validatedAnnotationType = Validated.class;@Nullableprivate Validator validator;public void setValidatedAnnotationType(Class<? extends Annotation> validatedAnnotationType) {Assert.notNull(validatedAnnotationType, "'validatedAnnotationType' must not be null");this.validatedAnnotationType = validatedAnnotationType;}public void setValidator(Validator validator) {// Unwrap to the native Validator with forExecutables supportif (validator instanceof LocalValidatorFactoryBean) {this.validator = ((LocalValidatorFactoryBean) validator).getValidator();}else if (validator instanceof SpringValidatorAdapter) {this.validator = validator.unwrap(Validator.class);}else {this.validator = validator;}}public void setValidatorFactory(ValidatorFactory validatorFactory) {this.validator = validatorFactory.getValidator();}//此方法在bean自身初始化时会创建一个DefaultPointcutAdvisor用于向符合条件的对象添加进行方法验证的AOP advise@Overridepublic void afterPropertiesSet() {Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));}//如果有 protected Advice createMethodValidationAdvice(@Nullable Validator validator) {return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());}}
MethodValidationPostProcessor
实现了接口BeanPostProcessor
定义的方法postProcessAfterInitialization
(从父类AbstractAdvisingBeanPostProcessor
继承),该方法会检查每个bean的创建(在该bean初始化之后),如果检测到该bean符合条件,会向其增加上述AOP advise
。
MethodValidationPostProcessor
是被ValidationAutoConfiguration
自动配置到IoC容器的。
ValidationAutoConfiguration
package org.springframework.boot.autoconfigure.validation;@Configuration@ConditionalOnClass(ExecutableValidator.class)@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")@Import(PrimaryDefaultValidatorPostProcessor.class)public class ValidationAutoConfiguration {@Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE)@ConditionalOnMissingBean(Validator.class)public static LocalValidatorFactoryBean defaultValidator() {LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();factoryBean.setMessageInterpolator(interpolatorFactory.getObject());return factoryBean;}// 向容器注册一个 bean MethodValidationPostProcessor @Bean@ConditionalOnMissingBeanpublic static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator) {MethodValidationPostProcessor processor = new MethodValidationPostProcessor();boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);processor.setProxyTargetClass(proxyTargetClass);processor.setValidator(validator);return processor;}}
MethodValidationInterceptor
切面中进行参数验证、返回值验证的相关逻辑在MethodValidationInterceptor
。
@Override
@SuppressWarnings("unchecked")
public Object invoke(MethodInvocation invocation) throws Throwable {// Avoid Validator invocation on FactoryBean.getObjectType/isSingletonif (isFactoryBeanMetadataMethod(invocation.getMethod())) {return invocation.proceed();}//获取对哪些组进行校验Class<?>[] groups = determineValidationGroups(invocation);// Standard Bean Validation 1.1 APIExecutableValidator execVal = this.validator.forExecutables();Method methodToValidate = invocation.getMethod();Set<ConstraintViolation<Object>> result;try {result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups);}catch (IllegalArgumentException ex) {// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011// Let's try to find the bridged method on the implementation class...methodToValidate = BridgeMethodResolver.findBridgedMethod(ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups);}if (!result.isEmpty()) {throw new ConstraintViolationException(result);}Object returnValue = invocation.proceed();result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);if (!result.isEmpty()) {throw new ConstraintViolationException(result);}return returnValue;
}
附录
Spring MVC localeResolver
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(localeChangeInterceptor());}/*** Cookie方式** @return*/@Beanpublic LocaleResolver localeResolver() {return new CookieLocaleResolver();}/*** 切换语言按钮URL?language=zh_CN,切换后将语言信息存入cookie;** @return*/@Beanpublic LocaleChangeInterceptor localeChangeInterceptor() {LocaleChangeInterceptor lci = new LocaleChangeInterceptor();//不设置,默认为locale。lci.setParamName("language");return lci;}
}
相关文章:

Spring中的数据校验--进阶
分组校验 场景描述 在实际开发中经常会遇到这种情况:添加用户时,id是由后端生成的,不需要校验id是否为空,但是修改用户时就需要校验id是否为空。如果在接收参数的User实体类的id属性上添加NotNull,显然无法实现。这时…...

多种方法解决谷歌(chrome)、edge、火狐等浏览器F12打不开调试页面或调试模式(面板)的问题。
文章目录1. 文章引言2. 解决问题3. 解决该问题的其他方法1. 文章引言 不论是前端开发者,还是后端开发者,我们在调试web项目时,偶尔弹出相关错误。 此时,我们需要打开浏览器的调试模式,如下图所示: 通过浏…...
默认生成的接口实现方法体的问题
随着集成开发环境越来越强大,编程开发工作也变得越来越高效,很多的代码都不需要逐字输入,可以利用代码生成和自动补全来辅助开发。但是这样的便利也可能引起一些疏忽,本文就Java开发中默认生成的接口实现方法来谈谈以前遇到的问题…...

【OJ】十级龙王间的决斗
📚Description: 在《驯龙高手2》,最精彩的高潮出现在两只阿尔法决斗的时候。 驯龙高手中的十星龙王又称喷冰龙,有且只有两只,是最大型的龙,所有其他龙都要膜拜它(当然,幼龙除外)&…...

java 自定义注解
文章目录前言Annotation包自定义注解自定义注解示例参考文章:java 自定义注解 用处_java注解和自定义注解的简单使用参考文章:java中自定义注解的作用和写法前言 在使用Spring Boot的时候,大量使用注解的语法去替代XML配置文件,十…...
产品经理知识体系:2.如何进行商业需求分析?
商业需求分析 思考 笔记 用户细分: 核心用户、用户分级 用户关系: 如何维护用户关系、维护等成本 关系和商业模式的整合 核心价值: 解决什么问题,满足什么需求,最终带给用户什么价值 渠道通道: 如何触达…...
EditPlus正则表达式替换字符串详解
正则表达式是一个查询的字符串,它包含一般的字符和一些特殊的字符,特殊字符可以扩展查找字符串的能力,正则表达式在查找和替换字符串的作用不可忽视,它能很好提高工作效率。EditPlus的查找,替换,文件中查找…...

Go基础-环境安装
文章目录1 Go?Golang?2 下载Go3 windows安装4 测试是否成功1 Go?Golang? Go也称为Golang,是Google开发的一个开源的编译型的静态语言。 Golang的主要关注点是高可用、高并发和高扩展性,Go语言定位是系统级编程语言,对web程序具有很好的支…...
《NFL橄榄球》:纽约巨人·橄榄1号位
纽约巨人(New York Giants)是美国全国橄榄球联盟在新泽西州东卢瑟福的一支球队。巨人是在1925年作为五个成员之一加入国家美式橄榄球联盟。 在2018年时,球队市值为33亿美元,在世界前50名球队中并列第8名,同时在NFL高居…...

2023/02/18 ES6数组的解读
1 扩展运算符 扩展运算符(spread)是三个点(…). 它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列. console.log(...[1, 2, 3]) // 1 2 3console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5该运算符主要用于…...

Ubuntu 20 安装包下载(清华镜像)
Ubuntu 20 安装包下载在国内推荐使用清华大学镜像 清华镜像地址:https://mirrors.tuna.tsinghua.edu.cn/ 在搜索框中输入Ubuntu,然后点击Ubuntu -release,这里面有近几年的Ubuntu镜像 点击你想下载的版本,我选择的是20.0413点击…...

华为OD机试 - 机器人走迷宫(JS)
机器人走迷宫 题目 房间有X*Y的方格组成,例如下图为6*4的大小。每一个放个以坐标(x,y)描述。 机器人固定从方格(0,0)出发,只能向东或者向北前进, 出口固定为房间的最东北角,如下图的方格(5,3)。 用例保证机器人可以从入口走到出…...

字节二面:10Wqps超高流量系统,如何设计?
超高流量系统设计思路 前言 在40岁老架构师 尼恩的**读者交流群(50)**中,大流量、高并发的面试题是一个非常、非常高频的交流话题。最近,有小伙伴面试字节时,遇到一个面试题: 10Wqps超高流量系统,该如何设计…...

基于springboot+html汽车维修系统汽车维修系统的设计与实现
基于springboothtml汽车维修系统汽车维修系统的设计与实现 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式…...

营销狂人杜国楹的两大顶级思维
“营销狂人”小罐茶 杜国楹两大顶级思维 1.一定要有【参照物思维】 2.一定要有【终局思维】 趣讲大白话:大牛的思考就是不同 *********** 杜国楹对茶行业思考 1.参照咖啡、酒的发展路径 2.中国茶工业化,品牌化是唯一壮大之路 3.龙头企业必须全品 没有参照物思维就没…...
面试题-前端开发JavaScript篇下(答案超详细)
文章目录 实现一个 once 函数,传入函数参数只执行一次将原生的 ajax 封装成 promisJS 监听对象属性的改变如何实现一个私有变量,用 getName 方法可以访问,不能直接访问==和===、以及 Object.is 的区别setTimeout、setInterval 和 requestAnimationFrame 之间的区别实现一个两…...
Android 9.0 修改Recovery字体图片的大小(正在清理)文字大小
1.概述 在9.0的系统产品定制化开发中,在系统中recovery功能也是非常重要的功能,所以说在进行recovery的时候,正在清理的 字体显示的有些小了,所以产品需求要求改大recovery的字体大小,所以这就需要在recovery页面看下字体大小的显示逻辑然后修改字体的显示大小,主要功能修…...
操作系统 五(文件系统)
一 文件定义:文件是指由创建者所定义的,具有文件名的一组相关元素的集合,可分为有结构文件和无结构文件两类。在有结构文件中,文件由若干个相关记录组成。而无结构文件则被看成一个字节流。文件在文件系统中是一个最大的数据单位&…...
华为OD机试 - 人数最多的站点(JS)
人数最多的站点 题目 公园园区提供小火车单向通行,从园区站点编号最小到最大, 通行如1~2~3~4~1万,然后供员工在各个办公园区穿梭, 通过对公司N个员工调研统计到每个员工的坐车区间,包含前后站点, 请设计一个程序计算出小火车在哪个园区站点时人数最多。 输入 输入的第…...

Mr. Cappuccino的第41杯咖啡——Kubernetes之Pod调度策略
Kubernetes之Pod调度策略Pod的4种调度策略定向调度nodeNamenodeSelector亲和性调度node亲和性硬限制软限制关系运算符pod亲和性pod反亲和性污点和容忍污点(taints)容忍(tolerations)默认情况下,Scheduler计算出一个Pod…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...