springboot使用hibernate validator校验

article/2025/10/6 21:26:26

目录

  • 一、参数校验
  • 二、hibernate validator校验demo
  • 三、hibernate的校验模式
    • 1、普通模式(默认是这个模式)
    • 2、快速失败返回模式
  • 四、hibernate的两种校验
    • 1、请求参数校验
    • 2、GET参数校验(@RequestParam参数校验)
    • 3、model校验
    • 4、对象级联校验
    • 5、分组校验
  • 五、自定义验证器
  • 六、常见的注解
  • 七、参考资料

一、参数校验

 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有两个麻烦:

  • 验证代码繁琐,重复劳动
  • 方法内代码显得冗长
  • 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码

hibernate validator(官方文档)提供了一套比较完善、便捷的验证实现方式。

spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。

二、hibernate validator校验demo

 先来看一个简单的demo,添加了Validator的注解:

import org.hibernate.validator.constraints.NotBlank;import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.Pattern;
@Getter
@Setter
@NoArgsConstructor
public class DemoModel {@NotBlank(message="用户名不能为空")private String userName;@NotBlank(message="年龄不能为空")@Pattern(regexp="^[0-9]{1,2}$",message="年龄不正确")private String age;@AssertFalse(message = "必须为false")private Boolean isFalse;/*** 如果是空,则不校验,如果不为空,则校验*/@Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正确")private String birthday;
}

POST接口验证,BindingResult是验证不通过的结果集合:

    @RequestMapping("/demo2")public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){if(result.hasErrors()){for (ObjectError error : result.getAllErrors()) {System.out.println(error.getDefaultMessage());}}}

POST请求传入的参数:{"userName":"dd","age":120,"isFalse":true,"birthday":"21010-21-12"}

输出结果:

出生日期格式不正确
必须为false
年龄不正确

 参数验证非常方便,字段上注解+验证不通过提示信息即可代替手写一大堆的非空和字段限制验证代码。下面深入了解下参数校验的玩法。

三、hibernate的校验模式

细心的读者肯定发现了:上面例子中一次性返回了所有验证不通过的集合,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。Hibernate Validator有以下两种验证模式:

1、普通模式(默认是这个模式)

  普通模式(会校验完所有的属性,然后返回所有的验证失败信息)

2、快速失败返回模式

  快速失败返回模式(只要有一个验证失败,则返回)

两种验证模式配置方式:(参考官方文档)

failFast:true  快速失败返回模式    false 普通模式 

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ).configure().failFast( true ).buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

和 (hibernate.validator.fail_fast:true  快速失败返回模式    false 普通模式)

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ).configure().addProperty( "hibernate.validator.fail_fast", "true" ).buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

四、hibernate的两种校验

配置hibernate Validator为快速失败返回模式:

@Configuration
public class ValidatorConfiguration {@Beanpublic Validator validator(){ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ).configure().addProperty( "hibernate.validator.fail_fast", "true" ).buildValidatorFactory();Validator validator = validatorFactory.getValidator();return validator;}
}

1、请求参数校验

如demo里示例的,验证请求参数时,在@RequestBody DemoModel demo之间加注解 @Valid,然后后面加BindindResult即可;多个参数的,可以加多个@Valid和BindingResult,如:

public void test()(@RequestBody @Valid DemoModel demo, BindingResult result)

public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)

    @RequestMapping("/demo2")public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){if(result.hasErrors()){for (ObjectError error : result.getAllErrors()) {System.out.println(error.getDefaultMessage());}}}

2、GET参数校验(@RequestParam参数校验)

使用校验bean的方式,没有办法校验RequestParam的内容,一般在处理Get请求(或参数比较少)的时候,会使用下面这样的代码:

    @RequestMapping(value = "/demo3", method = RequestMethod.GET)public void demo3(@RequestParam(name = "grade", required = true) int grade,@RequestParam(name = "classroom", required = true) int classroom) {System.out.println(grade + "," + classroom);}

使用@Valid注解,对RequestParam对应的参数进行注解,是无效的,需要使用@Validated注解来使得验证生效。如下所示:

a.此时需要使用MethodValidationPostProcessor 的Bean:

    @Beanpublic MethodValidationPostProcessor methodValidationPostProcessor() {/**默认是普通模式,会返回所有的验证不通过信息集合*/return new MethodValidationPostProcessor();}

或 可对MethodValidationPostProcessor 进行设置Validator(因为此时不是用的Validator进行验证,Validator的配置不起作用)

    @Beanpublic MethodValidationPostProcessor methodValidationPostProcessor() {MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();/**设置validator模式为快速失败返回*/postProcessor.setValidator(validator());return postProcessor;}@Beanpublic Validator validator(){ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ).configure().addProperty( "hibernate.validator.fail_fast", "true" ).buildValidatorFactory();Validator validator = validatorFactory.getValidator();return validator;}

b.方法所在的Controller上加注解@Validated

@RequestMapping("/validation")
@RestController
@Validated
public class ValidationController {/**如果只有少数对象,直接把参数写到Controller层,然后在Controller层进行验证就可以了。*/@RequestMapping(value = "/demo3", method = RequestMethod.GET)public void demo3(@Range(min = 1, max = 9, message = "年级只能从1-9")@RequestParam(name = "grade", required = true)int grade,@Min(value = 1, message = "班级最小只能1")@Max(value = 99, message = "班级最大只能99")@RequestParam(name = "classroom", required = true)int classroom) {System.out.println(grade + "," + classroom);}
}

c.返回验证信息提示

可以看到:验证不通过时,抛出了ConstraintViolationException异常,使用同一捕获异常处理:

@ControllerAdvice
@Component
public class GlobalExceptionHandler {@ExceptionHandler@ResponseBody@ResponseStatus(HttpStatus.BAD_REQUEST)public String handle(ValidationException exception) {if(exception instanceof ConstraintViolationException){ConstraintViolationException exs = (ConstraintViolationException) exception;Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();for (ConstraintViolation<?> item : violations) {/**打印验证不通过的信息*/System.out.println(item.getMessage());}}return "bad request, " ;}
}

d.验证

浏览器服务请求地址:http://localhost:8080/validation/demo3?grade=18&classroom=888

没有配置快速失败返回的MethodValidationPostProcessor 时输出信息如下:

年级只能从1-9
班级最大只能99

配置了快速失败返回的MethodValidationPostProcessor 时输出信息如下:

年级只能从1-9

浏览器服务请求地址:http://localhost:8080/validation/demo3?grade=0&classroom=0

没有配置快速失败返回的MethodValidationPostProcessor 时输出信息如下:

年级只能从1-9
班级最小只能1

配置了快速失败返回的MethodValidationPostProcessor 时输出信息如下:

年级只能从1-9

3、model校验

待校验的model:

@Data
public class Demo2 {@Length(min = 5, max = 17, message = "length长度在[5,17]之间")private String length;/**@Size不能验证Integer,适用于String, Collection, Map and arrays*/@Size(min = 1, max = 3, message = "size在[1,3]之间")private String age;@Range(min = 150, max = 250, message = "range在[150,250]之间")private int high;@Size(min = 3,max = 5,message = "list的Size在[3,5]")private List<String> list;
}

验证model,以下全部验证通过:

    @Autowiredprivate Validator validator;@RequestMapping("/demo3")public void demo3(){Demo2 demo2 = new Demo2();demo2.setAge("111");demo2.setHigh(150);demo2.setLength("ABCDE");demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}});Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2);for (ConstraintViolation<Demo2> model : violationSet) {System.out.println(model.getMessage());}}

4、对象级联校验

对象内部包含另一个对象作为属性,属性上加@Valid,可以验证作为属性的对象内部的验证:(验证Demo2示例时,可以验证Demo2的字段)

@Data
public class Demo2 {@Size(min = 3,max = 5,message = "list的Size在[3,5]")private List<String> list;@NotNull@Validprivate Demo3 demo3;
}@Data
public class Demo3 {@Length(min = 5, max = 17, message = "length长度在[5,17]之间")private String extField;
}
级联校验:
    /**前面配置了快速失败返回的Bean*/@Autowiredprivate Validator validator;@RequestMapping("/demo3")public void demo3(){Demo2 demo2 = new Demo2();demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}});Demo3 demo3 = new Demo3();demo3.setExtField("22");demo2.setDemo3(demo3);Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2);for (ConstraintViolation<Demo2> model : violationSet) {System.out.println(model.getMessage());}}
可以校验Demo3的extField字段。

5、分组校验

结论:分组顺序校验时,按指定的分组先后顺序进行验证,前面的验证不通过,后面的分组就不行验证。

有这样一种场景,新增用户信息的时候,不需要验证userId(因为系统生成);修改的时候需要验证userId,这时候可用用户到validator的分组验证功能。

设置validator为普通验证模式("hibernate.validator.fail_fast", "false"),用到的验证GroupA、GroupB和model:

GroupA、GroupB:

public interface GroupA {
}public interface GroupB {
}

验证model:Person 

 View Code

如上Person所示,3个分组分别验证字段如下:

  • GroupA验证字段userId;
  • GroupB验证字段userName、sex;
  • Default验证字段age(Default是Validator自带的默认分组)

a、分组

只验证GroupA、GroupB标记的分组:

@RequestMapping("/demo5")
public void demo5(){Person p = new Person();/**GroupA验证不通过*/p.setUserId(-12);/**GroupA验证通过*///p.setUserId(12);p.setUserName("a");p.setAge(110);p.setSex(5);Set<ConstraintViolation<Person>> validate = validator.validate(p, GroupA.class, GroupB.class);for (ConstraintViolation<Person> item : validate) {System.out.println(item);}
}

    @RequestMapping("/demo6")public void demo6(@Validated({GroupA.class, GroupB.class}) Person p, BindingResult result){if(result.hasErrors()){List<ObjectError> allErrors = result.getAllErrors();for (ObjectError error : allErrors) {System.out.println(error);}}}

GroupA、GroupB、Default都验证不通过的情况:

验证信息如下所示:

ConstraintViolationImpl{interpolatedMessage='必须在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必须在[4,20]'}
ConstraintViolationImpl{interpolatedMessage='必须大于0', propertyPath=userId, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必须大于0'}
ConstraintViolationImpl{interpolatedMessage='性别必须在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性别必须在[0,2]'}

GroupA验证通过、GroupB、Default验证不通过的情况:

验证信息如下所示:

ConstraintViolationImpl{interpolatedMessage='必须在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必须在[4,20]'}
ConstraintViolationImpl{interpolatedMessage='性别必须在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性别必须在[0,2]'}

b、组序列

除了按组指定是否验证之外,还可以指定组的验证顺序,前面组验证不通过的,后面组不进行验证:

指定组的序列(GroupA》GroupB》Default):

@GroupSequence({GroupA.class, GroupB.class, Default.class})
public interface GroupOrder {
}

测试demo:

    @RequestMapping("/demo7")public void demo7(){Person p = new Person();/**GroupA验证不通过*///p.setUserId(-12);/**GroupA验证通过*/p.setUserId(12);p.setUserName("a");p.setAge(110);p.setSex(5);Set<ConstraintViolation<Person>> validate = validator.validate(p, GroupOrder.class);for (ConstraintViolation<Person> item : validate) {System.out.println(item);}}

    @RequestMapping("/demo8")public void demo8(@Validated({GroupOrder.class}) Person p, BindingResult result){if(result.hasErrors()){List<ObjectError> allErrors = result.getAllErrors();for (ObjectError error : allErrors) {System.out.println(error);}}}

GroupA、GroupB、Default都验证不通过的情况:

验证信息如下所示:

ConstraintViolationImpl{interpolatedMessage='必须大于0', propertyPath=userId, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必须大于0'}

GroupA验证通过、GroupB、Default验证不通过的情况:

验证信息如下所示:

ConstraintViolationImpl{interpolatedMessage='必须在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必须在[4,20]'}
ConstraintViolationImpl{interpolatedMessage='性别必须在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性别必须在[0,2]'}

结论:分组顺序校验时,按指定的分组先后顺序进行验证,前面的验证不通过,后面的分组就不行验证。

 

五、自定义验证器

一般情况,自定义验证可以解决很多问题。但也有无法满足情况的时候,此时,我们可以实现validator的接口,自定义自己需要的验证器。

如下所示,实现了一个自定义的大小写验证器:

public enum CaseMode {UPPER,LOWER;
}@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {String message() default "";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};CaseMode value();
}public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {private CaseMode caseMode;public void initialize(CheckCase checkCase) {this.caseMode = checkCase.value();}public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {if (s == null) {return true;}if (caseMode == CaseMode.UPPER) {return s.equals(s.toUpperCase());} else {return s.equals(s.toLowerCase());}}
}

要验证的Model:

    public class Demo{@CheckCase(value = CaseMode.LOWER,message = "userName必须是小写")private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}}

validator配置:

    @Beanpublic Validator validator(){ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ).configure().addProperty( "hibernate.validator.fail_fast", "true" ).buildValidatorFactory();Validator validator = validatorFactory.getValidator();return validator;}

验证测试:

    @RequestMapping("/demo4")public void demo4(){Demo demo = new Demo();demo.setUserName("userName");Set<ConstraintViolation<Demo>> validate = validator.validate(demo);for (ConstraintViolation<Demo> dem : validate) {System.out.println(dem.getMessage());}}

输出结果:

userName必须是小写

 

六、常见的注解

  Bean Validation 中内置的 constraint     
@Null   被注释的元素必须为 null     
@NotNull    被注释的元素必须不为 null     
@AssertTrue     被注释的元素必须为 true     
@AssertFalse    被注释的元素必须为 false     
@Min(value)     被注释的元素必须是一个数字,其值必须大于等于指定的最小值     
@Max(value)     被注释的元素必须是一个数字,其值必须小于等于指定的最大值     
@DecimalMin(value)  被注释的元素必须是一个数字,其值必须大于等于指定的最小值     
@DecimalMax(value)  被注释的元素必须是一个数字,其值必须小于等于指定的最大值     
@Size(max=, min=)   被注释的元素的大小必须在指定的范围内     
@Digits (integer, fraction)     被注释的元素必须是一个数字,其值必须在可接受的范围内     
@Past   被注释的元素必须是一个过去的日期     
@Future     被注释的元素必须是一个将来的日期     
@Pattern(regex=,flag=)  被注释的元素必须符合指定的正则表达式     
Hibernate Validator 附加的 constraint     
@NotBlank(message =)   验证字符串非null,且长度必须大于0     
@Email  被注释的元素必须是电子邮箱地址     
@Length(min=,max=)  被注释的字符串的大小必须在指定的范围内     
@NotEmpty   被注释的字符串的必须非空     
@Range(min=,max=,message=)  被注释的元素必须在合适的范围内

//大于0.01,不包含0.01
@NotNull
@DecimalMin(value = "0.01", inclusive = false)
private Integer greaterThan;//大于等于0.01
@NotNull
@DecimalMin(value = "0.01", inclusive = true)
private BigDecimal greatOrEqualThan;@Length(min = 1, max = 20, message = "message不能为空")
//不能将Length错用成Range
//@Range(min = 1, max = 20, message = "message不能为空")
private String message;

 

七、参考资料

参考资料:

  • http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-gettingstarted

https://www.cnblogs.com/mr-yang-localhost/p/7812038.html

 

 


http://chatgpt.dhexx.cn/article/6MGTVKCP.shtml

相关文章

Validator 使用总结

介绍 首先说下大家常用的hibernate-validator&#xff0c;它是对JSR-303/JSR-349标准的实现&#xff0c;然后spring为了给开发者提供便捷集成了hibernate-validator&#xff0c;默认在springmvc模块。 依赖 本文所介绍皆在springboot应用的基础上&#xff0c;首先加上web模块…

浅谈 Android Tombstone(墓碑日志)分析步骤

最近项目产品刚刚出货&#xff0c;客户退机、死机事件频发。日常解决bug中&#xff0c;少不了和墓碑日志打交道&#xff0c;截止今天之前&#xff0c;见到墓碑日志都是一脸懵逼&#xff0c;不知道怎么分析。最近又有了两个日志&#xff0c;硬着头皮看吧。之所以称之为浅谈&…

Android tombstone文件是如何生成的

本节内容我们聚焦到androidQ上&#xff0c;分析android中一个用于debug的功能&#xff0c;那就是tombstone&#xff0c;俗称“墓碑”。现实生活中墓碑一般是给死人准备的&#xff0c;而在android系统中“墓碑”则是给进程准备的。 为何Android要设计出这样一个东西呢&#xff…

【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 获取 tombstone_0X 崩溃日志信息 )

文章目录 一、崩溃信息描述二、手机命令行操作三、电脑命令行操作四、Tombstone 内容 Tombstone 报错信息日志文件被保存在了 /data/tombstones/ 目录下 , 先 ROOT 再说 , 没有 ROOT 权限无法访问该目录中的信息 ; 使用 Pixel 2 手机进行调试 , 其它 ROOT 后的手机也可以使用 …

Android tombstone 分析案例

Android tombstone 分析案例 tombstone文件内容1. 体系结构2. 发生Crash线程3. 原因4. 寄存器状态4.1 处理器工作模式下的寄存器4.2 未分组寄存器r0 – r74.3 分组寄存器r8 – r144.4 程序计数器pc(r15)4.5 程序状态寄存器4.6 ARM参数规则 5. 回溯栈6. 程序栈7. 寄存器地址附近…

RocksDB Tombstone 详解

目录 为什么会有墓碑&#xff1f; 使用场景 原理 描述 分段 查询 优化点 总结 为什么会有墓碑&#xff1f; 我们知道 TP 数据库一般选择 KV 引擎作为存储引擎&#xff0c;数据库的元数据和数据通过一定的编码规则变成 KV 对存储在存储引擎中&#xff0c;比如 CockroachD…

Tombstone 文件分析

Tombstone 文件分析 /* * 下面信息是dropbox负责添加的 **/ isPrevious: true Build: Rock/odin/odin:7.1.1/NMF26F/1500868195:user/dev-keys Hardware: msm8953 Revision: 0 Bootloader: unknown Radio: unknown Kernel: Linux version 3.18.31-perf-g34cb3d1 (smartcmhardc…

android Tombstone 流程

一 总述 下面是一份dump 的log&#xff1a; 810 876 I system_server: libdebuggerd_client: started dumping process 678 740 740 I /system/bin/tombstoned: registered intercept for pid 678 and type kDebuggerdNativeBacktrace 678 678 I libc : Requested du…

android tombstone log分析

今天和大家一起聊聊android 中出现的 Tombstone问题&#xff0c;近期在定制pad 上分析设备概率性重启&#xff0c;导出bugreport日志后&#xff0c;除了看到anr log外&#xff0c;同级目录下还看到了tombstones 并且对比以往日志&#xff0c;发现都生产了大量tombstone...,于是…

深入学习tombstone和signal

三驾马车&#xff08;CPU&#xff0c;内存和存储设备&#xff09;中&#xff0c;跑得最慢的就是存储设备了 电脑上&#xff0c;从HDD 到SSD&#xff0c;从SATA SSD到PCIe SSD&#xff0c;硬盘是越来越快&#xff1b; 手机上&#xff0c;从SD卡&#xff0c;到eMMC卡&#xff0…

tombstone

1.什么是tombstone 当一个动态库&#xff08;native 程序&#xff09;开始执行时&#xff0c;系统会注册一些连接到 debuggerd 的 signal handlers&#xff0c;当系统 crash 的时候&#xff0c;会保存一个 tombstone 文件到/data/tombstones目录下&#xff08;Logcat中也会有相…

Tombstone原理分析

本文主要围绕三个问题对tombstone进行分析和介绍&#xff0c;debuggerd是如何监控进程并生成tombstone的&#xff1f;tombstone文件中的信息都是什么&#xff0c;是怎么获取的&#xff1f;tombstone文件应该怎么分析&#xff1f; 一、Tombstone简介 当一个native程序开始执行时…

【date】Linux date命令修改时间的问题

Linux date命令修改时间的问题 问题路径找原因解决方法 问题 Android10&#xff1b;高通平台 使用下面date命令修改时间日期&#xff0c;时分秒生效&#xff0c;年月日不生效 > date -D YYYY-MM-DD hh:mm:ss 路径 \android\external\toybox\toys\posix\date.c \android\e…

i2ctools工具移植到android(使用NDK方式 在某android平台测试)

前提条件 主板i2c已在设备树配置status和引脚复用正常&#xff0c;即设备的i2c总线达到正常使用条件I2C device interface假设内核已配置进去 编译工具链NDK环境搭建 下载NDK 下载地址点我解压 ~/workspace/ndk$ ls android-ndk-r22b android-ndk-r22b-linux-x86_64.zip …

高通平台 Android9 adb shell “hwclock -w“ 报错

hwclock -w 报错 文章目录 hwclock -w 报错问题现象分析1. hwclock命令分析2. /dev/rtc0驱动节点分析 修改设备树后hwclock -w报错没有了&#xff0c;但是系统会重启&#xff0c;原因未知 问题现象 sdm660_64:/ # hwclock -w hwclock: ioctl 4024700a: Invalid argument分析 …

Android top命令、ps命令、busybox命令

top命令 usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]Show process activity in real time.-H Show threads -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID) -o Show FIELDS (def PID,USER,PR,N…

OpenHarmony啃论文俱乐部—盘点开源鸿蒙引用的三方开源软件[1]

目录这里写自定义目录标题 OpenHarmony third_party三方库&#xff1a;学术研究和参与开源的结合third_party_openh264third_party_ninjathird_party_gnthird_party_markupsafethird_party_toyboxthird_party_gstreamerthird_party_ffmpegthird_party_mtdevthird_party_flutter…

Android缺少awk工具的几种解决方法

在日常测试中&#xff0c;我们会用到各种各样的Android平台&#xff0c;用于测试存储设备的性能。其中&#xff0c;我们依赖到Android平台自身的工具&#xff0c;通过编写shell脚本来实现测试存储设备的性能。   而awk工具(shell命令)在shell脚本中会经常用到&#xff0c;一般…

toybox 和 busybox 的作用

来自知乎&#xff1a;程序员秘书 ##前言## 我们在做android开发时&#xff0c;经常会有在板子系统里要修改文件内容对比验证问题&#xff0c;或者要操作特殊的shell命令操作看些信息&#xff0c;等等一些需求。但是往往会因为刷到板子的系统里默认没有/不支持相关的命令&…

欢乐听:一个简洁的瀑布流模式的音乐分享站

欢乐听 一个简洁的瀑布流模式的音乐分享站。