【源码解析】SpringBoot接口参数校验原理

news2024/11/20 4:31:51

使用示例

入门

web接口

@RestController
public class HelloController {

    @PostMapping("/t1")
    public void t1(@Validated @RequestBody Request request) {
        System.out.println(11);
    }
}

实体类

@Data
public class Request {

    @NotEmpty(message = "title不为空")
    private String title;

    @NotEmpty(message = "name不为空")
    private String name;
}

访问接口,会校验messagename的数据

自定义的校验器

注解

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy ={StatusValidator.class})
public @interface Status {
 
    String[] statusType() default {};
 
    String message() default "状态传递有误";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
 
}

StatusValidator自定义的校验器

public class StatusValidator implements ConstraintValidator<Status, Integer> {

    private List<String> typeStatus;

    @Override
    public void initialize(Status constraintAnnotation) {
        typeStatus = Arrays.asList(constraintAnnotation.statusType());
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
        if (value != null) {
            if (!typeStatus.contains(String.valueOf(value))) {
                return false;
            }
        }
        return true;
    }
}

在实体类上添加校验属性

@Data
public class Request {

    @NotEmpty(message = "title不为空")
    private String title;

    @NotEmpty(message = "name不为空")
    private String name;

    @Status(statusType = {"1","2"})
    private Integer loginStatus;
}

非web接口校验

@Service
@Validated
public class HelloService {

    public void sayHello(@Valid Request request) {
        System.out.println(request);
    }
}

嵌套校验

实体类Request,包含嵌套校验的属性User

@Data
public class Request {

    @NotEmpty(message = "title不为空")
    private String title;

    @NotEmpty(message = "name不为空")
    private String name;

    @Status(statusType = {"1","2"})
    private Integer loginStatus;

    @Valid
    @NotNull
    private User user;

}

实体类

@Data
public class User {

    @NotEmpty(message = "密码不能为空")
    private String password;

    @Max(value = 1)
    private Integer age;
}

分组校验

校验属性设置分组

@Data
public class Request {

    @NotEmpty(message = "title不为空", groups = Group1.class)
    private String title;

    @NotEmpty(message = "name不为空", groups = Group2.class)
    private String name;

    @Status(statusType = {"1","2"})
    private Integer loginStatus;

    private User user;


    public interface Group1 {
    }

    public interface Group2 {
    }
}


对参数进行分组校验

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @PostMapping("/t1")
    public void t1(@Validated(Request.Group1.class) @RequestBody Request request) {
        System.out.println(11);
    }
}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理

源码解析

web参数校验

RequestResponseBodyMethodProcessor#resolveArgument,解析接口中的参数。判断校验结果是否有错,抛出异常。MethodArgumentNotValidException属于BindException的一种。

	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		if (binderFactory != null) {
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}

		return adaptArgumentIfNecessary(arg, parameter);
	}

AbstractMessageConverterMethodArgumentResolver#validateIfApplicable,进行校验

	protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
			Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
			if (validationHints != null) {
				binder.validate(validationHints);
				break;
			}
		}
	}

ValidationAnnotationUtils#determineValidationHints,判断是否存在注解,ValidValidated

	public static Object[] determineValidationHints(Annotation ann) {
		Class<? extends Annotation> annotationType = ann.annotationType();
		String annotationName = annotationType.getName();
		if ("javax.validation.Valid".equals(annotationName)) {
			return EMPTY_OBJECT_ARRAY;
		}
		Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
		if (validatedAnn != null) {
			Object hints = validatedAnn.value();
			return convertValidationHints(hints);
		}
		if (annotationType.getSimpleName().startsWith("Valid")) {
			Object hints = AnnotationUtils.getValue(ann);
			return convertValidationHints(hints);
		}
		return null;
	}

	private static Object[] convertValidationHints(@Nullable Object hints) {
		if (hints == null) {
			return EMPTY_OBJECT_ARRAY;
		}
		return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
	}

DataBinder#validate(java.lang.Object...),遍历校验器。

	public void validate(Object... validationHints) {
		Object target = getTarget();
		Assert.state(target != null, "No target to validate");
		BindingResult bindingResult = getBindingResult();
		// Call each validator with the same binding result
		for (Validator validator : getValidators()) {
			if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
				((SmartValidator) validator).validate(target, bindingResult, validationHints);
			}
			else if (validator != null) {
				validator.validate(target, bindingResult);
			}
		}
	}

ValidatorImpl#validate,对实体类进行校验

    public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
        Contracts.assertNotNull(object, Messages.MESSAGES.validatedObjectMustNotBeNull());
        this.sanityCheckGroups(groups);
        Class<T> rootBeanClass = object.getClass();
        BeanMetaData<T> rootBeanMetaData = this.beanMetaDataManager.getBeanMetaData(rootBeanClass);
        if (!rootBeanMetaData.hasConstraints()) {
            return Collections.emptySet();
        } else {
            BaseBeanValidationContext<T> validationContext = this.getValidationContextBuilder().forValidate(rootBeanClass, rootBeanMetaData, object);
            ValidationOrder validationOrder = this.determineGroupValidationOrder(groups);
            BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(this.validatorScopedContext.getParameterNameProvider(), object, validationContext.getRootBeanMetaData(), PathImpl.createRootPath());
            return this.validateInContext(validationContext, valueContext, validationOrder);
        }
    }

ValidatorImpl#validateConstraintsForDefaultGroup,根据组级别获取MetaConstraint

	private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<U, Object> valueContext) {
		final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
		final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();

		// evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions
		for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
			BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
			boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();

			// if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy.
			if ( defaultGroupSequenceIsRedefined ) {
				Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() );
				Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();

				while ( defaultGroupSequence.hasNext() ) {
					for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) {
						boolean validationSuccessful = true;

						for ( Group defaultSequenceMember : groupOfGroups ) {
							validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz,
									metaConstraints, defaultSequenceMember ) && validationSuccessful;
						}

						validationContext.markCurrentBeanAsProcessed( valueContext );

						if ( !validationSuccessful ) {
							break;
						}
					}
				}
			}
			// fast path in case the default group sequence hasn't been redefined
			else {
				Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();
				validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints,
						Group.DEFAULT_GROUP );
				validationContext.markCurrentBeanAsProcessed( valueContext );
			}

			// all constraints in the hierarchy has been validated, stop validation.
			if ( defaultGroupSequenceIsRedefined ) {
				break;
			}
		}
	}

ValidatorImpl#validateConstraintsForSingleDefaultGroupElement,对于每一个MetaConstraint执行校验

	private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext, ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces,
			Class<? super U> clazz, Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {
		boolean validationSuccessful = true;

		valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );

		for ( MetaConstraint<?> metaConstraint : metaConstraints ) {
			// HV-466, an interface implemented more than one time in the hierarchy has to be validated only one
			// time. An interface can define more than one constraint, we have to check the class we are validating.
			final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();
			if ( declaringClass.isInterface() ) {
				Class<?> validatedForClass = validatedInterfaces.get( declaringClass );
				if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) {
					continue;
				}
				validatedInterfaces.put( declaringClass, clazz );
			}

			boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );
			if ( shouldFailFast( validationContext ) ) {
				return false;
			}

			validationSuccessful = validationSuccessful && tmp;
		}
		return validationSuccessful;
	}

SimpleConstraintTree#validateConstraints,获取校验器。

	@Override
	protected void validateConstraints(ValidationContext<?> validationContext,
			ValueContext<?, ?> valueContext,
			Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {

		if ( LOG.isTraceEnabled() ) {
			LOG.tracef(
					"Validating value %s against constraint defined by %s.",
					valueContext.getCurrentValidatedValue(),
					descriptor
			);
		}

		// find the right constraint validator
		ConstraintValidator<B, ?> validator = getInitializedConstraintValidator( validationContext, valueContext );

		// create a constraint validator context
		ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(
				descriptor, valueContext.getPropertyPath()
		);

		// validate
		if ( validateSingleConstraint( valueContext, constraintValidatorContext, validator ).isPresent() ) {
			violatedConstraintValidatorContexts.add( constraintValidatorContext );
		}
	}

ConstraintValidatorManagerImpl#getInitializedValidator,获取校验器

	public <A extends Annotation> ConstraintValidator<A, ?> getInitializedValidator(
			Type validatedValueType,
			ConstraintDescriptorImpl<A> descriptor,
			ConstraintValidatorFactory constraintValidatorFactory,
			HibernateConstraintValidatorInitializationContext initializationContext) {
		Contracts.assertNotNull( validatedValueType );
		Contracts.assertNotNull( descriptor );
		Contracts.assertNotNull( constraintValidatorFactory );
		Contracts.assertNotNull( initializationContext );

		CacheKey key = new CacheKey( descriptor.getAnnotationDescriptor(), validatedValueType, constraintValidatorFactory, initializationContext );

		@SuppressWarnings("unchecked")
		ConstraintValidator<A, ?> constraintValidator = (ConstraintValidator<A, ?>) constraintValidatorCache.get( key );

		if ( constraintValidator == null ) {
			constraintValidator = createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext );
			constraintValidator = cacheValidator( key, constraintValidator );
		}
		else {
			LOG.tracef( "Constraint validator %s found in cache.", constraintValidator );
		}

		return DUMMY_CONSTRAINT_VALIDATOR == constraintValidator ? null : constraintValidator;
	}

	protected <A extends Annotation> ConstraintValidator<A, ?> createAndInitializeValidator(
			Type validatedValueType,
			ConstraintDescriptorImpl<A> descriptor,
			ConstraintValidatorFactory constraintValidatorFactory,
			HibernateConstraintValidatorInitializationContext initializationContext) {

		ConstraintValidatorDescriptor<A> validatorDescriptor = findMatchingValidatorDescriptor( descriptor, validatedValueType );
		ConstraintValidator<A, ?> constraintValidator;

		if ( validatorDescriptor == null ) {
			return null;
		}

		constraintValidator = validatorDescriptor.newInstance( constraintValidatorFactory );
		initializeValidator( descriptor, constraintValidator, initializationContext );

		return constraintValidator;
	}

ConstraintTree#validateSingleConstraint,用校验器执行isValid方法

	protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(
			ValueContext<?, ?> valueContext,
			ConstraintValidatorContextImpl constraintValidatorContext,
			ConstraintValidator<A, V> validator) {
		boolean isValid;
		try {
			@SuppressWarnings("unchecked")
			V validatedValue = (V) valueContext.getCurrentValidatedValue();
			isValid = validator.isValid( validatedValue, constraintValidatorContext );
		}
		catch (RuntimeException e) {
			if ( e instanceof ConstraintDeclarationException ) {
				throw e;
			}
			throw LOG.getExceptionDuringIsValidCallException( e );
		}
		if ( !isValid ) {
			//We do not add these violations yet, since we don't know how they are
			//going to influence the final boolean evaluation
			return Optional.of( constraintValidatorContext );
		}
		return Optional.empty();
	}

非web接口校验

ValidationAutoConfiguration会注入MethodValidationPostProcessor

    @Bean
    @ConditionalOnMissingBean(
        search = SearchStrategy.CURRENT
    )
    public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator, ObjectProvider<MethodValidationExcludeFilter> excludeFilters) {
        FilteredMethodValidationPostProcessor processor = new FilteredMethodValidationPostProcessor(excludeFilters.orderedStream());
        boolean proxyTargetClass = (Boolean)environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
        processor.setProxyTargetClass(proxyTargetClass);
        processor.setValidator(validator);
        return processor;
    }

MethodValidationPostProcessor#afterPropertiesSet,创建切面和拦截器。

	private Class<? extends Annotation> validatedAnnotationType = Validated.class;

	@Override
	public void afterPropertiesSet() {
		Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
		this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
	}

	protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
		return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
	}

AbstractAdvisingBeanPostProcessor#postProcessAfterInitializationMethodValidationPostProcessor继承了AbstractAdvisingBeanPostProcessor,添加到advised 中,通过ProxyFactory 返回代理对象。

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.advisor == null || bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}

		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);

			// Use original ClassLoader if bean class not locally loaded in overriding class loader
			ClassLoader classLoader = getProxyClassLoader();
			if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
				classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
			}
			return proxyFactory.getProxy(classLoader);
		}

		// No proxy needed.
		return bean;
	}

MethodValidationInterceptor#invoke,进行方法校验,校验不通过则报错。可以对参数和返回值进行校验。

	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
		if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
			return invocation.proceed();
		}

		Class<?>[] groups = determineValidationGroups(invocation);

		// Standard Bean Validation 1.1 API
		ExecutableValidator execVal = this.validator.forExecutables();
		Method methodToValidate = invocation.getMethod();
		Set<ConstraintViolation<Object>> result;

		Object target = invocation.getThis();
		Assert.state(target != null, "Target must not be null");

		try {
			result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
		}
		catch (IllegalArgumentException ex) {
			// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
			// Let's try to find the bridged method on the implementation class...
			methodToValidate = BridgeMethodResolver.findBridgedMethod(
					ClassUtils.getMostSpecificMethod(invocation.getMethod(), target.getClass()));
			result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
		}
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

		Object returnValue = invocation.proceed();

		result = execVal.validateReturnValue(target, methodToValidate, returnValue, groups);
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

		return returnValue;
	}

MethodValidationInterceptor#determineValidationGroups,获取方法或者类上的注解Validated

	protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
		Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class);
		if (validatedAnn == null) {
			Object target = invocation.getThis();
			Assert.state(target != null, "Target must not be null");
			validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
		}
		return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
	}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理

在这里插入图片描述

标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理

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

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

相关文章

Qwik 1.0 发布,全栈式 Web 框架

Qwik 是一个全栈式 Web 框架&#xff0c;Qwik 基于 React、Angular 和 Vue 等其他 Web 框架的概念&#xff0c;但以 JavaScript 流等更新的方法脱颖而出&#xff0c;允许以高性能向用户交付复杂的 Web 应用程序。 随着 Web 应用程序变得越来越大&#xff0c;它们的启动性能会下…

百度文心一言在国产模型中倒数?我看懵了

最近几天&#xff0c;我们公众号的社群在纷纷转发一张名为SuperClue 评测的截图。科大讯飞甚至在官号进行了宣传&#xff1a; 由于讯飞星火大模型刚发布&#xff0c;笔者玩的少&#xff0c;它是不是真的是国产最强这个笔者不敢下结论。 各个大模型的研究测试传送门 阿里通义千问…

揭开Facebook数据抓取的面纱,深入了解其运作机制

在互联网时代&#xff0c;数据是一切的基础。而社交媒体作为数据存储与传输的重要渠道&#xff0c;其数据价值不言而喻。 那么&#xff0c;Facebook数据抓取是如何进行的呢&#xff1f; 1.API接口 首先&#xff0c;我们需要了解一些基本的概念。Facebook的API&#xff08;Ap…

微服务#2 Feign 和 Gateway

一. Feign Feign远程调用 Feign的使用步骤 引入依赖 <!--feign客户端依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency> 添加Ena…

数组和指针

文章目录 指针和数组指针为什么要有指针如何理解编址指针的内存布局指针解引用如何将数值存储到指定的内存地址栈随机化 数组数组的内存布局&a[0] VS &a步长问题 指针和数组无关C为何要这样设计 指针数组和数组指针数组元素个数是否是数组类型的一部分?是地址的强制转…

国赛推荐方向!Prompt工程设计大赛

近期&#xff0c;大型语言模型&#xff08; LLM&#xff0c;Large Language Model &#xff09;的魅力&#xff0c;让大家惊艳于 AI 的天花板还能继续抬升。具备“涌现能力”的 LLM &#xff0c;许多能力都将被解锁&#xff0c;带来意想不到的精彩礼物。对于用户来说&#xff0…

maven学习总结

生命周期 每个生命周期的各个环节都是由各种插件完成&#xff01;&#xff01;&#xff01;Maven有三个相互独立的生命周期&#xff08;Maven的这三个生命周期不能看成一个整体&#xff09;&#xff01;&#xff01;&#xff01; 我们在开发中描述的项目的生命周期&#xff0…

Java之阻塞队列和消息队列

目录 一.上节复习 1.什么是单列模式 2.饿汉模式 3.懒汉模式 二.阻塞队列 1.什么是阻塞队列 三.消息队列 1.什么是消息队列 2.消息队列的作用 1.解耦 2.削峰填谷 3.异步 四.JDK中的阻塞队列 1.常见的阻塞队列 2.向阻塞队列中放入元素---put() 3.向阻塞队列中拿出元…

python url拼接的方法

Python的 url是一个常用的文件链接&#xff0c;一个文件包含多个 url&#xff0c;在很多网站中&#xff0c;我们都需要拼接多个 url。 在网上我们经常可以看到关于 Python拼接的方法介绍&#xff0c;但是很多都是非常不完整的&#xff0c;今天我们就来了解一下&#xff0c;比较…

晨控CK-FR208-EIP与欧姆龙PLC工业Ethernet/IP协议通讯指南

CK-FR208-EIP 是一款支持标准工业 Ethernet/IP 协议的多通道工业 RFID 读写器&#xff0c;读卡器 工作频率为 13.56MHZ&#xff0c;支持对 I-CODE 2、I-CODE SLI 等符合 ISO15693 国际标准协议格式标签的读写。 读卡器同时支持标准工业通讯协议 Ethernet/IP&#xff0c;方便用…

Linux使用rsync同步文件

1.rsync的概念 rsync&#xff0c;remote synchronize顾名思义就知道它是一款实现远程同步功能的软件&#xff0c;它在同步文件的同时&#xff0c;可以保持原来文件的权限、时间、软硬链接等附加信息。 2.查看rsync 查看服务器端rsync版本 rsync --version rsync命令选项 -…

从GFS到GPT,AI Infra的激荡20年

导读 最近AIGC和LLM的浪潮层层迭起&#xff0c;大有把AI行业过去十年画的饼&#xff0c;一夜之间完全变现的势头。而AI Infra&#xff08;构建AI所需的基础设施&#xff09;&#xff0c;也成了讨论的焦点之一。大众对AI Infra的关注点&#xff0c;往往放在AI算力上——比如A100…

创作纪念日|我在CSDN的第365天(内含粉丝福利)

创作纪念日 大家好&#xff0c;我是陈橘又青&#xff0c;最近因为一直在备考&#xff0c;所以没怎么更新博客&#xff0c;今天起来和往常一样看了一眼私信&#xff0c;发现了一条来自CSDN官方的私信。 打开一看&#xff0c;原来是创作一周年的通知&#xff0c;回想起来&#…

Python数据分析:NumPy、Pandas和Matplotlib的使用和实践

在现代数据分析领域中&#xff0c;Python已成为最受欢迎的编程语言之一。Python通过庞大的社区和出色的库支持&#xff0c;成为了数据科学家和分析师的首选语言。在Python的库中&#xff0c;NumPy、Pandas和Matplotlib是三个最为重要的库&#xff0c;它们分别用于处理数值数组、…

基于密度的无线传感器网络聚类算法的博弈分析(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 提高能源效率是无线传感器网络面临的关键挑战之一&#xff0c;无线传感器网络日益普遍。由于节点&#xff08;传感器&#xff…

服务高可用保障:服务限流,Nginx实现服务限流

一、前言 1.1什么是限流&#xff1f; 限流存在于高可用服务中。 用于高可用的保护手段&#xff0c;主要包括&#xff1a;缓存&#xff0c;降级&#xff0c;限流 限流&#xff1a;只允许指定的事件进入系统&#xff0c;超过的部分将被拒绝服务&#xff0c;排队或者降级处理。 …

【零基础学web前端】html学习,表格标签,列表标签,表单标签(form和input),无语义标签div与span

前言: 大家好,我是良辰丫,今天我们就开始进入前端知识的学习&#x1f49e;&#x1f49e; &#x1f9d1;个人主页&#xff1a;良辰针不戳 &#x1f4d6;所属专栏&#xff1a;零基础学web前端 &#x1f34e;励志语句&#xff1a;生活也许会让我们遍体鳞伤&#xff0c;但最终这些伤…

组织学图像弱监督腺体分割的在线简易示例挖掘

文章目录 Online Easy Example Mining for Weakly-Supervised Gland Segmentation from Histology Images摘要本文方法分割 实验结果 Online Easy Example Mining for Weakly-Supervised Gland Segmentation from Histology Images 摘要 背景 开发AI辅助的组织学图像腺体分割方…

DNDC模型在土地利用变化、未来气候变化下的建模方法及温室气体时空动态模拟

由于全球变暖、大气中温室气体浓度逐年增加等问题的出现&#xff0c;“双碳”行动特别是碳中和已经在世界范围形成广泛影响。“十四五”时期&#xff0c;我国生态文明建设进入了以降碳为重点战略方向、推动减污降碳协同增效、促进经济社会发展全面绿色转型、实现生态环境质量改…

除氟树脂,除氟树脂用啥再生,离子交换除氟,矿井水除氟

氟化物选择吸附树脂 Tulsimer CH-87 是一款去除水溶液中氟离子的专用的凝胶型选择性离子交换树脂。它是具有氟化物选择性官能团的交联聚苯乙烯共聚物架构的树脂。 去除氟离子的能力可以达到 1ppm 以下的水平。中性至碱性的PH范围内有较好的工作效率&#xff0c;并且很容易再生…