springMvc自定义参数校验器及基础使用
文章目录
- 学习链接
- 基础使用
- 1.单个参数校验
- 全局异常处理器
- 2.实体类参数校验
- 3.嵌套校验
- 4.分组校验
- 5.自定义校验注解
- 自定义参数校验器
- springmvc参数校验使用方法
- 自定义错误消息模板
- 观察源码
- 自定义错误消息
- 创建ValidationMessages.properties
- 在Controller中使用
- 自定义参数校验器
- CheckEmailRegister
- CheckEmailRegisterValidator
- 在controller中使用
- 自定义枚举传参的校验器
- EmailCodeTypeEnum枚举类
- @Enums校验注解
- EnumsValidator
- 自定义枚举int值校验器
- EnumsType
- EnumsTypeValidator
- 使用示例
学习链接
spring boot mvc Validator 自定义校验(枚举值校验)
SpringBoot 参数校验的方法
Spring Validation最佳实践及其实现原理
注解系列——自定义注解验证器
基础使用
1.单个参数校验
import javax.validation.constraints.NotNull;
@RestController
@RequestMapping("/vadmin/cart")
@Validated // 开启校验(在类上面不能用@Valid,否则下面的校验注解无效)
public class CartController {@RequestMapping("deleteCart")@ResponseBody // 如果为null,这里将会抛出异常,可使用全局异常处理器捕获public Result deleteCart(@NotNull Long userId, @NotNull Long pid){ cartService.deleteCart(userId, pid);return Result.succ(null);}
}
// 普通参数校验(如上),在controller类上加@Validated注解才能生效(不要在类上使用@Valid,否则无效)。
// 如果将@Validated加在方法参数里面,普通参数校验将无效(不会做校验),
// 将@Validated加载controller类上并不会引起实体类的参数校验
// 即(实体类中有属性有校验注解,但是controller的方法参数里面没有写@Validated)
// 如果校验失败, 将会抛出ConstraintViolationException异常
全局异常处理器
@ControllerAdvice
public class MyExceptionHandler {@ExceptionHandler(MyException.class)@ResponseBodypublic Result myExceptionResult(MyException ex){return Result.fail(ex.getMessage());}@ExceptionHandler(Exception.class)@ResponseBodypublic Result exceptionResult(Exception ex){return Result.fail("内部错误: " + ex.getMessage());}}
2.实体类参数校验
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_cart")
public class Cart implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "tc_id", type = IdType.AUTO)private Long tcId;private Long cartId;private Long userId;@NotNull(message = "商品ID不能为空")private Long pid;@Min(value = 1,message = "至少添加一件商品")@NotNull(message = "必须添加商品数量信息")private Long pcount;}@RequestMapping("addToCart")
@ResponseBody // 启用校验 // 校验结果封装 // 后面可以接参数
public Result addToCart(@Validated Cart cart, BindingResult bindingResult,Man man) {if (bindingResult.hasErrors()) {StringBuilder sb = new StringBuilder();bindingResult.getAllErrors().forEach(error->{sb.append(error.getDefaultMessage() + " | ");});throw new MyException(sb.toString());// 处理校验结果,抛出异常,让全局异常处理器处理自定义异常}cartService.addCart(cart);return Result.succ(null);
}
// 实体类校验注意事项
// 1. 必须把@Validated写在controller方法里面,写在类上面无效;
// 2. 一个校验的实体类对应一个BindingResult,否则会抛出MethodArgumentNotValidException异常(全局异常处理器处理)
// 如:(@Validated Person person,BindingResult bindingResult1,
// @Validated Man man, BindingResult bindingResult) 是正确的
// 如:(@Validated Cart cart, BindingResult bindingResult,Man man) 是正确的
// 如:(@Validated Cart cart,Man man, BindingResult bindingResult) 是错误的
// 第2小点总结: 一个@Validated对应一个BindingResult,否则抛出异常
// 3. 如果需要把错误信息配置在配置文件中
// 在springboot的resources目录下新建【ValidationMessages.properties】
// 内容为:person.name.notnull=person.name用户名不能为空
// man.name.notnull=man.name用户名名不能为空
// 之后即可在实体类属性上引用: @NotNull(message = "{man.name.notnull}")
// 4. 如果前面有校验注解,但是后面如果不写BindingResult,controller就会抛出异常;
// 如果写了BindingResult那么就会进controller方法的逻辑
3.嵌套校验
@PostMapping("saveCareOrders") // 这里的@Validated触发对CareOrderFormDto类中的注解校验
public AjaxResult saveCareOrders(@Validated @RequestBody CareOrderFormDto careOrderFormDto) {}@Data
@AllArgsConstructor
@NoArgsConstructor
public class CareOrderFormDto implements Serializable {// 处方@Valid // 在这里触发对CareOrderDto类中的注解校验@NotNull // 如果这里不写@NotNull注解, 只写@Valid注解,那么如果前端如果根本就不传careOrderDto过来,// 那么就不会触发CareOrderDto类中的校验注解,传了的话,就做校验// 所以建议加上@NotNull注解,先触发@NotNull的非空校验,再通过@Valid触发CareOrderDto类中的校验注解private CareOrderDto careOrderDto;// 处方详情@Valid // 在这里触发对CareOrderItemDto类的注解校验@NotEmpty(message = "处方详情不能为空") // 将会触发集合不能为空的校验private List<CareOrderItemDto> careOrderItemDtoList;
}
4.分组校验
1.实体类
@Data
public class Person { // 读取resources目录下的ValidationMessages.properties文件 、归Agroup@NotNull(message = "{person.name.notnull}",groups = {Agroup.class})private String name;@NotNull(message = "{person.age.notnull}",groups = {Bgroup.class})private Integer age;// 不写分组,则默认属于Default.class组@NotNull(message = "address不能为空")private String address;
}
2.校验
@RequestMapping("test04") // 只会校验Agroup相关的
public String test04(@Validated({Agroup.class}) Person person,BindingResult result) {log.info("{},{}",person.getName(),person.getAge());return "ok";
}@RequestMapping("test05") // 只会校验Bgroup相关的
public String test05(@Validated({Bgroup.class}) Person person,BindingResult result) {log.info("{},{}",person.getName(),person.getAge());return "ok";
}@RequestMapping("test06") // 只会校验Agroup、Bgroup相关的
public String test06(@Validated({Agroup.class,Bgroup.class}) Person person,BindingResult result) {log.info("{},{}",person.getName(),person.getAge());return "ok";
}@RequestMapping("test07") // 校验Agroup、Bgroup再加上 没有分组的校验注解【加上默认的Default.class组的】
public String test07(@Validated({Agroup.class,Bgroup.class, Default.class}) Person person, BindingResult result) {log.info("{},{}",person.getName(),person.getAge());return "ok";
}@RequestMapping("test07") // 只会校验默认组的
public String test07(@Validated Person person, BindingResult result) {log.info("{},{}",person.getName(),person.getAge());return "ok";
}
5.自定义校验注解
1.自定义校验用法:
package com.zzhua.controller.validate;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckLenValidator.class)
public @interface CheckLen {String message() default "{字符串长度不符合要求}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};int len();
}public class CheckLenValidator implements ConstraintValidator<CheckLen, String> {private int len;public void initialize(CheckLen constraint) {this.len = constraint.len();}public boolean isValid(String obj, ConstraintValidatorContext context) {return obj != null && len == obj.length();}
}@RestController
@RequestMapping("test")
@Validated // 必须加这个并且加到这个位置(加到下面的test方法中无效, 因为i参数不是实体类; 实体类是加在下面方法中,才能生效// 并且这两种加的方法互不影响)
public class RoleController {@GetMapping("test")public AjaxResult test(@CheckLen(len=3) String i) {return AjaxResult.success("hello");}
}
2.内置的校验注解
// 查看 ConstraintHelper 这个类里面注册了很多 validator
// 这也是为什么内置校验注解没有校验器的原因 validatedBy={}
自定义参数校验器
springmvc参数校验使用方法
我们先总结一下在springMvc使用注解来实现参数校验的方法
-
在Controller接口上添加@Validated,可以触发在此controller类中,所有使用了校验注解的接口
如:@Validated // 添加此注解 @RestController public class LoginController {// 对单个参数的校验@GetMapping("login/captcha")public Result<Object> captcha(@NotNull(message = "type不能为空") Integer type) {return loginService.captcha(type, request,response);}// 对自定义类型参数校验//(注意这里只会校验registerDTO是否为null,而不会触发在EmailRegisterDTO 类上的校验注解校验)//(如果需要触发对EmailRegisterDTO 类上的校验注解校验,那么看下面的嵌套校验使用)@PostMapping("register/email") // 这里只是为了演示效果, 才把RequestBody的required属性置为false的。public Result<Boolean> registerEmail(@NotNull(message="参数不能为空") // 此处触发了对registerDTO实体类的校验@RequestBody(required = false) EmailRegisterDTO registerDTO) {return loginService.registerEmail(registerDTO);}}
-
在Controller类中的某个接口方法中在方法参数的前面加上@Valid或@Validate注解,即可触发该参数所对应的实体类A上的属性上所添加的校验注解。如果这个实体类A上的属性还是个实体类B(我们自己定义的类),那么可以继续在这个实体类A的这个属性(类型为实体类B)上添加@Valid注解,来触发对嵌套的实体类B上的加了校验注解的属性校验,即
嵌套校验
。
如:@Data public class EmailRegisterDTO {@Emailprivate String email;private String emailCode;private String password;private String checkPassword;@NotNull("customVo不能为空")@Valid // 1. 触发嵌套校验, 不使用此注解,将不会触发CustomVo上的类上使用的校验注解的校验// 2. 如果没有@NotNull注解校验,而只有@Valid注解,那就是如果前端传了customVo就校验,没传,那就不校验private CustomVo customVo;@NotEmpty("customVos不能为空")private List<CustomVo> customVos; }@Data public class CustomVo {@NotBlank("attr不能为空")private String attr; }// 需要在参数的前面加上@Valid注解,才能触发EmailRegisterDTO类上的校验注解的校验 @PostMapping("register/email") public Result<Boolean> registerEmail(@Valid @NotNull(message="参数不能为空") @RequestBody(required = false) EmailRegisterDTO registerDTO) {return Result.ok(true); }
自定义错误消息模板
观察源码
我们注意到源码里面,就比如@NotNull这个注解,里面的message使用了大括号,来引用消息内容。错误消息的内容可以到org/hibernate/validator/ValidationMessages_zh_CN.properties文件中找到。
@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 { };/*** Defines several {@link NotNull} annotations on the same element.** @see javax.validation.constraints.NotNull*/@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Documented@interface List {NotNull[] value();}
}
ValidationMessages_zh_CN.properties消息的内容如下,可以看到里面
- 可以使用
{注解的属性}
的方式引用注解里面的属性值, - 可以使用
${validatedValue}
的方式引用待校验的值 - 在
${..}
中可以使用表达式
javax.validation.constraints.AssertFalse.message = 只能为false
javax.validation.constraints.AssertTrue.message = 只能为true
javax.validation.constraints.DecimalMax.message = 必须小于或等于{value}
javax.validation.constraints.DecimalMin.message = 必须大于或等于{value}
javax.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message = 最大不能超过{value}
javax.validation.constraints.Min.message = 最小不能小于{value}
javax.validation.constraints.Negative.message = 必须是负数
javax.validation.constraints.NegativeOrZero.message = 必须是负数或零
javax.validation.constraints.NotBlank.message = 不能为空
javax.validation.constraints.NotEmpty.message = 不能为空
javax.validation.constraints.NotNull.message = 不能为null
javax.validation.constraints.Null.message = 必须为null
javax.validation.constraints.Past.message = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message = 必须是正数
javax.validation.constraints.PositiveOrZero.message = 必须是正数或零
javax.validation.constraints.Size.message = 个数必须在{min}和{max}之间org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message = 不能为空
org.hibernate.validator.constraints.NotEmpty.message = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message = 需要在{min}和{max}之间
org.hibernate.validator.constraints.SafeHtml.message = 可能有不安全的HTML内容
org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message = 需要是一个合法的URLorg.hibernate.validator.constraints.time.DurationMax.message = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
自定义错误消息
创建ValidationMessages.properties
在resources目录下,创建ValidationMessages.properties文件,内容可以如下:
## ${validatedValue}去引用待校验的值,而{min}引用校验注解实例的属性值
a.b.c = ${validatedValue}-{min}这个不该为空的
在类上添加校验注解,并自定义消息模板内容
@Data
public class EmailRegisterDTO {@Length(message = "${validatedValue}不是一个合法的值",min = 3)@NotNull(message = "这个@length不校验null,还得我出马")private String email;// 使用{errorMsgkey} 大括号的形式去引用写在ValidationProperties的模板消息,也可以用来引用校验注解的属性值// ${validatedValue}去引用待校验的值@Length(message = "{a.b.c}-${validatedValue}-${validatedValue.length()}-{min}!!!",min = 3)private String emailCode;}
在Controller中使用
@Validated
@RestController
public class LoginController {@PostMapping("register/email")public Result<Boolean> registerEmail(@Valid @NotNull(message="参数不能为空") @RequestBody(required = false) EmailRegisterDTO registerDTO) {return Result.ok(true);}}
自定义参数校验器
上面这种校验,它只能对某个实体的的单个属性作校验,但是有的时候,一个实体类的2个属性是互相关联的(比如,注册的时候,需要用户输入两次密码,一个为密码,第二个为确认密码,前端做是必要的,但后台也应该要做),这时候,我们只能被迫在业务代码里写了。但是我们可以通过自定义参数校验器实现
CheckEmailRegister
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckEmailRegisterValidator.class)
public @interface CheckEmailRegister {// 可参考 ValidationMessages_zh_CN.properties 文件中的写法,这里可以用el表达式的语法去写逻辑String message() default "${validatedValue}中的${validatedValue.email}不合法,${validatedValue.email==null||validatedValue.email==''?'邮箱不能为空(⊙﹏⊙)':''}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
CheckEmailRegisterValidator
- 通过initialize方法,可以获取校验注解实例,可以方便的获取注解的属性,可以保存下来,以作后面校验使用
- isValid方法返回true或者false,表示是否校验通过,如果校验不通过,将会使用校验注解的message属性作为消息模板(功能同如上所述)
- 可以使用ConstraintValidatorContext提供的api,来传入消息模板,自定义消息错误提示
@Slf4j
public class CheckEmailRegisterValidator implements ConstraintValidator<CheckEmailRegister, EmailRegisterDTO> {@Overridepublic void initialize(CheckEmailRegister anno) {}@Overridepublic boolean isValid(EmailRegisterDTO registerDTO, ConstraintValidatorContext context) {if (registerDTO == null) {return false;}try {// 这里不要给消息模板, 测试一下自定义注解的默认的消息模板if (StringUtils.isEmpty(registerDTO.getEmail()) || registerDTO.getEmail().length() < 2) {return false;}if (StringUtils.isEmpty(registerDTO.getEmailCode())) {return newErrorMsg(context,"邮箱验证码不能为空");}if (StringUtils.isEmpty(registerDTO.getPassword())) {return newErrorMsg(context,"密码不能为空");}if (StringUtils.isEmpty(registerDTO.getCheckPassword())) {return newErrorMsg(context,"密码不能为空");}if (!Objects.equals(registerDTO.getPassword(), registerDTO.getCheckPassword())) {return newErrorMsg(context,"两次输入密码不一致," +"一个是:${validatedValue.password}," +"一个是:${validatedValue.checkPassword}");}} catch (RuntimeException ex) {log.error("校验发生异常: {}", ex.getMessage());return false;}return true;}private boolean newErrorMsg(ConstraintValidatorContext context, String errMsg) {context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(errMsg).addConstraintViolation();return false;}}
在controller中使用
@Validated
@RestController
public class LoginController {@ApiOperation("邮箱注册账号")@PostMapping("web/register/email") // @Controller上加了@Validated注解,触发了@CheckEmailRegister 的校验,// 如果需要触发对registerDTO的类里面的属性上的校验注解的校验,这里需要加上@Validpublic Result<Boolean> registerEmail(@CheckEmailRegister @RequestBody EmailRegisterDTO registerDTO) {return loginService.registerEmail(registerDTO);}
}
自定义枚举传参的校验器
EmailCodeTypeEnum枚举类
@Getter
public enum EmailCodeTypeEnum {REGISTER(1, "registerTpl.ftl"),FIND_PWD(2, "findPwd.ftl"),;private Integer type;private String templateName;EmailCodeTypeEnum(int type, String templateName) {this.type = type;this.templateName = templateName;}public static EmailCodeTypeEnum type(Integer type) {for (EmailCodeTypeEnum emailCodeTypeEnum : EmailCodeTypeEnum.values()) {if (Objects.equals(emailCodeTypeEnum.type, type)) {return emailCodeTypeEnum;}}throw new RuntimeException();}
}
@Enums校验注解
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumsValidator.class)
public @interface Enums {String message() default "非法值:${validatedValue},请传入指定范围内的枚举值";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};// 校验的枚举类Class<?> enumClazz();// 校验的枚举类的属性String enumField();// null是否合法boolean nullValid() default false;}
EnumsValidator
public class EnumsValidator implements ConstraintValidator<Enums, Object> {// 校验的枚举类private Class<?> enumClazz;// 校验的枚举类的属性private Field enumField;private boolean nullValid;@Overridepublic void initialize(Enums enums) {this.nullValid = enums.nullValid();Class<?> enumsClass = enums.enumClazz();if (!enumsClass.isEnum()) {throw new RuntimeException(MessageFormat.format("%s不是枚举类", enums.enumClazz().getName()));}this.enumClazz = enumsClass;try {Field field = enumClazz.getDeclaredField(enums.enumField());field.setAccessible(true);this.enumField = field;} catch (NoSuchFieldException e) {throw new RuntimeException(MessageFormat.format("枚举类%s,无此%s属性", enums.enumClazz().getName(), enumField));}}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {if (value == null) {return nullValid; // null是否合法}for (Object enumConstant : this.enumClazz.getEnumConstants()) {try {if (Objects.equals(enumField.get(enumConstant), value)) {return true;}} catch (IllegalAccessException e) {return false;}}List<Object> enumList = Arrays.stream(this.enumClazz.getEnumConstants()).map(obj -> {try {return enumField.get(obj);} catch (IllegalAccessException e) {throw new RuntimeException("枚举校验发生错误");}}).collect(Collectors.toList());context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("允许的值为: "+ StringUtils.arrayToCommaDelimitedString(enumList.toArray())).addConstraintViolation();return false;}}
自定义枚举int值校验器
EnumsType
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumsTypeValidator.class)
public @interface EnumsType {String message() default "非法值:${validatedValue},请传入指定范围内的枚举值";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};int[] types();// null是否合法boolean nullValid() default false;}
EnumsTypeValidator
public class EnumsTypeValidator implements ConstraintValidator<EnumsType, Integer> {private List<Integer> typeList = new ArrayList<>();private boolean nullValid;@Overridepublic void initialize(EnumsType enums) {int[] types = enums.types();for (int type : types) {typeList.add(type);}Assert.notEmpty(this.typeList, "@EnumsType提供的枚举值不能为空数组");this.nullValid = enums.nullValid();}@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {if (value == null) {return nullValid;}for (Object type : this.typeList) {if (Objects.equals(type, value)) {return true;}}context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("非法值:${validatedValue},请传入" + Arrays.toString(this.typeList.toArray())).addConstraintViolation();return false;}
}
使用示例
@Validated
@RestController
public class LoginController {// 下面的type参数只能传入指定的1,2@GetMapping("login/captcha")public Result<Map<String,Object>> captcha(@EnumsType(types = {1,2}) Integer type) {return loginService.captcha(type, request,response);}
}
相关文章:

springMvc自定义参数校验器及基础使用
文章目录 学习链接基础使用1.单个参数校验全局异常处理器 2.实体类参数校验3.嵌套校验4.分组校验5.自定义校验注解 自定义参数校验器springmvc参数校验使用方法自定义错误消息模板观察源码自定义错误消息创建ValidationMessages.properties 在Controller中使用 自定义参数校验器…...

Java基础之ConcurrentHashMap答非所问
ConcurrentHashMap的数据结构是什么? ConcurrentHashMap仅仅是HashMap的线程安全版本,针对HashMap的线程安全优化,所以HashMap有的特点ConcurrentHashMap同意具有, ConcurrentHashMap的数据结构跟HashMap是一样的。 在JDK7版本使用…...

「锂」清思绪,触达未来 | 锂电池企业如何实现数字化破局?
锂电池制造的困局与破局 锂电池行业产业链的上游主要为正负极材料、电解液、电极基材、隔膜等领域的供应商;中游为电芯制造及封装行业;下游则主要是动力电池、消费电子等锂电池的应用领域。 图1:锂电池行业产业链 近年来,随着相…...

量化散户交易数据:追涨爆亏99%,杀跌少赚28倍?| 追涨杀跌一时爽,散户钱包火葬场?【邢不行】
你第一次炒股的经历是不是这样的: 你有一个朋友,他说在XX股票上大赚了一笔,你听后是既羡慕又不服。 于是你下载了炒股软件,看了眼这只股票,有点心动。但由于没有交易经验,股价又确实涨了不少,…...

Eclipse字体大小设置的5个步骤
Eclipse是一款非常流行的Java集成开发环境(IDE),它被广泛地用于开发Java应用程序。在Eclipse中,字体大小对于用户体验至关重要。因此,了解如何在Eclipse中设置字体大小是非常有用的。 1、打开Eclipse 首先,打开Eclipse IDE。如果E…...

qt creator添加build步骤删除某个文件
参考:https://blog.csdn.net/weixin_44436546/article/details/113587115 1. windows下配置: 添加build步骤;在commad栏输入cmd,会弹出C:\Windows\system32\cmd.exe;在Arguments栏输入/c release\upgrade.o;Working …...

四. AMS源码分析
AMS 主要负责四大组件的启动、切换和调度以及应用程序的管理和调度工作. ActivityManager的组成主要分为以下几个部分: 服务代理: 由ActivityManagerProxy实现,用于与Server端提供的系统服务进行进程间通信服务中枢: ActivityManagerNative,继承自Binder并实现了IActivityMana…...

OpenGL进阶 | 绘制一个三角形
一、准备绘图数据 VBO(Vertex Buffer Object) 在opengl中,所有的数据都要放在显存中,通过VBO(Vertex Buffer Object)可将CPU数据传到GPU。 VBO(Vertex Buffer Object)是OpenGL中的一…...

CSAPP Lab3- bufbomb
实验目标 (1)掌握函数调用时的栈帧结构 (2)利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使得程序执行我们所期望的过程 实验代码 (1)makecookie:生成c…...

分布式系统之CAP定理介绍
前言 在分布式系统的设计和实现中,CAP定理是一个非常重要的概念。本文将介绍CAP定理的概念、含义和应用。 什么是 CAP 定理? CAP定理是分布式系统设计中的一个基本原则,它指出,在分布式系统中,一致性(Consi…...

vcomp140.dll怎么安装?提示vcomp140.dll丢失怎样修复?
在用电脑玩游戏或者打开软件工作的时候,电脑提示vcomp140.dll丢失无法执行此代码,是什么回事呢?需要怎么修复呢?不用紧张,小编今天就把vcomp140.dll文件修复方法分享给大家。我总结了几个修复经验; vcomp14…...

CA OpenSSL自签名证书(服务器/客户端)
参考文章 https://juejin.cn/post/7092789498823573518 https://blog.csdn.net/mengting2040/article/details/120001810 目录 使用 OpenSSL 生成证书创建根证书创建 Root Pair创建 Root Key创建 Root Crt 创建服务器端证书创建服务器端keyip需要换成自己服务器的外网ip地址&am…...

计算机视觉(4)—— 未有深度学习之前
目录 四、未有深度学习之前 4.1 图像分割 4.1.1 基于阈值分割 4.1.2 基于边缘分割 4.1.3 基于区域分割 (1)区域生长法 (2)分水岭算法 4.1.4 基于图论分割 (1)Graph Cuts 分割 (2&…...

怎么获取winform中动态代码生成的控件的状态
winform怎么获取动态代码生成窗口里面的控件的属性状态 MainForm中调用 private void ShowPropertyForm() { PropertyForm form new PropertyForm(selectedShape); form.ShowDialog(); pictureBox1.Refresh(); …...

Windows安装Maven并配置环境
Windows下安装和配置Maven的步骤 介绍:步骤:步骤 1:下载Maven步骤 2:解压缩Maven分发包步骤 3:设置环境变量步骤 4:验证安装 结论: 介绍: Maven是一个非常流行的构建和项目管理工具…...

致力于中小企业JavaEE企业级快速开发平台、后台框架平台
一、开源项目简介 J2eeFAST 是一个 Java EE 企业级快速开发平台, 致力于打造中小企业最好用的开源免费的后台框架平台 。系统基于(Spring Boot、Spring MVC、Apache Shiro、MyBatis-Plus、Freemarker、Bootstrap、AdminLTE)经典技术开发&…...

【神经网络】tensorflow实验9--分类问题
1. 实验目的 ①掌握逻辑回归的基本原理,实现分类器,完成多分类任务; ②掌握逻辑回归中的平方损失函数、交叉熵损失函数以及平均交叉熵损失函数。 2. 实验内容 ①能够使用TensorFlow计算Sigmoid函数、准确率、交叉熵损失函数等,…...

LeetCode2. 两数相加
写在前面: 题目链接:LeetCode2两数相加 编程语言:C 题目难度:中等 一、题目描述 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 …...

基于无线传感网络(WSN)的目标跟踪技术(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 无线传感器网络由于其自组织性、鲁棒性及节点数量巨大的特点,非常适合于目标跟踪。无线传感器网络中的移动目标跟踪实际上就是…...

百度发布首个可信AI工具集TrustAI,助力数据分析与增强
百度发布首个集分析与增强于一体的可信AI工具集TrustAI,该工具集旨在帮助用户快速、准确地对各种类型的数据进行分析和增强,从而提高数据的价值和可信度。 随着人工智能技术的快速发展,数据的价值和重要性日益凸显。然而,在数据处…...

电力系统负荷与电价预测优化模型(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

asp.net+C#超市商品进销存管理系统
本超市商品管理系统主要超市内部提供服务,系统分为管理员员工两部分。 本研究课题重点主要包括了下面几大模块:管用户登录,员工管理,商品管理,进货管理,销售管理,供应商信息,会员信…...

轻量级K8s发行版的五大优势,助力企业快速拥抱边缘计算
随着物联网和移动设备的普及,边缘计算已成为当前信息技术领域的热门话题。为了满足这一需求,越来越多的企业开始探索使用容器化技术来打造轻量级的K8s发行版。这种发行版可以更加灵活地部署在物理边缘,提供更快速、更稳定的服务。 在这篇文章…...

【深入理解redis】数据结构
文章目录 动态字符串SDS字符串编码类型 intsetDictZipListZipList的连锁更新问题 QuickListSkipListRedisObjectStringListSet结构ZSETHash Redis 共有 5 种基本数据结构:String(字符串)、List(列表)、Set(…...

《计算机网络—自顶向下方法》 第三章Wireshark实验:DNS协议分析
域名系统 DNS(Domain Name System) 是互联网使用的命名系统,用于把便于大家使用的机器名字转换为 IP 地址。许多应用层软件经常直接使用 DNS,但计算机的用户只是间接而不是直接使用域名系统。 互联网采用层次结构的命名树作为主机的名字,并使…...

JUC(十二)-线程中断相关问题(LockSupport,sleep,InterruptException)
JUC线程中断相关问题总结 线程中断相关问题总结 JUC线程中断相关问题总结一、 sleep 和线程中断之间的关系和特点结论测试验证代码如下 二、 LockSupport 和线程中断之间的关系结论测试验证代码如下 一、 sleep 和线程中断之间的关系和特点 结论 线程调用 Thread.sleep之后会进…...

Kotlin高级协程
Kotlin高级协程 一.前言二.先从线程说起三.协程的设计思想四.协程特点:优雅的实现移步任务五.协程基本使用六.协程和线程相比有什么特点,如何优雅的实现异步任务 一.前言 在文章正式上干货之前,先说一点背景吧;我是 Kotlin 协程官…...

车载软件架构——闲聊几句AUTOSAR BSW(四)
我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 我们并不必要为了和谐,而时刻保持通情达理;我们需要具备的是,偶尔有肚量欣然承认在某些方面我们可能会有些不可理喻。该有主见的时候能掷地有声地镇得住场…...
Linux:rpm查询安装 yum安装
环境: 需要插入安装镜像 镜像内有所需的安装库 我这里使用的虚拟机直接连接光盘 连接的光盘挂载在/dev/cdrom 由于我们无法直接进入,所以选择把/dev/cdrom挂载到别的地方即可 mount /dev/cdrom /123 将/dev/cdrom 挂载到 /123 目录下 Packages下就是…...

Android音视频开发之音频录制和播放
1.封装音频录制工具类: public class RecorderAudioManagerUtils {private static volatile RecorderAudioManagerUtils mInstance;public static RecorderAudioManagerUtils getInstance() {if (mInstance null) {synchronized (RecorderAudioManagerUtils.class…...