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…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...

Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...