Spring中的数据校验

news2024/9/23 15:32:51

数据校验基础

参考: Java Bean Validation 规范

Spring对Bean Validation的支持

Spring定义了一个接口org.springframework.validation.Validator,用于应用相关的对象的校验器。

这个接口完全从基础设施或者上下文中脱离的,这意味着它没有跟web层或者数据访问层或者其余任何的某一个层次发生耦合。所以它能用于应用中的任意一个层次,能对应用中的任意一个对象进行校验。

接口定义

public interface Validator {
    // 此clazz是否可以被validate
	boolean supports(Class<?> clazz);
	
    // 执行校验,错误消息放在Errors中
    // 如果能执行校验,通常也意味着supports方法返回true
	// 可以参考ValidationUtils这个工具类
	void validate(Object target, Errors errors);
}

接口实现

image-20230216155742364

SmartValidator

对Validator接口进行了增强,能进行分组校验

public interface SmartValidator extends Validator {

	// validationHints:就是启动的校验组
    // target:需要校验的结果
    // errors:封装校验
    void validate(Object target, Errors errors, Object... validationHints);
	
    // 假设value将被绑定到指定对象中的指定字段上,并进行校验
    // @since 5.1  这个方法子类需要复写 否则不能使用
    default void validateValue(Class<?> targetType, String fieldName, @Nullable Object value, Errors errors, Object... validationHints) {
        throw new IllegalArgumentException("Cannot validate individual value for " + targetType);
    }
}

SpringValidatorAdapter

SpringValidatorAdapter实现了对 Bean Validation适配

// 可以看到,这个接口同时实现了Spring中的SmartValidator接口跟JSR中的Validator接口
public class SpringValidatorAdapter implements SmartValidator, javax.validation.Validator {
	
    //约束必须使用的3个属性
	private static final Set<String> internalAnnotationAttributes = new HashSet<>(4);
	static {
		internalAnnotationAttributes.add("message");
		internalAnnotationAttributes.add("groups");
		internalAnnotationAttributes.add("payload");
	}
	
    // targetValidator就是实际完成校验的对象
	@Nullable
	private javax.validation.Validator targetValidator;
    
	public SpringValidatorAdapter(javax.validation.Validator targetValidator) {
		Assert.notNull(targetValidator, "Target Validator must not be null");
		this.targetValidator = targetValidator;
	}
	SpringValidatorAdapter() {
	}
	void setTargetValidator(javax.validation.Validator targetValidator) {
		this.targetValidator = targetValidator;
	}

    // 支持对所有类型的Bean的校验
	@Override
	public boolean supports(Class<?> clazz) {
		return (this.targetValidator != null);
	}
	
    // 调用targetValidator完成校验,并通过processConstraintViolations方法封装校验后的结果到Errors中
	@Override
	public void validate(Object target, Errors errors) {
		if (this.targetValidator != null) {
			processConstraintViolations(this.targetValidator.validate(target), errors);
		}
	}
	
    // 完成分组校验
	@Override
	public void validate(Object target, Errors errors, Object... validationHints) {
		if (this.targetValidator != null) {
			processConstraintViolations(
					this.targetValidator.validate(target, asValidationGroups(validationHints)), errors);
		}
	}
	
    // 完成对对象上某一个字段及给定值的校验
	@SuppressWarnings("unchecked")
	@Override
	public void validateValue(
			Class<?> targetType, String fieldName, @Nullable Object value, Errors errors, Object... validationHints) {

		if (this.targetValidator != null) {
			processConstraintViolations(this.targetValidator.validateValue(
					(Class) targetType, fieldName, value, asValidationGroups(validationHints)), errors);
		}
	}


	// @since 5.1
	// 将validationHints转换成JSR中的分组
	private Class<?>[] asValidationGroups(Object... validationHints) {
		Set<Class<?>> groups = new LinkedHashSet<>(4);
		for (Object hint : validationHints) {
			if (hint instanceof Class) {
				groups.add((Class<?>) hint);
			}
		}
		return ClassUtils.toClassArray(groups);
	}

	// 省略对校验错误的封装
    // .....

    // 省略对JSR中validator接口的实现,都是委托给targetValidator完成的
    // ......

}


ValidatorAdapter

SpringValidatorAdapter同一级别的类,但是不同的是他没有实现JSR中的Validator接口。一般不会使用这个类。

它实现了对 SmartValidator 的适配。

public class ValidatorAdapter implements SmartValidator, ApplicationContextAware, InitializingBean, DisposableBean {

	private final SmartValidator target;

	private final boolean existingBean;

	ValidatorAdapter(SmartValidator target, boolean existingBean) {
		this.target = target;
		this.existingBean = existingBean;
	}

	public final Validator getTarget() {
		return this.target;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return this.target.supports(clazz);
	}
    //... ... 
}

CustomValidatorBean

配置一个bean,暴露一个 JSR-303 Validator,使用了 JSR 的3个接口。

public class CustomValidatorBean extends SpringValidatorAdapter implements Validator, InitializingBean {

	@Nullable
	private ValidatorFactory validatorFactory;

	@Nullable
	private MessageInterpolator messageInterpolator;

	@Nullable
	private TraversableResolver traversableResolver;
}

LocalValidatorFactoryBean

OptionalValidatorFactoryBean

继承了LocalValidatorFactoryBean,区别在于让校验器的初始化成为可选的,即使校验器没有初始化成功也不会报错。

@Validated跟@Valid的区别

定义

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
	// 校验时启动的分组
	Class<?>[] value() default {};
}

@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface Valid {
    //没有提供任何属性
}

区别

  • 来源不同:@Valid是JSR的规范,来源于javax.validation包下,而@Validated是Spring自身定义的注解,位于org.springframework.validation.annotation包下

  • 作用范围不同:@Validated无法作用在字段上,正因为如此它就无法完成对级联属性的校验。而@Valid的没有这个限制。

  • 注解中的属性不同:@Validated注解中可以提供一个属性去指定校验时采用的分组,而@Valid没有这个功能,因为@Valid不能进行分组校验

应用

准备

待校验的类:

@Data
public class ValidatedData {

    @NotNull
    public String name;

    @Positive
    public Integer age;

    @NotNull
    @NotEmpty
    private List<@Email String> emails;

    /**
     * 定义的2个组,以接口的形式
     */
    public interface GroupA {
    }

    public interface GroupB {

    }

}

/**
 * 外部验证数据,用于级联验证
 *
 * @author lihz
 * @date 2023/2/18
 */
@Data
public class OuterValidatedData {
    @NotNull
    String name;

    @Valid
    ValidatedData validatedData;
}

测试Controller:

package com.jurassic.cloud.project.controller;

import com.jurassic.cloud.project.dto.OuterValidatedData;
import com.jurassic.cloud.project.dto.ValidatedData;
import com.jurassic.cloud.project.service.impl.ValidationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;

@RestController
@RequestMapping("/validate")
public class ValidationController {

    @Autowired
    ValidationService validationService;

    @PostMapping("/valid")
    public String testValid(@RequestBody @Valid ValidatedData data) {
        System.out.println(data);
        return "OK";
    }

    @PostMapping("/validated")
    public String testValidated(@RequestBody @Validated ValidatedData data) {
        System.out.println(data);
        return "OK";
    }

    @PostMapping("/valid/nest")
    public String testValidNest(@RequestBody @Valid OuterValidatedData data) {
        System.out.println(data);
        return "OK";
    }

    @PostMapping("/validated/nest")
    public String testValidatedNest(@RequestBody @Validated OuterValidatedData data) {
        System.out.println(data);
        return "OK";
    }

    @PostMapping("/valid/method")
    public String testValidMethod(@RequestBody ValidatedData data) {
        validationService.testValid(data);
        return "OK";
    }

    @PostMapping("/validated/method")
    public String testValidatedMethod(@RequestBody ValidatedData data) {
        validationService.testValidated(data);
        return "OK";
    }

    @PostMapping("/valid/nest/method")
    public String testValidNestMethod(@RequestBody OuterValidatedData data) {
        validationService.testValidNest(data);
        return "OK";
    }

    @PostMapping("/validated/nest/method")
    public String testValidatedNestMethod(@RequestBody OuterValidatedData data) {
        validationService.testValidatedNest(data);
        return "OK";
    }

    @PostMapping("/valid/simple")
    public String testValid(@Valid @Max(10) int age, @Valid @NotBlank String name) {
        System.out.println(age + "      " + name);
        return "OK";
    }

    @PostMapping("/validated/simple")
    public String testValidated(@Validated @Max(10) int age, @Validated @NotBlank String name) {
        System.out.println(age + "      " + name);
        return "OK";
    }

    @PostMapping("/non/method/simple")
    public String testNonMethodSimple(@Max(10) int age, @NotBlank String name) {
        validationService.testNon(age, name);
        return "OK";
    }

    @PostMapping("/valid/method/simple")
    public String testValidMethodSimple(@Max(10) int age, @NotBlank String name) {
        validationService.testValid(age, name);
        return "OK";
    }

    @PostMapping("/validated/method/simple")
    public String testValidatedMethodSimple(@Max(10) int age, @NotBlank String name) {
        validationService.testValidated(age, name);
        return "OK";
    }
}

测试数据:

{
  "name": "demon",
  "age": -1,
  "emails": [
    "demon7552003@hotmail.com"
  ]
}

{
  "name": "xxxxx",
  "data": {
	  "name": "demon",
	  "age": -1,
	  "emails": [
		"demon7552003@hotmail.com"
	  ]
	}
}

测试服务

package com.jurassic.cloud.project.service.impl;

import com.jurassic.cloud.project.dto.OuterValidatedData;
import com.jurassic.cloud.project.dto.ValidatedData;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;

/**
 *
 *
 * @author lihz
 * @date 2023/2/18
 */
@Service
//@Validated
@Valid
public class ValidationService {
    public void testValid(@Valid ValidatedData data) {
        System.out.println(data);
    }

    public void testValidated(@Validated ValidatedData data) {
        System.out.println(data);
    }

    public void testValidNest(@Valid OuterValidatedData data) {
        System.out.println(data);
    }

    public void testValidatedNest(@Validated OuterValidatedData data) {
        System.out.println(data);
    }

    public void testNon(  @Max(10) int age,  @NotBlank String name) {
        System.out.println(age+"     "+name);
    }

    public void testValid(@Valid @Max(10) int age,@Valid @NotBlank String name) {
        System.out.println(age+"     "+name);
    }

    public void testValidated(@Validated   @Max(10) int age, @Validated  @NotBlank String name) {
        System.out.println(age+"     "+name);
    }



}

对JavaBean的校验(Controller层)

测试结果

  • valid测试

    {
        "code": 1,
        // age 必须是正数
        "msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.ValidationController.testValid(com.jurassic.cloud.project.dto.ValidatedData): [Field error in object 'validatedData' on field 'age': rejected value [-1]; codes [Positive.validatedData.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validatedData.age,age]; arguments []; default message [age]]; default message [必须是正数]] ",
        "data": null
    }
    
  • validated测试

{
    "code": 1,
    "msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.ValidationController.testValidated(com.jurassic.cloud.project.dto.ValidatedData): [Field error in object 'validatedData' on field 'age': rejected value [-1]; codes [Positive.validatedData.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validatedData.age,age]; arguments []; default message [age]]; default message [必须是正数]] ",
    "data": null
}
  • valid测试 嵌套数据

    {
        "code": 1,
        "msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.ValidationController.testValidNest(com.jurassic.cloud.project.dto.OuterValidatedData): [Field error in object 'outerValidatedData' on field 'validatedData.age': rejected value [-1]; codes [Positive.outerValidatedData.validatedData.age,Positive.validatedData.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [outerValidatedData.validatedData.age,validatedData.age]; arguments []; default message [validatedData.age]]; default message [必须是正数]] ",
        "data": null
    }
    
  • validated测试 嵌套数据

    {
        "code": 1,
        "msg": "Validation failed for argument [0] in public java.lang.String com.jurassic.cloud.project.controller.ValidationController.testValidatedNest(com.jurassic.cloud.project.dto.OuterValidatedData): [Field error in object 'outerValidatedData' on field 'validatedData.age': rejected value [-1]; codes [Positive.outerValidatedData.validatedData.age,Positive.validatedData.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [outerValidatedData.validatedData.age,validatedData.age]; arguments []; default message [validatedData.age]]; default message [必须是正数]] ",
        "data": null
    }
    

总结

从结果上看 @Valid@Validated 都能触发级联验证。

如果要触发属性的级联验证,一定要放注解 @Valid

对普通方法的JavaBean校验

测试结果

Service类上不加注解

  • valid测试

    OK
    
  • validated测试

    ok
    
  • valid测试 嵌套数据

    OK
    
  • validated测试 嵌套数据

    OK
    

Service类上加@Valid

  • valid测试

    OK
    
  • validated测试

    ok
    
  • valid测试 嵌套数据

    OK
    
  • validated测试 嵌套数据

    OK
    

Service类上加@Validated

  • valid测试

    {
        "code": 1,
        "msg": "testValid.data.age: 必须是正数",
        "data": null
    }
    
  • validated测试

    ok
    
  • valid测试 嵌套数据

    {
        "code": 1,
        "msg": "testValidNest.data.validatedData.age: 必须是正数",
        "data": null
    }
    
  • validated测试 嵌套数据

    OK
    

总结

只有类上添加了@Vlidated注解,并且待校验的**JavaBean上添加了@Valid**的情况下校验才会生效。

返回的异常信息包含了 进行验证的方法的名称,以及验证的属性

对简单参数的校验(Controller层)

测试结果

1、测试:http://127.0.0.1:8073/validate/valid/simple?age=20&name=demon

结果:OK

2、测试:http://127.0.0.1:8073/validate/validated/simple?age=20&name=demon

结果:OK

结论

参数上,不论加 @Valid 还是 @Validated ,都不会触发验证。

对简单参数的校验(Service层)

测试结果

1、测试:http://127.0.0.1:8073/validate/valid/simple?age=20&name=demon

结果:OK

2、测试:http://127.0.0.1:8073/validate/validated/simple?age=20&name=demon

结果:OK

结论

参数上,不论加 @Valid 还是 @Validated ,都不会触发验证。

对普通方法上的简单参数的校验(Service层)

1、参数不加注解: http://127.0.0.1:8073/validate/non/method/simple?age=20

2、参数加@Valid: http://127.0.0.1:8073/validate/valid/simple?age=20

3、参数加@Validated:http://127.0.0.1:8073/validate/validated/simple?age=20

测试结果

Service类上不加注解

1、OK

2、OK

3、OK

Service类上加@Valid

1、OK

2、OK

3、OK

Service类上加@Validated

1、

{
    "code": 1,
    "msg": "testNon.age: 最大不能超过10, testNon.name: 不能为空",
    "data": null
}

2、

{
    "code": 1,
    "msg": "testValid.name: 不能为空, testValid.age: 最大不能超过10",
    "data": null
}

3、

{
    "code": 1,
    "msg": "testValidated.age: 最大不能超过10, testValidated.name: 不能为空",
    "data": null
}

结论

参数上,不论加 @Valid 还是 @Validated,或者不加 ,都不会影响结果。

仅在类上加 @Validated 时,才会触发简单参数的验证。

普通方法验证的总结

仅在类上加@Validated 时,才会触发参数的验证。对于非简单类的验证,必须加 @Valid 触发级联验证。

Controller层验证的总结

仅能验证非简单类型

从结果上看 @Valid@Validated 都能触发级联验证。

如果要触发属性的级联验证,一定要放注解 @Valid

Spring对JSR的适配

在普通方法上的验证,抛出异常ConstraintViolationException,在Controller层抛出的异常是MethodArgumentNotValidException。Spring对参数绑定做了封装,错误信息被封装为BindingResult,在方法RequestResponseBodyMethodProcessor#resolveArgument中做处理。

全局异常处理

通过@RestControllerAdvice 做AOP。

@RestControllerAdvice
public class MethodArgumentNotValidExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();

        StringBuilder stringBuilder = new StringBuilder();
        for (FieldError error : bindingResult.getFieldErrors()) {
            String field = error.getField();
            Object value = error.getRejectedValue();
            String msg = error.getDefaultMessage();
            String message = String.format("错误字段:%s,错误值:%s,原因:%s;", field, value, msg);
            stringBuilder.append(message).append("\r\n");
        }
        return Result.error(MsgDefinition.ILLEGAL_ARGUMENTS.codeOf(), stringBuilder.toString());
    }
}

附录

参考

官网:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/spring-framework-reference/core.html#validation-beanvalidation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java基础】Java语言特性

认识Java java语言的执行过程 编写纯文本文件 .java 经过javac编译器(java complier)编译 .class .class是二进制的字节码 在源文件中定义几个类&#xff0c;就会生成几个 由JVM运行 .class JVM把字节码编译成可以在处理器上运行的高性能的本地代码&#xff08;native code),…

Linux进程概念(三)

环境变量与进程地址空间环境变量什么是环境变量常见环境变量环境变量相关命令环境变量的全局属性PWDmain函数的三个参数进程地址空间什么是进程地址空间进程地址空间&#xff0c;页表&#xff0c;内存的关系为什么存在进程地址空间环境变量 什么是环境变量 我们所有写的程序都…

SpringBoot升级到3.0

SpringBoot 3.0出来有一段时间了&#xff0c;一直没时间来整理&#xff0c;这次来看一下吧。 Spring Boot 可以轻松创建独立的、生产级的基于 Spring 的应用程序&#xff0c;您可以“直接运行”。 SpringBoot升级到3.01. SpringBoot的维护时间线2. pom添加3. 打包大小对比4. 升…

GEE学习笔记 七十:【GEE之Python版教程四】Python基础编程二

通过上一章的讲解&#xff0c;我们对于python有了初步的了解&#xff0c;这一章就详细讲解一下python的各个变量以及运算规则等内容。 关于测试代码推荐初学者将每一段代码都自己敲入编辑器中在本地运行。 1、数值 这是任何编程中都会有的基本变量&#xff0c;在python支持的…

mac m1设备上安装Qt并使用qt编程遇到的问题以及解决方式

# 简介&#xff1a; 首先在M1平台上的程序可以看到有两种架构&#xff0c;分别是intel的&#xff08;x86-64&#xff09;和苹果的m1&#xff08;arm64架构&#xff09;&#xff0c;根据苹果的介绍&#xff0c;当在m1上面运行intel程序的时候使用的是转译的方式运行的&#xff…

设计模式(十)----结构型模式之适配器模式

1、概述 如果去欧洲国家去旅游的话&#xff0c;他们的插座如下图最左边&#xff0c;是欧洲标准。而我们使用的插头如下图最右边的。因此我们的笔记本电脑&#xff0c;手机在当地不能直接充电。所以就需要一个插座转换器&#xff0c;转换器第1面插入当地的插座&#xff0c;第2面…

以太网详细解析

数据链路层&#xff1a;考虑相邻两个节点之间的传输&#xff08;通过网线/光纤/无线直接相连的两个设备&#xff09; 这里以数据链路层其中最知名的就是“以太网” 以太网帧格式&#xff1a; 以太网数据帧帧头载荷帧尾 帧头&#xff1a;目的地址、源地址、类型 目的地址和源…

51单片机简易电阻电感电容RLC测量仪仿真设计

51单片机简易电阻电感电容RLC测量仪仿真( proteus仿真程序讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0040 51单片机简易电阻电感电容RLC测量仪仿真51单片机最小系统的相关知识复位…

【第二阶段:java基础】第13章:泛型(P553-P568):自定义泛型、泛型的集成和通配符、Junit

本系列博客是韩顺平老师java基础课的课程笔记&#xff0c;B站&#xff1a;课程链接&#xff0c;吐血推荐的一套全网最细java教程&#xff0c;获益匪浅&#xff01; 韩顺平P553-P5681. 泛型的理解和好处2. 泛型的定义3. 泛型的语法4. 泛型的细节5. 自定义泛型6. 泛型继承和通配符…

62 一次 Promotion failed 的调试

前言 最近 有一个想法就是, 调试一下 DefNewGeneration 里面的晋升失败的情况 呵呵 对于这块的代码上面, 看着感觉有一些疑问的地方, 因此想通过 实际的调试, 来验证一下 实际的情况 然后 之前写了一个用例, 但是 和心中的期望差距甚大, 当然 主要的问题 还是自己对于 细…

CSDN 算法技能树 蓝桥杯-基础 刷题+思考总结

切面条-蓝桥杯-基础-CSDN算法技能树https://edu.csdn.net/skill/algorithm/algorithm-530255df51be437b967cbc4524fe66ea?category188 目录 切面条 大衍数列 门牌制作 方阵转置 微生物增殖 成绩统计 星系炸弹 判断闰年的依据: 特别数的和 *日志统计*&#xff08;双指…

结构体与引用

1.结构体基本概念结构体属于用户自定义的数据类型&#xff0c;允许用户存储不同的数据类型2.结构体定义和使用语法: struct 结构体 { 结构体成员列表 };通过结构体创建变量的方式有三种:struct 结构体名 变量名struct 结构体名 变量名 { 成员1值&#xff0c;成员2值...}定义结构…

《爆肝整理》保姆级系列教程python接口自动化(十八)--重定向(Location)(详解)

简介   在实际工作中&#xff0c;有些接口请求完以后会重定向到别的url&#xff0c;而你却需要重定向前的url。URL主要是针对虚拟空间而言&#xff0c;因为不是自己独立管理的服务器&#xff0c;所以无法正常进行常规的操作。但是自己又不希望通过主域名的二级目录进行访问&…

分布式session共享解决方案

分布式session共享解决方案 1.分布式 Session 问题 示意图 解读上图&#xff0c;假如我们去购买商品 当 Nginx 对请求进行负载均衡后, 可能对应到不同的 Tomcat比如第 1 次请求, 均衡到 TomcatA, 这时 Session 就记录在 TomcatA, 第 2 次请求&#xff0c; 均衡到 TomcatB, 这…

【Mysql8.0取消严格区分大小】已安装的mysql8.0取消严格区分大小写及mysql8.0重装与赋权限详解(2023年亲测有效)

【写在前面】其实故事要从my.cnf为空&#xff0c;且lower-case-table-names为0开始&#xff0c;linux环境下mysql8.0及其之后的版本对表名和数据库是严格区分大小写的&#xff0c;从而导致我们运行项目时候会报错Table xxx.QRTZ_LOCKS doesnt exist。但是我已经装好了mysql8.0咋…

17.CSS伪类

举一个简单的例子来说明什么是伪类&#xff1f; 从之前的代码中&#xff0c;如下图&#xff0c;我们像给这两个列表中的某一列单独设置样式&#xff0c;我们该如何做呢&#xff1f; 我们肯定会选择在li标签上添加class去实现&#xff0c;如下 开始标记结束标记实际元素 <…

python--matplotlib(2)

前言 Matplotlib画图工具的官网地址是 http://matplotlib.org/ Python环境下实现Matlab制图功能的第三方库&#xff0c;需要numpy库的支持&#xff0c;支持用户方便设计出二维、三维数据的图形显示&#xff0c;制作的图形达到出版级的标准。 实验环境 Pycharm2020.2.5社区版,w…

算法练习-链表(二)

算法练习-链表&#xff08;二&#xff09; 文章目录算法练习-链表&#xff08;二&#xff09;1. 奇偶链表1.1 题目1.2 题解2. K 个一组翻转链表2.1 题目2.2 题解3. 剑指 Offer 22. 链表中倒数第k个节点3.1 题目3.2 题解3.2.1 解法13.2.2 解法24. 删除链表的倒数第 N 个结点4.1 …

中国智能物流行业市场规模及未来发展趋势

中国智能物流行业市场规模及未来发展趋势编辑中国智能物流行业市场规模正在快速增长。随着电子商务、物流配送、物联网等行业的发展&#xff0c;物流行业需求不断提高&#xff0c;智能物流产品应运而生。智能物流行业主要通过智能化管理、智能路径规划、智能定位、物流配送等方…

Java特性之设计模式【工厂模式】

一、工厂模式 概述 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式 在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通…