小架構step系列29:校驗注解的組合

1 概述

如果遇到某些屬性需要多種校驗,比如需要非空、符合某正則表達式、長度不能超過某值等,如果這種屬性只有有限幾個,那么手工把對應的校驗注解都加上即可。但如果這種屬性比較多,那么重復加這些校驗注解,也是一種代碼重復。

hibernate-validator包提供了一種組合注解的方式,來解決上面場景的問題。

2 原理

2.1 例子

先來看一個組合校驗注解的例子:

1) 定義一個新注解@FormatedString

import org.hibernate.validator.constraints.CompositionType;
import org.hibernate.validator.constraints.ConstraintComposition;import javax.validation.Constraint;
import javax.validation.OverridesAttribute;
import javax.validation.Payload;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@ConstraintComposition(CompositionType.AND)
@NotEmpty
@Pattern(regexp = "")
@Size
@Constraint(validatedBy = {})
public @interface FormatedString {String message() default "{string.formated.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };@OverridesAttribute(constraint = Size.class, name = "min")int min() default 0;@OverridesAttribute(constraint = Size.class, name = "max")int max() default Integer.MAX_VALUE;@OverridesAttribute(constraint = Pattern.class, name = "regexp")String regexp() default "";
}

從上面看,@FormatedString注解的上方指定了非空(@NotEmpty)、正則表達式(@Pattern)、長度限制(@Size)這三個常用的的校驗注解。@Constraint里面的validatedBy值為空,代表著自身沒有提供ContraintValidator。@ConstraintComposition指定了多個注解校驗結果的關系是AND,也就是都需要校驗通過則總結果才算通過。

注意:使用@OverridesAttribute把參數傳給原注解,這不屬于Spring環境,不能使用Spring的@AliasFor來傳參數。@Pattern的regexp屬性沒有給默認值,需要給一個默認值,否則無法通過編譯。

2) 使用注解

// 以O開頭且后面都是數字,長度在16-20之間
@FormatedString(regexp = "O[0-9]+", min = 16, max = 20)
private String orderNumber;

2.2 處理流程

詳細的流程需要參考前面的文章,這里只給出涉及處理組合注解的部分。

// 源碼位置:org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl
public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,Constrainable constrainable,ConstraintAnnotationDescriptor<T> annotationDescriptor,ConstraintLocationKind constraintLocationKind,Class<?> implicitGroup,ConstraintOrigin definedOn,ConstraintType externalConstraintType) {this.annotationDescriptor = annotationDescriptor;this.constraintLocationKind = constraintLocationKind;this.definedOn = definedOn;this.isReportAsSingleInvalidConstraint = annotationDescriptor.getType().isAnnotationPresent(ReportAsSingleViolation.class);// 省略部分代碼// 1. 當把校驗注解的信息和屬性封裝為ConstraintDescriptorImpl時,還會解析一下校驗注解里是否還帶其它校驗注解。//    annotationDescriptor為校驗注解(如@NotNull),constrainable為屬性字段信息(如標了@NotNull注解的mobilePhone屬性字段),constraintType為GENERIC。//    返回結果賦值給了composingConstraints。this.composingConstraints = parseComposingConstraints( constraintHelper, constrainable, constraintType );this.compositionType = parseCompositionType( constraintHelper );validateComposingConstraintTypes();if ( constraintType == ConstraintType.GENERIC ) {this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( genericValidatorDescriptors );}else {this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( crossParameterValidatorDescriptors );}this.hashCode = annotationDescriptor.hashCode();
}// 源碼位置:org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl
private Set<ConstraintDescriptorImpl<?>> parseComposingConstraints(ConstraintHelper constraintHelper, Constrainable constrainable, ConstraintType constraintType) {Set<ConstraintDescriptorImpl<?>> composingConstraintsSet = newLinkedHashSet();Map<ClassIndexWrapper, Map<String, Object>> overrideParameters = parseOverrideParameters();Map<Class<? extends Annotation>, ComposingConstraintAnnotationLocation> composingConstraintLocations = new HashMap<>();// 2. annotationDescriptor為校驗注解(如@NotNull),從注解上獲取標注的其它注解for ( Annotation declaredAnnotation : this.annotationDescriptor.getType().getDeclaredAnnotations() ) {Class<? extends Annotation> declaredAnnotationType = declaredAnnotation.annotationType();// 3. NON_COMPOSING_CONSTRAINT_ANNOTATIONS為@Target、@Retention等常用注解,它們與校驗無關,過濾掉if ( NON_COMPOSING_CONSTRAINT_ANNOTATIONS.contains( declaredAnnotationType.getName() ) ) {continue;}// 4. 判斷注解是否為校驗注解,內置的注解和加了@Constraint的注解才是校驗注解if ( constraintHelper.isConstraintAnnotation( declaredAnnotationType ) ) {if ( composingConstraintLocations.containsKey( declaredAnnotationType )&& !ComposingConstraintAnnotationLocation.DIRECT.equals( composingConstraintLocations.get( declaredAnnotationType ) ) ) {throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(), declaredAnnotationType );}// 5. 為每個校驗注解也生成一個ConstraintDescriptorImplConstraintDescriptorImpl<?> descriptor = createComposingConstraintDescriptor(constraintHelper,constrainable,overrideParameters,OVERRIDES_PARAMETER_DEFAULT_INDEX,declaredAnnotation,constraintType);// 6. 生成的ConstraintDescriptorImpl,放到composingConstraintsSet里面,該Set座位返回值賦值給了this.composingConstraintscomposingConstraintsSet.add( descriptor );composingConstraintLocations.put( declaredAnnotationType, ComposingConstraintAnnotationLocation.DIRECT );LOG.debugf( "Adding composing constraint: %s.", descriptor );}else if ( constraintHelper.isMultiValueConstraint( declaredAnnotationType ) ) {List<Annotation> multiValueConstraints = constraintHelper.getConstraintsFromMultiValueConstraint( declaredAnnotation );int index = 0;for ( Annotation constraintAnnotation : multiValueConstraints ) {if ( composingConstraintLocations.containsKey( constraintAnnotation.annotationType() )&& !ComposingConstraintAnnotationLocation.IN_CONTAINER.equals( composingConstraintLocations.get( constraintAnnotation.annotationType() ) ) ) {throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(),constraintAnnotation.annotationType() );}ConstraintDescriptorImpl<?> descriptor = createComposingConstraintDescriptor(constraintHelper,constrainable,overrideParameters,index,constraintAnnotation,constraintType);composingConstraintsSet.add( descriptor );composingConstraintLocations.put( constraintAnnotation.annotationType(), ComposingConstraintAnnotationLocation.IN_CONTAINER );LOG.debugf( "Adding composing constraint: %s.", descriptor );index++;}}}return CollectionHelper.toImmutableSet( composingConstraintsSet );
}
private static final List<String> NON_COMPOSING_CONSTRAINT_ANNOTATIONS = Arrays.asList(Documented.class.getName(),Retention.class.getName(),Target.class.getName(),Constraint.class.getName(),ReportAsSingleViolation.class.getName(),Repeatable.class.getName(),Deprecated.class.getName()
);// 源碼位置:org.hibernate.validator.internal.metadata.core.MetaConstraint
MetaConstraint(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl<A> constraintDescriptor,ConstraintLocation location, List<ContainerClassTypeParameterAndExtractor> valueExtractionPath,Type validatedValueType) {// 7. 在把找到的校驗注解(如@NotNull)和屬性字段封裝為MetaConstraint時,要確定ConstraintTree的實際實現類,//    ConstraintTree的實現有兩種,一種是對應單個校驗注解的SimpleConstraintTree,另外一種是對應多個校驗注解的ComposingConstraintTreethis.constraintTree = ConstraintTree.of( constraintValidatorManager, constraintDescriptor, validatedValueType );this.location = location;this.valueExtractionPath = getValueExtractionPath( valueExtractionPath );this.hashCode = buildHashCode( constraintDescriptor, location );this.isDefinedForOneGroupOnly = constraintDescriptor.getGroups().size() <= 1;
}// 源碼位置:org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree
public static <U extends Annotation> ConstraintTree<U> of(ConstraintValidatorManager constraintValidatorManager,ConstraintDescriptorImpl<U> composingDescriptor, Type validatedValueType) {// 8. composingDescriptor.getComposingConstraintImpls()獲取到的就是上面的composingConstraints//    當校驗注解上還加了其它校驗注解,則composingConstraints不為空,//    composingConstraints為空則創建的是SimpleConstraintTree,否則創建的是ComposingConstraintTreeif ( composingDescriptor.getComposingConstraintImpls().isEmpty() ) {return new SimpleConstraintTree<>( constraintValidatorManager, composingDescriptor, validatedValueType );}else {return new ComposingConstraintTree<>( constraintValidatorManager, composingDescriptor, validatedValueType );}
}
// 源碼位置:org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl
public Set<ConstraintDescriptorImpl<?>> getComposingConstraintImpls() {return composingConstraints;
}// 實際校驗的時候會調ConstraintTree的validateConstraints()方法,ComposingConstraintTree重載了父類ConstraintTree的validateConstraints()方法
// 源碼位置:org.hibernate.validator.internal.engine.constraintvalidation.ComposingConstraintTree
protected void validateConstraints(ValidationContext<?> validationContext,ValueContext<?, ?> valueContext,Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {// 9. 校驗CompositionResult compositionResult = validateComposingConstraints(validationContext, valueContext, violatedConstraintValidatorContexts);Optional<ConstraintValidatorContextImpl> violatedLocalConstraintValidatorContext;if ( mainConstraintNeedsEvaluation( validationContext, violatedConstraintValidatorContexts ) ) {if ( LOG.isTraceEnabled() ) {LOG.tracef("Validating value %s against constraint defined by %s.",valueContext.getCurrentValidatedValue(),descriptor);}ConstraintValidator<B, ?> validator = getInitializedConstraintValidator( validationContext, valueContext );ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(descriptor, valueContext.getPropertyPath());violatedLocalConstraintValidatorContext = validateSingleConstraint(valueContext,constraintValidatorContext,validator);if ( !violatedLocalConstraintValidatorContext.isPresent() ) {compositionResult.setAtLeastOneTrue( true );}else {compositionResult.setAllTrue( false );}}else {violatedLocalConstraintValidatorContext = Optional.empty();}if ( !passesCompositionTypeRequirement( violatedConstraintValidatorContexts, compositionResult ) ) {prepareFinalConstraintViolations(validationContext, valueContext, violatedConstraintValidatorContexts, violatedLocalConstraintValidatorContext);}
}// 源碼位置:org.hibernate.validator.internal.engine.constraintvalidation.ComposingConstraintTree
private CompositionResult validateComposingConstraints(ValidationContext<?> validationContext,ValueContext<?, ?> valueContext,Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {CompositionResult compositionResult = new CompositionResult( true, false );// 10. 當校驗注解上還加了其它校驗注解,其它校驗注解每個也對應生成了一個處理單個校驗注解的SimpleConstraintTree//     children就是SimpleConstraintTree列表for ( ConstraintTree<?> tree : children ) {List<ConstraintValidatorContextImpl> tmpConstraintValidatorContexts = new ArrayList<>( 5 );// 11. 把注解當一個普通的校驗注解完成校驗邏輯tree.validateConstraints( validationContext, valueContext, tmpConstraintValidatorContexts );violatedConstraintValidatorContexts.addAll( tmpConstraintValidatorContexts );if ( tmpConstraintValidatorContexts.isEmpty() ) {compositionResult.setAtLeastOneTrue( true );if ( descriptor.getCompositionType() == OR ) {break;}}else {compositionResult.setAllTrue( false );if ( descriptor.getCompositionType() == AND&& ( validationContext.isFailFastModeEnabled() || descriptor.isReportAsSingleViolation() ) ) {break;}}}return compositionResult;
}// 回到ComposingConstraintTree的validateConstraints繼續處理
// 源碼位置:org.hibernate.validator.internal.engine.constraintvalidation.ComposingConstraintTree
protected void validateConstraints(ValidationContext<?> validationContext,ValueContext<?, ?> valueContext,Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {// 9. 校驗CompositionResult compositionResult = validateComposingConstraints(validationContext, valueContext, violatedConstraintValidatorContexts);Optional<ConstraintValidatorContextImpl> violatedLocalConstraintValidatorContext;// 12. 如果校驗注解本身也指定了校驗器,那么它本身也需要當一個普通校驗注解進行校驗if ( mainConstraintNeedsEvaluation( validationContext, violatedConstraintValidatorContexts ) ) {if ( LOG.isTraceEnabled() ) {LOG.tracef("Validating value %s against constraint defined by %s.",valueContext.getCurrentValidatedValue(),descriptor);}ConstraintValidator<B, ?> validator = getInitializedConstraintValidator( validationContext, valueContext );ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(descriptor, valueContext.getPropertyPath());violatedLocalConstraintValidatorContext = validateSingleConstraint(valueContext,constraintValidatorContext,validator);if ( !violatedLocalConstraintValidatorContext.isPresent() ) {compositionResult.setAtLeastOneTrue( true );}else {compositionResult.setAllTrue( false );}}else {violatedLocalConstraintValidatorContext = Optional.empty();}// 13. 有多個校驗結果的時候,需要確定一下總結果是什么if ( !passesCompositionTypeRequirement( violatedConstraintValidatorContexts, compositionResult ) ) {prepareFinalConstraintViolations(validationContext, valueContext, violatedConstraintValidatorContexts, violatedLocalConstraintValidatorContext);}
}// 源碼位置:org.hibernate.validator.internal.engine.constraintvalidation.ComposingConstraintTree
private boolean passesCompositionTypeRequirement(Collection<?> constraintViolations, CompositionResult compositionResult) {// 14. 有三種關系:一是OR,只要有一個校驗通過即可;二是AND,需要所有都校驗通過;三是ALL_FALSE,所有校驗都不通過。//     如果不指定CompositionType,那么默認是ANDCompositionType compositionType = getDescriptor().getCompositionType();boolean passedValidation = false;switch ( compositionType ) {case OR:passedValidation = compositionResult.isAtLeastOneTrue();break;case AND:passedValidation = compositionResult.isAllTrue();break;case ALL_FALSE:passedValidation = !compositionResult.isAtLeastOneTrue();break;}assert ( !passedValidation || !( compositionType == AND ) || constraintViolations.isEmpty() );if ( passedValidation ) {constraintViolations.clear();}return passedValidation;
}

在找校驗注解的時候,把關聯的注解也當普通的校驗注解處理(封裝到ConstraintDescriptorImpl對象里),處理結果放到一個Set中。在校驗的時候,先處理這個Set的內容,處理流程和普通的校驗注解一樣;處理完之后再處理當前校驗注解本身,處理流程和普通的校驗注解一樣。

由此可以看出,在設計的時候,不管是內置的校驗注解,還是自定義的校驗注解,原理都是一致的,只有初始化的地方不一樣;同樣,組合校驗注解也會分解成當前注解和關聯的校驗注解,分解之后也是普通的注解,也是在初始化的時候和校驗開始時分解的地方不一樣,其它的都是一樣的。這個組合的設計思路值得學習。

3 架構一小步

針對需要一起組合使用的校驗注解,利用hibernate-validator包提供的組合機制自定義一個新校驗注解把它們組合起來,方便使用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91417.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91417.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91417.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡基礎19:OSPF多區域實驗

一、拓撲結構1. 網絡拓撲&#xff1a;骨干區域&#xff08;Area 0&#xff09;&#xff1a;連接核心設備&#xff08;AR1、AR2、AR3、AR4、AR5、AR6&#xff09;。非骨干區域&#xff1a;Area 1&#xff1a;AR5 ? AR9Area 2&#xff1a;AR5 ? AR10Area 3&#xff1a;AR6 ? A…

goland編寫go語言導入自定義包出現: package xxx is not in GOROOT (/xxx/xxx) 的解決方案

問題 寫了個自定義的包 calc.go&#xff0c;在路徑 $GOPATH/go_project/src/demo_51_package/com/目錄下&#xff0c;其中main.go 是main方法的入口代碼 main.go 代碼如下 package main import "demo_51_package/com" func main() {add : calc.Add(1, 2)println(add)…

HLS視頻切片音頻中斷問題分析與解決方案

HLS視頻切片音頻中斷問題分析與解決方案 問題背景 在使用FFmpeg進行HLS視頻切片并通過hls.js前端播放時&#xff0c;開發者經常遇到一個典型問題&#xff1a;第一個視頻切片播放正常且有聲音&#xff0c;但后續切片卻突然失去音頻。這種現象在直播和點播場景中均有出現&#xf…

【Linux網絡編程】網絡層協議 - IP

目錄 背景補充 協議頭格式 IP報文的分片與組裝 網段劃分 網段劃分是什么&#xff1f;為什么要進行網段劃分&#xff1f; 怎么進行網段劃分&#xff1f; 路由 路由表生成算法 背景補充 假設現在主機B要給主機C發送消息。在我們前面的學習中&#xff0c;一直都是將數據拷…

從“救火”到“先知”:潤建曲尺運維大模型如何重構網絡運維價值鏈

“7月18號&#xff0c;北京&#xff0c;晴&#xff0c;最高溫度38攝氏度。”天氣預報緩緩播報&#xff0c;商場、地鐵、辦公樓無不歌頌著威利斯開利的貢獻&#xff0c;但這份涼爽的背后&#xff0c;離不開 “電” 的無聲托舉。5G毫秒級下載、絲滑的移動支付、智能電表、智能家居…

Element表格單元格類名動態設置

在 Element UI 的 el-table 組件中&#xff0c;cell-class-name 屬性用于動態自定義表格單元格的 CSS 類名&#xff0c;通常用于根據數據條件設置樣式。1. 基本用法在 el-table 上綁定 :cell-class-name 屬性&#xff0c;值為一個函數。該函數接收一個對象參數&#xff0c;返回…

利用容器適配器實現stack和queue外加deque的介紹(STL)

文章目錄前言什么是容器適配器&#xff1f;觀察庫中的源碼那么該如何使用容器適配器呢&#xff1f;deque的簡單介紹(了解)deque的原理介紹deque的優缺為什么選擇deque作為stack和queue的底層默認容器&#xff1f;&#xff08;重點&#xff09;利用容器適配器實現我們自己的棧和…

【因子動物園巡禮】第12章:機器學習在因子投資中的應用(中文翻譯)

【因子動物園巡禮】第12章&#xff1a;機器學習在因子投資中的應用&#xff08;中文翻譯&#xff09;第12章 因子投資中的機器學習12.1 量化金融中的人工智能12.2 量化因子投資的AI化組件&#xff1a;解剖學視角12.2.1 數據源拓展與預處理12.2.2 因子研究12.2.3 因子模型12.2.4…

【Golang】用官方rate包構造簡單IP限流器

文章目錄使用 Go 實現基于 IP 地址的限流機制什么是 IP 限流&#xff1f;基于 rate.Limiter 實現 IP 限流1. 設計思路2. 代碼實現3. 限流中間件4. 在 Gin 中使用中間件代碼解釋使用 Go 實現基于 IP 地址的限流機制 在高流量的服務中&#xff0c;限流是一個至關重要的環節。它不…

力扣 Pandas 挑戰(6)---數據合并

本文圍繞力扣的Pandas簡單題集&#xff0c;解析如何用Pandas完成基礎數據處理任務&#xff0c;適合Pandas初學者學習。題目1&#xff1a;1050. 合作過至少三次的演員和導演題目描述&#xff1a;ActorDirector 表&#xff1a;---------------------- | Column Name | Type | …

隨筆之TDengine基準測試示例

文章目錄一、基本信息二、基準測試策略三、基準測試過程1. 模擬高并發寫入場景2. 模擬并發查詢場景四、基準測試結論一、基本信息 TDengine 版本&#xff1a;3.3.6.13&#xff08;目前最新版本&#xff09;服務器配置&#xff1a;16核CPU&#xff0c;32GB內存&#xff0c;高IO…

【IQA技術專題】DISTS代碼講解

本文是對DISTS圖像質量評價指標的代碼解讀&#xff0c;原文解讀請看DISTS文章講解。 本文的代碼來源于IQA-Pytorch工程。 1、原文概要 以前的一些IQA方法對于捕捉紋理上的感知一致性有所欠缺&#xff0c;魯棒性不足。基于此&#xff0c;作者開發了一個能夠在圖像結構和圖像紋…

2024年SEVC SCI2區,一致性虛擬領航者跟蹤群集算法GDRRT*-PSO+多無人機路徑規劃,深度解析+性能實測

目錄1.摘要2.算法背景3.GDRRT*-PSO與虛擬領航者跟蹤算法4.結果展示5.參考文獻6.算法輔導應用定制讀者交流1.摘要 隨著無人機技術的快速發展及其卓越的運動和機動性能&#xff0c;無人機在社會和軍事等諸多領域得到了廣泛應用。多無人機協同作業&#xff0c;能夠顯著提升任務執…

鏈特異性文庫是什么?為什么它在轉錄組測序中越來越重要?

鏈特異性文庫是什么&#xff1f;為什么它在轉錄組測序中越來越重要&#xff1f; 在現代分子生物學研究中&#xff0c;RNA測序&#xff08;RNA-seq&#xff09; 是一種廣泛應用的技術&#xff0c;用于分析基因在不同條件下的表達情況。而在RNA-seq的眾多技術細節中&#xff0c;有…

ClickHouse vs PostgreSQL:數據分析領域的王者之爭,誰更勝一籌?

文章概要 作為一名數據架構師&#xff0c;我經常被問到一個問題&#xff1a;在眾多數據庫選擇中&#xff0c;ClickHouse和PostgreSQL哪一個更適合我的項目&#xff1f;本文將深入探討這兩種數據庫系統的核心差異、性能對比、適用場景以及各自的優缺點&#xff0c;幫助您在技術選…

面向對象系統的單元測試層次

面向對象系統的單元測試層次面向對象&#xff08;Object-Oriented, OO&#xff09;編程范式引入了封裝、繼承和多態等核心概念&#xff0c;這使得傳統的、基于函數的單元測試方法不再充分。面向對象系統的單元測試必須適應其獨特的結構和行為特性&#xff0c;從單一方法擴展到類…

如何用USRP捕獲手機信號波形(上)系統及知識準備

目錄&#xff1a; 如何用USRP捕獲手機信號波形&#xff08;上&#xff09;系統及知識準備 如何用USRP捕獲手機信號波形&#xff08;中&#xff09;手機/基站通信 如何用USRP捕獲手機信號波形&#xff08;下&#xff09;協議分析 一、手機通信參數獲取 首先用Cellular-z網絡…

C語言-數組:數組(定義、初始化、元素的訪問、遍歷)內存和內存地址、數組的查找算法和排序算法;

本章概述思維導圖&#xff1a;C語言數組在C語言中&#xff0c;數組是一種固定大小的、相同類型元素的有序集合&#xff0c;通過索引&#xff08;下標&#xff09;訪問。數組數組&#xff1a;是一種容器&#xff0c;可以用來存儲同種數據類型的多個值&#xff1b;數組特點&#…

河南萌新聯賽2025第(二)場:河南農業大學(補題)

文章目錄前言A.約數個數和整除分塊(相當于約數求和)相關例題&#xff1a;取模B.異或期望的秘密二進制的規律相關例題累加器小藍的二進制詢問乘法逆元1. 概念2.基本定義3.費馬小定理1.定理內容2.重要推論D.開羅爾網絡的備用連接方案E.咕咕嘎嘎!!!(easy)I.猜數游戲(easy)K.打瓦M.…

常見中間件漏洞

一、TomcatTomcat put方法任意文件寫入漏洞環境搭建&#xff0c;啟動時端口被占用就改yml配置文件&#xff0c;改成8081端口。(我這里是8080)cd vulhub-master/tomcat/CVE-2017-12615 docker-compose up -d 去抓包&#xff0c;改成put提交。下面的內容是用哥斯拉生成的木馬文件…