一起学SF框架系列5.11-spring-beans-数据校验validation

news2024/11/16 11:38:11

    在日常的项目开发中,应用在执行业务逻辑之前,为了防止非法参数对业务造成的影响,必须通过校验保证传入数据是合法正确的,但很多时候同样的校验出现了多次,在不同的层,不同的方法上,导致代码冗余,违反DRY原则。
    Java提供了数据校验规范来解决这个问题,可极大的简化校验实现,节省大量的工作量。Spring作为开发框架,支持相关规范,除了规范要求外,并提供了额外的增强。

Java数据校验规范

规范定义

    Java为Bean数据合法性校验提供了标准框架规范,它定义了一套可标注在成员变量,属性方法上的校验注解。最初版本为 Java Bean Validation1.0(JSR-303)、Java Bean Validation1.1(JSR-349),Java Bean Validation2.0(JSR-380),目前最新版本为Java Bean Validation3.0(同2.0最要变化是命名空间变为"jakarta.validation.*)。需要注意的是,JSR只是一项标准,它规定了一些校验注解的规范,但没有实现。
规范官网地址:https://beanvalidation.org/

常见注解

---- Java Bean Validation1.0 ----
@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=) 被注释的元素必须符合指定的正则表达式

---- Java Bean Validation1.1 ----
@Valid 用于嵌套校验,可以对一个对象中的属性进行递归校验。
@ConvertGroup 用于分组校验,可以指定校验的分组,根据不同的分组执行不同的校验规则。
@GroupSequence 用于定义校验分组的顺序,指定不同分组的执行顺序。
增加了允许对任意方法和构造函数的参数和返回值进行约束
提供了一种在进行级联(嵌套)验证时更改目标组的方法。
约束消息可以使用EL表达式进行更灵活的呈现和字符串格式设置。同样,在EL上下文中也可以使用验证值。
支持容器的校验,通过TYPE_USE类型的注解实现对容器内容的约束:List<@Email String>

---- Java Bean Validation2.0 ----
@Negative 被注释的元素必须为 负数
@NegativeOrZero 被注释的元素必须为 负数或0
@Positive 被注释的元素必须为 正数(0为非法值)
@PositiveOrZero 被注释的元素必须为 正数或0
@PastOrPresent 被注释的元素必须是一个过去或当前的日期
@Future OrPresent 被注释的元素必须是一个将来或当前的日期
@NotEmpty 被注释的元素不能为NULL或者是EMPTY
@NotBlank 被注释的元素(字符串)不能为Null且至少包含一个非空格字符(去掉前后空格判断)
@Email 被注释的元素(字符串)必须满足email格式

---- Hibernate Validator增强 ----
@Range(min=, max=) 被注释的元素必须位于(包括)指定的最小值和最大值之间
@Length(min=, max=) 被注释的元素(字符串)长度必须在给定的范围之内,包含两端

---- Spring Validator增强 ----
@Validated Spring Validator提供的增强,支持分组校验

@Valid和@Validated区别

1、@Valid是JSR标准规范,@Validated是Spring Validator提供的。
2、@Valid可支持METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE标注,不支持类上注解;@Validated支持TYPE,METHOD,PARAMETER上注解,支持类上注解,不支持属性上注解。
3、@Valid直接嵌套对象自动校验,@Validated不支持(因为不支持属性上注解)。
4、@Valid 不支持分组校验,@Validated支持。

规范实现

Hibernate Validator是Java规范官方认证的实现(如下图),实现见:https://hibernate.org/validator/。
在这里插入图片描述

Spring Validator

Spring提供了数据校验功能:Spring提供了Validator接口契约,可用于应用程序的每一层。

Validator接口

Spring提供了一个Validator接口(org.springframework.validation.Validator)(代码如下),使用它来验证对象;验证类需实现Validator接口,通过validate方法完成具体验证。

public interface Validator {
	// 是否当前当前验证器支持的类
	boolean supports(Class<?> clazz);
	// 验证给定的对象,如果出现验证错误,则将这些对象注册到给定的errors对象中 注1
	void validate(Object target, Errors errors);
}

注1:Errors是Spring用来存储并公开特定对象的数据绑定和验证错误信息接口,方法较多,默认实现BeanPropertyBindingResult,其类关系图如下:
在这里插入图片描述
另:SmartValidator接口继承于Validator接口,主要增强支持来自验证器外部提示的功能。

工具类

Spring提供实用工具类ValidationUtils,主要处理拒绝空字段的方法。为防止实例化,定义为抽象类,所有实现方法均是静态方法。

public abstract class ValidationUtils {
	private static final Log logger = LogFactory.getLog(ValidationUtils.class);


	/**
	 * 简易方式调用具体验证器进行验证
	 * validator-验证器
	 * target-验证对象
	 * errors-保存错误的errors对象
	 */
	public static void invokeValidator(Validator validator, Object target, Errors errors) {
		invokeValidator(validator, target, errors, (Object[]) null);
	}
	/**
	 * 调用具体验证器进行验证(实现)
	 * validator-验证器
	 * target-验证对象
	 * errors-保存错误的errors对象
	 * validationHints-提示对象(可多个)
	 */
	public static void invokeValidator(
			Validator validator, Object target, Errors errors, @Nullable Object... validationHints) {

		//对应参数不能空,否则程序会直接结束
		Assert.notNull(validator, "Validator must not be null");
		Assert.notNull(target, "Target object must not be null");
		Assert.notNull(errors, "Errors object must not be null");

		if (logger.isDebugEnabled()) {
			logger.debug("Invoking validator [" + validator + "]");
		}
		if (!validator.supports(target.getClass())) {
			throw new IllegalArgumentException(
					"Validator [" + validator.getClass() + "] does not support [" + target.getClass() + "]");
		}

		if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator smartValidator) {
			// 如果校验器是SmartValidator,调用smartValidator.validate
			smartValidator.validate(target, errors, validationHints);
		}
		else {
			// 如果校验器不是SmartValidator,调用validator.validate
			validator.validate(target, errors);
		}

		if (logger.isDebugEnabled()) {
			if (errors.hasErrors()) {
				logger.debug("Validator found " + errors.getErrorCount() + " errors");
			}
			else {
				logger.debug("Validator found no errors");
			}
		}
	}

    //字符串空验证(用错误代码获取错误信息)
	public static void rejectIfEmpty(Errors errors, String field, String errorCode) {
		rejectIfEmpty(errors, field, errorCode, null, null);
	}
    //字符串空验证(defaultMessage-提供默认错误信息)
	public static void rejectIfEmpty(Errors errors, String field, String errorCode, String defaultMessage) {
		rejectIfEmpty(errors, field, errorCode, null, defaultMessage);
	}
    //字符串空验证(errorArgs-格式化错误信息对应参数)
	public static void rejectIfEmpty(Errors errors, String field, String errorCode, Object[] errorArgs) {
		rejectIfEmpty(errors, field, errorCode, errorArgs, null);
	}
    //字符串空验证真正实现
	public static void rejectIfEmpty(Errors errors, String field, String errorCode,
			@Nullable Object[] errorArgs, @Nullable String defaultMessage) {

		Assert.notNull(errors, "Errors object must not be null");
		Object value = errors.getFieldValue(field);
		if (value == null || !StringUtils.hasLength(value.toString())) {
			errors.rejectValue(field, errorCode, errorArgs, defaultMessage);
		}
	}

    //字符串空或仅仅空格 验证
	public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode) {
		rejectIfEmptyOrWhitespace(errors, field, errorCode, null, null);
	}
    //字符串空或仅仅空格 验证(defaultMessage-提供默认错误信息)
	public static void rejectIfEmptyOrWhitespace(
			Errors errors, String field, String errorCode, String defaultMessage) {

		rejectIfEmptyOrWhitespace(errors, field, errorCode, null, defaultMessage);
	}
    //字符串空或仅仅空格 验证(errorArgs-格式化错误信息对应参数)
	public static void rejectIfEmptyOrWhitespace(
			Errors errors, String field, String errorCode, @Nullable Object[] errorArgs) {

		rejectIfEmptyOrWhitespace(errors, field, errorCode, errorArgs, null);
	}
    //字符串空或仅仅空格 验证实现
	public static void rejectIfEmptyOrWhitespace(
			Errors errors, String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage) {

		Assert.notNull(errors, "Errors object must not be null");
		Object value = errors.getFieldValue(field);
		if (value == null ||!StringUtils.hasText(value.toString())) {
			errors.rejectValue(field, errorCode, errorArgs, defaultMessage);
		}
	}
}

WEB项目应用

validation在web项目中的应用是最经典的,这也是validation的初衷。针对spring-boot或springMVC项目,如何使用Spring Validation在网上非常多,在此不赘述。

非WEB项目应用

非web项目应用,网上资料较少,本文着重讲这方面示例。

配置

在pom.xnl引入如下配置:

    <!-- hibernate-validator7对应tomcat-embed-el10支持jakarta;hibernate-validator6对应tomcat-embed-el8支持javax -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>7.0.5.Final</version>
    </dependency>
    <dependency>
	    <groupId>org.apache.tomcat.embed</groupId>
	    <artifactId>tomcat-embed-el</artifactId>
	    <version>10.0.5</version>
    </dependency>

提醒要特别注意版本匹配问题:
hibernate-validator7.0.5对应tomcat-embed-el10.0.5支持jakarta;
hibernate-validator6.2.5对应tomcat-embed-el8.5.29支持javax

原始方式应用

原始方式不是主要的应用方式,但理解原始方式有助于理解Spring实现校验的机制。

校验对象

public class Address {
	String country="CN";
	String province;
	String city;
	String county;
	// 省略get/set方法
}
public class Driver {
	String surname;  // 姓
	String name; // 名
	Integer age;
	Address address;
	// 省略get/set方法
}

实现校验类

@Component
public class AddressValidator implements Validator {

	//必须实现的接口方法-指定验证的类
	public boolean supports(Class clazz) {
		return Address.class.equals(clazz);
	}

	//必须实现的接口方法-指定验证的类
	public void validate(Object obj, Errors e) {
		ValidationUtils.rejectIfEmpty(e, "country", "country.empty");
		ValidationUtils.rejectIfEmpty(e, "province", "province.empty");
		ValidationUtils.rejectIfEmpty(e, "city", "city.empty");
		ValidationUtils.rejectIfEmpty(e, "county", "county.empty");
		ValidationUtils.rejectIfEmpty(e, "desc", "desc.empty");
	}
}
@Component
public class DriverValidator  implements Validator {
	@Autowired
	AddressValidator addressValidator;
	
	public boolean supports(Class clazz) {
		return Driver.class.isAssignableFrom(clazz);
	}

	public void validate(Object target, Errors e) {
		ValidationUtils.rejectIfEmptyOrWhitespace(e, "surname", "field.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(e, "name", "field.required");
		Driver driver = (Driver) target;
		if (driver.getAge() < 18) {
			e.rejectValue("age", "negativevalue","未满18岁不能领取驾照");
		} else if (driver.getAge() > 70) {
			e.rejectValue("age", "too.darn.old","超过70岁不能领取驾照");
		}
		try {
			//嵌套验证路径入栈
			e.pushNestedPath("address");
			ValidationUtils.invokeValidator(this.addressValidator, driver.getAddress(), e);
		} finally {
			//嵌套验证路径出栈
			e.popNestedPath();
		}
	}
}

校验

	public void demo() {
		Address addr=new Address();
		addr.setProvince("四川");
		addr.setCity("成都");
		addr.setCounty("高新区");
		addr.setDesc("详细地址");

		Driver driver=new Driver();
		driver.setSurname("wang");
		driver.setName("wang");
		driver.setAge(20);
		driver.setAddress(addr);
		
		// 用DataBinder实现校验 注1
        DataBinder binder = new DataBinder(driver);
        binder.setValidator(driverValidator);
        // 调用校验
        binder.validate();
     	// 获取校验结果
        BindingResult results = binder.getBindingResult();
        
        System.out.println(results);
	}

注1:DataBinder见https://blog.csdn.net/davidwkx/article/details/131913078

注解方式应用

注解方式是应用实现的主要方式,简单高效。

校验对象

public class AddressAnnotateValidator {
	@NotBlank(message = "country 不能为空",groups = ValidGroupUpdate.class)
	String country="CN";
	@NotBlank
	String province;
	@NotBlank
	String city;
	@NotBlank
	String county;
	// 区(county)以下的地址描述
	@NotBlank
	String desc;
	// 省略get/set方法
}
public class DriverAnnotateValidator {
    private Integer id;
    @NotBlank(message = "姓不可以为空")
    @Length(min = 1, max = 20, message = "姓长度需要在20个字以内")
    private String surname;
    @NotBlank(message = "名不可以为空")
    @Length(min = 1, max = 20, message = "名长度需要在20个字以内")
    private String name;
    // 电话号码校验器是自己实现的 注1
	@NotBlank(message = "电话不可以为空")
    @Length(min = 1, max = 11, message = "电话长度需要在11个字以内")
    @MobilePhoneCheck
    private String mobilePhone;
    @NotBlank(message = "邮箱不允许为空")
    @Email(message = "邮箱格式不正确")
    @Length(min = 5, max = 50, message = "邮箱长度需要在50个字符以内")
    private String mail;
    @Max(70)
    @Min(18)
    private int age;
    @NotNull(message = "联系地址不可以为空")
    @Valid
    private AddressAnnotateValidator addr;
    // 省略get/set方法
}

注1:利用注解,可处理大量空、必须输入等繁琐校验。但有些校验很复杂,规范中注解不能支持,就需要自定义校验注解。

自定义校验注解

下面以定义移动电话校验注解为例,示例如何自定义校验注解。
1、定义校验注解MobilePhoneCheck

//我们可以直接拷贝系统内的注解如@Min,复制到我们新的注解中,然后根据需要修改。
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
// 关联注解的实现类
@Constraint(validatedBy = {MobilePhoneValidator.class})
public @interface MobilePhoneCheck {
	//校验错误的默认信息
	String message() default "手机号码格式有问题";
	//是否强制校验
	boolean isRequired() default false;
	Class<?>[] groups() default {};
	Class<? extends Payload>[] payload() default {};
}

2、注解实现类MobilePhoneValidator(class)

// 实现的接口是MobilePhoneCheck注解
@Component
public class MobilePhoneValidator  implements ConstraintValidator<MobilePhoneCheck, String> {	 
	 private boolean required = false;
	 // 用正则表达式判断手机号码格式合法性
	 private static final Pattern mobile_pattern = Pattern.compile("^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$");
	 
	 // 注解接口方法的标准实现(必须)
	 @Override
	 public void initialize(MobilePhoneCheck constraintAnnotation) {
		 required = constraintAnnotation.isRequired();
	 }
	 // 注解接口方法的实现(必须)-验证时真正使用的接口
	 @Override
	 public boolean isValid(String phone, ConstraintValidatorContext constraintValidatorContext) {
		  //是否为手机号的实现
		  if (required) 
			  return isMobile(phone);
		  if (!StringUtils.hasText(phone)) 
			  return true;
		  return isMobile(phone);
	  }

	 //手机号码合法性判断
	 public static boolean isMobile(String src) {
		  if (!StringUtils.hasText(src)) 
			  return false;
		  Matcher m = mobile_pattern.matcher(src);
		  return m.matches();
	 }
}

校验对象

	public void demo() {
		AddressAnnotateValidator addr=new AddressAnnotateValidator();
		addr.setProvince("四川");
		addr.setCity("成都");
//		addr.setCounty("高新区");
		addr.setDesc("详细地址");
		DriverAnnotateValidator driver=new DriverAnnotateValidator();
		driver.setSurname("wang");
		driver.setName("wang");
		driver.setAge(15);
		driver.setMobilePhone("13300333691");
		driver.setAddr(addr);
		
		//获取校验结果(如何集合为空,表示无错误)  注1
		 Map<String,String> validateResult=BeanValidatorUtil.validate(driver);
 		 System.out.println(validateResult);
	}

注1:需项目提供校验方式。附BeanValidatorUtil代码:

public class BeanValidatorUtil {
	//用spring的校验器
//    private static final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
	//用Hibernate的校验器
    private static final ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
            .configure()
            .addProperty( "hibernate.validator.fail_fast", "true" )  //快速检测模式:不检测全部错误,碰到第一个错误就返回
            .buildValidatorFactory();
	
    //单个对象校验
    public static <T> Map<String,String> validate(T t, Class... groups){
    	//取校验器
        Validator validator=validatorFactory.getValidator();
        
        //校验
        Set<ConstraintViolation<T>> validateResult=validator.validate(t,groups);
        //如果为空
        if (validateResult.isEmpty()){
            return Collections.emptyMap();
        }else{
            //不为空时表示有错误(属性为key,错误信息为value的map) 注1
        	return validateResult.stream().collect(Collectors.toMap(k -> k.getPropertyPath().toString(), v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue(), (key1, key2) -> key2));
        }
    }
    //集合对象校验
    public static Map<String,String> validateList(Collection<?> collection){
        if(collection==null || collection.size()<1)
            return Collections.emptyMap();

        Map<String,String> errors=Collections.emptyMap();
        for(Object el:collection) {
        	errors=validate(el,new Class[0]);
        	if(!errors.isEmpty())  //有错
        		break;
        }
        return errors;
    }

     // 校验某一对象是否合法
    public static Map<String,String> validateObject(Object first,Object... objects){
        if (objects !=null && objects.length > 0 ){
            return validateList(Arrays.asList(first,objects));
        } else {
            return validate(first , new Class[0]);
        }
    }
}

注1:实际应用最好是抛出ConstraintViolationException异常,然后增加全局异常处理,这样程序处理很简单:每个方法只需要在第一行增加校验参数即可。

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

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

相关文章

map求和accumulate、参数互换

运行代码&#xff1a; //map求和accumulate、参数互换 #include"std_lib_facilities.h"istream& operator>>(istream& is, map<string, int>&mm) {string ss"";int ii0;is >> ss;if(is>>ii)mm[ss] ii;return is; }t…

VSCode中python代码输出中文乱码解决

前言 最近在vs code里面执行python脚本时&#xff0c;只有打印中文&#xff0c;就会乱码。 内容 先检查右下角编码集设置是否正确 检查右下角编码集设置是否正确 &#xff1a; 如果不是utf-8点击修改。 如果还是不行&#xff0c;就进行下面的操作 修改用户设置 路径&a…

【算法基础:动态规划】5.1 背包问题

文章目录 01背包例题&#xff1a;2. 01背包问题 完全背包例题&#xff1a;3. 完全背包问题 多重背包例题&#xff1a;4. 多重背包问题 I例题&#xff1a;5. 多重背包问题 II&#xff08;数据范围较大&#xff1a;二进制优化&#xff09; 分组背包例题&#xff1a;9. 分组背包问…

2023/7/29总结

项目&#xff1a; 这几天主要实现了评论的功能点: 还是有点小bug&#xff0c;还在更改中…… 修改个人中心的界面 接下来是把收藏完善&#xff0c;因为收藏需要用户自己创建一个新的收藏夹

iOS开发-转场动画切换界面(类似系统动画)

iOS开发-转场动画切换界面&#xff08;类似系统动画&#xff09; 在开发中&#xff0c;无论我们使用 push 还是 present 推出新的 viewcontroller 时&#xff0c;系统为了提高用户体验都会为我们默认加上一些过渡动画。但是开发中需要自定义过度动画效果。这里就需要用到了转场…

二十五章:用于弱监督语义分割的激活调节和重新校准方案

0.摘要 图像级弱监督语义分割&#xff08;WSSS&#xff09;是一项基础而具有挑战性的计算机视觉任务&#xff0c;有助于场景理解和自动驾驶。大多数现有方法利用基于分类的类激活图&#xff08;CAMs&#xff09;作为初始伪标签&#xff0c;但这些方法往往关注区分性的图像区域&…

Leetcode刷题---C语言实现初阶数据结构---单链表

1 删除链表中等于给定值 val 的所有节点 删除链表中等于给定值 val 的所有节点 给你一个链表的头节点head和一个整数val&#xff0c;请你删除链表中所有满足Node.valval的节点&#xff0c;并返回新的头节点 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[…

Tomcat 的使用(图文教学)

Tomcat 的使用&#xff08;图文教学&#xff09; 前言一、什么是Tomcat&#xff1f;二、Tomcat 服务器和 Servlet 版本的对应关系三、Tomcat 的使用1、安装2、目录介绍3、如何启动4、Tomcat 的停止5、如何修改 Tomcat 的端口号6、如何部暑 web 工程到 Tomcat 中6.1 方式一6.2 …

建设银行秋招指南,备考技巧和考试内容详解

建设银行秋招简介 银行作为非常吃香的岗位&#xff0c;每年都有不少同学通过投递简历&#xff0c;进入笔试&#xff0c;再到面试成功&#xff0c;成功到银行就职&#xff0c;也有相当一部分同学因为信息差&#xff0c;符合条件却没有报名。无法进入银行工作。 建设银行的秋招…

从保存受限的手机APP中提取文件(读取Android系统中的新增缓存文件)

这个手机APP的权限可能设置了无法在应用内保存文件&#xff0c;但是这个文件实际上一定存在于本地的某个地方&#xff0c;本文的方法通过遍历最后修改日期在今天的文件&#xff0c;很容易就可以找到它。 首先安装一个QPython&#xff0c;这个软件可以允许你在安卓手机上运行Py…

Linux常用命令——dpkg-reconfigure命令

在线Linux命令查询工具 dpkg-reconfigure Debian Linux中重新配制一个已经安装的软件包 补充说明 dpkg-reconfigure命令是Debian Linux中重新配置已经安装过的软件包&#xff0c;可以将一个或者多个已安装的软件包传递给此指令&#xff0c;它将询问软件初次安装后的配置问题…

平板光波导中导模的(注意不是泄露模)传播常数β的matlab计算(验证了是对的)

参照的是导波光学_王建(清华大学)的公式(3-1-2、3-1-3)&#xff0c;算的参数是这本书的图3-3的。 function []PropagationConstantsMain() clear;clc;close all lambda01.55;%真空或空气中的入射波长&#xff0c;单位um k02*pi/lambda0; m3;%导模阶数(需要人为指定) n11.62;%芯…

Godot 4 源码分析 - 动态导入图片文件

用Godot 4尝试编一个电子书软件&#xff0c;初步效果已经出来&#xff0c;并且通过管道通信接口可以获取、设置属性、调用函数&#xff0c;貌似能处理各种事宜了。 其实不然&#xff0c;外因通过内因起作用&#xff0c;如果没把里面搞明白&#xff0c;功能没有开放出来&#x…

Android 13(T) - Media框架(1)- 总览

从事Android Media开发工作三年有余&#xff0c;刚从萌新变成菜鸟&#xff0c;一路上跌跌撞撞学习&#xff0c;看了很多零零碎碎的知识&#xff0c;为了加深对Android Media框架的理解&#xff0c;决定在这里记录下学习过程中想到的一些问题以及一些思考&#xff0c;也希望对初…

国产颗粒更快更稳,价格厚道的光威天策弈系列DDR4内存条值得安排

想要用最少的费用打造出一台性能强悍的电脑&#xff0c;自己动手DIY组装电脑是个更好的选择&#xff0c;特别是今年硬盘和内存方面降价潮此起彼伏&#xff0c;出现了很多神价&#xff0c;高性能内存和硬盘对平台性能提升的效果也是非常显著的。 相比于传统大厂的内存&#xff0…

心法利器[92] | 谈校招:刷题和笔试准备

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

Linux NUMA架构(非统一内存访问)

NUMA架构 NUMA Architecture| Non Uniform Memory Access Policy/Model | Numa Node Configuration (CPU Affinity) NUMA架构产生的原因 cpu的高速处理功能和内存存储直接的速度会严重影响cpu的性能。传统的计算机单核架构,cpu通过内存总线(内存访问控制器)直接连接到一…

【Linux基础】WSL安装Ubuntu

说明 本文使用的Windows环境是Windows 11 专业版。 WSL现在有二代WSL2&#xff0c;后续都通过WSL2来安装Linux&#xff0c;使用的是Ubuntu发行版&#xff0c;版本是20.04。 安装过程使用了PowerShell&#xff0c;且是管理员权限打开的。 参考适用于 Linux 的 Windows 子系统…

【 Spring AOP学习二】统一功能处理:拦截器异常返回数据格式

目录 一、用户登录权限效验 &#x1f351;1、Spring拦截器实现用户统一登录验证&#xff08;重要&#xff09; &#xff08;1&#xff09;定义一个拦截器 &#xff08;2&#xff09;将自定义拦截器加入到系统配置中 &#x1f351;2、拦截器实现原理 &#x1f351;3、统一…

car tire

汽车轮胎规则参数 小车、轿车轮胎规格参数图解-有驾 半挂车轮胎尺寸多少 货车轮胎尺寸对照表【汽车时代网】