一起学SF框架系列5.12-spring-beans-数据绑定dataBinding
数据绑定有助于将用户输入动态绑定到应用程序的域模型(或用于处理用户输入的任何对象),主要用于web层,但实际可用于任何层。Spring提供了DataBinder来做到这一点,并提供了Validator进行数据验证,两者组成了完整的数据输入数据处理机制。但数据绑定主要应用于 Spring 内部(如在SpringMVC中,http传入/返回参数如何绑定到具体参数或类上,就是通过数据绑定进行的),开发者直接使用的场景并不多。如果想要深入理解 Spring 内部的运行机制,数据绑定是必须了解的一块内容。
体验数据绑定
先以如下例子体验数据绑定:
@Component
public class DemoDatabinder{public void demo() {// 数据Address Address addr=new Address();addr.setProvince("四川");addr.setCity("成都");addr.setCounty("高新区");addr.setDesc("学习大道168");// 待绑定的数据driverDriver driver=new Driver();/* 准备绑定的数据(实际应用中看做前端输入的数据) */Map<String, Object> input = new HashMap<>();input.put("surname", "wang");input.put("name", "wang");// age是整形 注1input.put("age", 20);// address是对象input.put("address", addr);/* 绑定数据 *///把准备绑定的数据转换成绑定PropertyValues PropertyValues propertyValues = new MutablePropertyValues(input);// 用DataBinder 进行绑定DataBinder dataBinder = new DataBinder(driver);dataBinder.bind(propertyValues); BindingResult br = dataBinder.getBindingResult(); if(br.hasErrors())System.out.println("数据绑定错误");}
}
注1:age是整形,输入值可以是整形20,也可以是字符串“20”或“20 ”(包含空格)
从上例中可看出,数据绑定就是把输入数据(input)绑定到对象(driver)的对应属性上,绑定有以下要求:
1、输入数据名同对象属性名相同。
2、输入数据值的数据类型同对象属性的数据类型可以不一致,绑定会自动转换。
Spring数据绑定
功能
Spring中,DataBinder有两大功能:
1、设置对象属性值。
2、对数据进行校验(数据校验见:https://blog.csdn.net/davidwkx/article/details/131401699)。
作用
Spring作为一个成熟框架,数据绑定是重要特性之一,数据绑定主要作用体现如下:
1、在WEB环境下,Spring可自动把Http中的请求参数或者请求体中的Json字符串绑定到对应实体对象上,这样可大大提高开发人员的效率,这是开发人员直观感知到的数据绑定。非WEB环境,开发人员也可按需使用数据绑定技术,只不过很少使用而已。
2、Spring框架本身也用到数据绑定:在xml中配置bean属性或注解配置属性,也是用的数据绑定来实现的。
类关系图
从图中可以看出,除了DataBinder使用的数据PropertyValues,关系图比较简单,只是实现了PropertyEditorRegistry, TypeConverter。
BindingResult继承于Errors,保存绑定结果,包括错误信息。
PropertyEditorRegistry
设置bean属性值的属性编辑器,代码如下:
public interface PropertyEditorRegistry {// 为给定类型的所有属性设置属性编辑器void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);// 获取给定类型低属性编辑器@NullablePropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
TypeConverter
转换数据到指定的类型,接口定义简单(仅参数不同):
public interface TypeConverter {@Nullable<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;@Nullable<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException;@Nullable<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field) throws TypeMismatchException;@Nullabledefault <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {throw new UnsupportedOperationException("TypeDescriptor resolution not supported");}
}
源码跟踪
DataBinder.bind(PropertyValues pvs)
// 过渡类public void bind(PropertyValues pvs) {MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues mutablePropertyValues ?mutablePropertyValues : new MutablePropertyValues(pvs));doBind(mpvs);}// 绑定处理protected void doBind(MutablePropertyValues mpvs) {// 根据允许绑定的字段检查给定的属性值,删除不允许的字段的值checkAllowedFields(mpvs);// 根据所需绑定字段检查给定的属性值,对于必须有值的字段如果没有值,生成缺少值的错误信息checkRequiredFields(mpvs);// 赋值到对象属性applyPropertyValues(mpvs);}// 过渡类protected void applyPropertyValues(MutablePropertyValues mpvs) {try {// Bind request parameters onto target object.// 赋值到对象属性 注1getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());}catch (PropertyBatchUpdateException ex) {// 绑定过程发生错误,则生成错误放到BindingResultfor (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());}}}
注1:此处实际调用BeanWrapperImpl.setPropertyValues,其类关系图如下:
BeanWrapperImpl是BeanWrapper的默认实现,BeanWrapper是Spring服务于JavaBeans的底层接口,主要提供分析和操作JavaBeans,包括:获取和设置属性值(单独或批量)、获取属性描述符以及查询属性可读性/可写性的能力等。BeanWrapperImpl关系图比较复杂,这里不具体分析,只关注数据绑定用到的实现在AbstractPropertyAccessor.setPropertyValues
AbstractPropertyAccessor.setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
设置对象属性。
@Overridepublic void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)throws BeansException {List<PropertyAccessException> propertyAccessExceptions = null;// 把数据转换成MutablePropertyValues List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues mpvs ?mpvs.getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));if (ignoreUnknown) {this.suppressNotWritablePropertyException = true;}try {// 按数据逐一设置for (PropertyValue pv : propertyValues) {try {setPropertyValue(pv);}catch (NotWritablePropertyException ex) {if (!ignoreUnknown) {throw ex;}// Otherwise, just ignore it and continue...}catch (NullValueInNestedPathException ex) {if (!ignoreInvalid) {throw ex;}// Otherwise, just ignore it and continue...}catch (PropertyAccessException ex) {if (propertyAccessExceptions == null) {propertyAccessExceptions = new ArrayList<>();}propertyAccessExceptions.add(ex);}}}finally {if (ignoreUnknown) {this.suppressNotWritablePropertyException = false;}}// 如果有属性可访问性异常,组合成一个异常抛出if (propertyAccessExceptions != null) {PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);throw new PropertyBatchUpdateException(paeArray);}}// 过渡方法@Overridepublic void setPropertyValue(PropertyValue pv) throws BeansException {// 实现在AbstractNestablePropertyAccessor setPropertyValue(pv.getName(), pv.getValue());}
AbstractNestablePropertyAccessor .setPropertyValue(String propertyName, @Nullable Object value)
// 列出这个类便于后续理解-AbstractNestablePropertyAccessor内部类:用于存储属性token的Holder类protected static class PropertyTokenHolder {public PropertyTokenHolder(String name) {this.actualName = name;this.canonicalName = name;}public String actualName;public String canonicalName;@Nullablepublic String[] keys;}// 本类设置属性值入口@Overridepublic void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {AbstractNestablePropertyAccessor nestedPa;try {// 获取属性访问器nestedPa = getPropertyAccessorForPropertyPath(propertyName);}catch (NotReadablePropertyException ex) {throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,"Nested property in path '" + propertyName + "' does not exist", ex);}// 将属性名称解析为相应的属性名称标记PropertyTokenHolder PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));// setPropertyValue在AbstractNestablePropertyAccessor 实现nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));}// 分两种情况处理protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {if (tokens.keys != null) {// 集合类属性赋值processKeyedProperty(tokens, pv);}else {// 单个(非集合类)属性赋值processLocalProperty(tokens, pv);}}// 集合类属性赋值@SuppressWarnings({"unchecked", "rawtypes"})private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) {Object propValue = getPropertyHoldingValue(tokens);PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);if (ph == null) {throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.actualName, "No property handler found");}Assert.state(tokens.keys != null, "No token keys");String lastKey = tokens.keys[tokens.keys.length - 1];// 属性是数组类型if (propValue.getClass().isArray()) {Class<?> requiredType = propValue.getClass().getComponentType();int arrayIndex = Integer.parseInt(lastKey);Object oldValue = null;try {if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {oldValue = Array.get(propValue, arrayIndex);}// 转换数据为属性值类型Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),requiredType, ph.nested(tokens.keys.length));int length = Array.getLength(propValue);if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {Class<?> componentType = propValue.getClass().getComponentType();Object newArray = Array.newInstance(componentType, arrayIndex + 1);System.arraycopy(propValue, 0, newArray, 0, length);int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');String propName = tokens.canonicalName.substring(0, lastKeyIndex);setPropertyValue(propName, newArray);propValue = getPropertyValue(propName);}// Array赋值Array.set(propValue, arrayIndex, convertedValue);}catch (IndexOutOfBoundsException ex) {throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,"Invalid array index in property path '" + tokens.canonicalName + "'", ex);}}// 属性是list类型else if (propValue instanceof List list) {Class<?> requiredType = ph.getCollectionType(tokens.keys.length);int index = Integer.parseInt(lastKey);Object oldValue = null;if (isExtractOldValueForEditor() && index < list.size()) {oldValue = list.get(index);}Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),requiredType, ph.nested(tokens.keys.length));int size = list.size();if (index >= size && index < this.autoGrowCollectionLimit) {for (int i = size; i < index; i++) {try {list.add(null);}catch (NullPointerException ex) {throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,"Cannot set element with index " + index + " in List of size " +size + ", accessed using property path '" + tokens.canonicalName +"': List does not support filling up gaps with null elements");}}// list赋值-增加list.add(convertedValue);}else {try {// list赋值-增加或修改list.set(index, convertedValue);}catch (IndexOutOfBoundsException ex) {throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,"Invalid list index in property path '" + tokens.canonicalName + "'", ex);}}}// 属性是Map 类型else if (propValue instanceof Map map) {Class<?> mapKeyType = ph.getMapKeyType(tokens.keys.length);Class<?> mapValueType = ph.getMapValueType(tokens.keys.length);// IMPORTANT: Do not pass full property name in here - property editors// must not kick in for map keys but rather only for map values.TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);Object convertedMapKey = convertIfNecessary(null, null, lastKey, mapKeyType, typeDescriptor);Object oldValue = null;if (isExtractOldValueForEditor()) {oldValue = map.get(convertedMapKey);}// Pass full property name and old value in here, since we want full// conversion ability for map values.Object convertedMapValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),mapValueType, ph.nested(tokens.keys.length));// map赋值map.put(convertedMapKey, convertedMapValue);}else {throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,"Property referenced in indexed property path '" + tokens.canonicalName +"' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");}}// 单个(非集合类)属性赋值private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);if (ph == null || !ph.isWritable()) {if (pv.isOptional()) {if (logger.isDebugEnabled()) {logger.debug("Ignoring optional value for property '" + tokens.actualName +"' - property not found on bean class [" + getRootClass().getName() + "]");}return;}if (this.suppressNotWritablePropertyException) {// Optimization for common ignoreUnknown=true scenario since the// exception would be caught and swallowed higher up anyway...return;}throw createNotWritablePropertyException(tokens.canonicalName);}Object oldValue = null;try {Object originalValue = pv.getValue();Object valueToApply = originalValue;if (!Boolean.FALSE.equals(pv.conversionNecessary)) {/* 数据类型转换 */if (pv.isConverted()) {// 已转换直接取转换后的值valueToApply = pv.getConvertedValue();}else {if (isExtractOldValueForEditor() && ph.isReadable()) {try {oldValue = ph.getValue();}catch (Exception ex) {if (ex instanceof PrivilegedActionException pae) {ex = pae.getException();}if (logger.isDebugEnabled()) {logger.debug("Could not read previous value of property '" +this.nestedPath + tokens.canonicalName + "'", ex);}}}// 数据类型转换valueToApply = convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());}pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);}// 赋值ph.setValue(valueToApply);}catch (TypeMismatchException ex) {throw ex;}catch (InvocationTargetException ex) {PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());if (ex.getTargetException() instanceof ClassCastException) {throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());}else {Throwable cause = ex.getTargetException();if (cause instanceof UndeclaredThrowableException) {// May happen e.g. with Groovy-generated methodscause = cause.getCause();}throw new MethodInvocationException(propertyChangeEvent, cause);}}catch (Exception ex) {PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());throw new MethodInvocationException(pce, ex);}}
注:关于数据类型转换详见:https://blog.csdn.net/davidwkx/article/details/131913840
相关文章:

一起学SF框架系列5.12-spring-beans-数据绑定dataBinding
数据绑定有助于将用户输入动态绑定到应用程序的域模型(或用于处理用户输入的任何对象),主要用于web层,但实际可用于任何层。Spring提供了DataBinder来做到这一点,并提供了Validator进行数据验证,两者组成了…...

火热报名中 | 赛宁独家技术支持第七届“蓝帽杯”网络安全技能大赛
由公安部网络安全保卫局、教育部教育管理信息中心、中国教育协会指导,中国人民公安大学主办,奇安信科技集团股份有限公司协办,南京赛宁信息技术有限公司提供技术支持的2023第七届“蓝帽杯”全国大学生网络安全技能大赛于近日正式开启报名。 …...

无涯教程-jQuery - Ajax Tutorial函数
AJAX是用于创建交互式Web应用程序的Web开发技术。如果您了解JavaScript,HTML,CSS和XML,则只需花费一个小时即可开始使用AJAX。 为什么要学习Ajax? AJAX代表 A 同步 Ja vaScript和 X ML。 AJAX是一项新技术,可借助XML,HTML,CSS和Java Script创建更好,更快,更具交互性的Web应用…...
Android日志
Android中的日志工具类是Log(android.util.Log),这个类中提供了如下5个方法来供我们打印日志。 Log.v()。用于打印那些最为琐碎的、意义最小的日志信息。对应级别verbose,是Android日志里面级别最低的一种。 Log.d()。用于打印一…...

【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
目录 前言 http包的HandleFunc函数 http.Request/http.ResponseWriter httptest 定义被测接口 测试代码 测试执行 总结 资料获取方法 前言 Mock是一个做自动化测试永远绕不过去的话题。本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法。 可能有…...
SpringBoot自定义注解 + AOP+分布式Redis 防止重复提交
第一步 引入依赖pom.xml: <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.3</version> <!-- 使用最新版本 --></dependency><dependency><groupId&g…...
3.yum安装分布式LNMP--剧本
文章目录 修改hosts创建剧本文件 修改hosts vim /etc/ansible/hosts[webservers] 192.168.242.67[dbservers] 192.168.242.68[phpservers] 192.168.242.69创建剧本文件 vim lnmp.yaml- name: nginx playhosts: webserversremote_user: rootvars:- http_port: 192.168.242.67:…...

论文笔记:Fine-Grained Urban Flow Prediction
2021 WWW 1 intro 细粒度城市流量预测 两个挑战 细粒度数据中观察到的网格间的转移动态使得预测变得更加复杂 需要在全局范围内捕获网格单元之间的空间依赖性单独学习外部因素(例如天气、POI、路段信息等)对大量网格单元的影响非常具有挑战性——>论…...

系统集成|第八章(笔记)
目录 第八章 进度管理8.1 主要过程8.1.1 规划进度管理8.1.2 定义活动8.1.3 排列活动顺序8.1.4 估算活动资源8.1.5 估算活动持续时间8.1.6 制定进度计划8.1.7 控制进度 8.2 注意与问题 上篇:第七章、范围管理 第八章 进度管理 8.1 主要过程 包括: 规划进…...

【分布式】分布式唯一 ID 的 几种生成方案以及优缺点snowflake优化方案
在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一…...

FFmpeg5.0源码阅读——av_interleaved_write_frame
摘要:本文主要详细描述FFmpeg中封装时写packet到媒体文件的函数av_interleaved_write_frame的实现。 关键字:av_interleaved_write_frame 读者须知:读者需要熟悉ffmpeg的基本使用。 1 基本调用流程 av_interleaved_write_frame的基本…...

力扣 70. 爬楼梯
题目来源:https://leetcode.cn/problems/climbing-stairs/description/ C题解(来源代码随想录): 本质上是一道斐波那契数题。 动规五部曲:定义一个一维数组来记录不同楼层的状态 确定dp数组以及下标的含义。dp[i]&am…...
AVFoundation - 媒体捕捉
文章目录 注意使用 NSCameraUsageDescriptioniOS 的摄像头可能比 Mac 更多功能特性@interface Capture ()<AVCaptureFileOutputRecordingDelegate>@property (strong, nonatomic) AVCaptureSession *captureSession; @property (weak, nonatomic) AVCaptureDeviceInput *…...

【新版系统架构补充】-嵌入式技术
嵌入式微处理体系结构 冯诺依曼结构 传统计算机采用冯诺依曼结构,也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构 冯诺依曼的计算机程序和数据共用一个存储空间,程序指令存储地址和数据存储地址指向同一个存…...

fpga开发--蜂鸣器发出连续不同的音调
描述 使用fpga蜂鸣器连续发出do,re,mi,fa,so,la,xi七个不同的音调,每个音调的持续时间为0.5s。 思路 采用状态机实现音调的转化,当do状态持续了0.5s之后转移到re状态,…...

Redis 主从同步原理
一、什么是主从同步? 主从同步,就是将数据冗余备份,主库(Master)将自己库中的数据,同步给从库(Slave)。 从库可以一个,也可以多个,如图所示: 二…...

opencv-28 自适应阈值处理-cv2.adaptiveThreshold()
什么是自适应阈值处理? 对于色彩均衡的图像,直接使用一个阈值就能完成对图像的阈值化处理。但是,有时图像的色彩是不均衡的,此时如果只使用一个阈值,就无法得到清晰有效的阈值分割结果图像。 有一种改进的阈值处理技术ÿ…...
Java泛型5——泛型通配符
注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过 目录: Java泛型1——概述Java泛型2——泛型类Java泛型3——泛型接口Java泛型4——泛型方法Java泛型5——泛型通配符Java泛型6——类型擦除 什么是通配符 在Java中,类…...
牛客 AB25 ranko的手表 JAVA 枚举
描述 ranko 的手表坏了,正常应该显示 xx:xx 的形式(4 个数字),比如下午 1 点半应该显示 13:30 ,但现在经常会有一些数字有概率无法显示。 ranko 在 �1t1 时刻看了下时间,过了一段时间在 &am…...

常微分方程建模R包ecode(二)——绘制相速矢量场
本节中我们考虑一个更为复杂的常微分方程模型, d X C d t ν ( X A Y A ) − β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ) ⋅ X C , ( 1 ) d Y C d t β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ρ ) ⋅ Y C , ( 2 ) d X A d t g ⋅ X C − β ⋅ X A ⋅ ( Y C Y A …...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...