当前位置: 首页 > news >正文

《手写Spring渐进式源码实践》实践笔记(第十七章 数据类型转换)

文章目录

  • 第十七章 数据类型转换工厂设计实现
    • 背景
      • 技术背景
        • Spring数据转换实现方式
        • 类型转换器(Converter)接口设计实现
      • 业务背景
    • 目标
    • 设计
    • 实现
      • 代码结构
      • 类图
      • 实现步骤
    • 测试
      • 事先准备
      • 属性配置文件
      • 转换器工厂Bean
      • 测试用例
      • 测试结果:
    • 总结


第十七章 数据类型转换工厂设计实现

背景

技术背景

Spring数据转换实现方式

Spring提供了多种机制来实现数据转换,以下是一些主要的方式:

  1. 类型转换器(Converter)接口: Spring提供了Converter<S, T>接口,允许开发者自定义转换逻辑。通过实现这个接口,你可以定义如何将源类型S转换为目标类型T。例如:

    @Component
    public class StringToIntegerConverter implements Converter<String, Integer> {@Overridepublic Integer convert(String source) {return Integer.valueOf(source);}
    }
    

    然后,你可以在配置中注册这个转换器,Spring会自动在需要的时候使用它。

  2. 属性编辑器(PropertyEditor)PropertyEditor是Java的一个老机制,Spring也支持它。通过实现PropertyEditor接口,你可以定义如何将字符串转换为其他类型。不过,从Spring 5开始,推荐使用Converter接口,因为PropertyEditor将在未来版本中被弃用。

  3. 格式化程序(Formatter)Formatter是Java的一个接口,用于格式化和解析对象。Spring通过FormattingConversionService支持Formatter,可以在Spring MVC中使用它来格式化和解析数据。

  4. 自动应用的转换器(Global Converters): 在Spring MVC中,可以通过实现WebMvcConfigurer接口的addFormatters方法来添加全局的转换器,这些转换器会对所有的控制器方法参数和返回值进行转换。

    java

    @Override
    public void addFormatters(FormatterRegistry registry) {registry.addConverter(new StringToIntegerConverter());
    }
    
  5. 局部转换器(Local Converters): 除了全局转换器,你还可以在方法级别使用@InitBinder注解来添加局部转换器,这些转换器只对特定的控制器或请求参数有效。

  6. Spring Expression Language(SpEL): SpEL也支持类型转换,当你在表达式中使用不同类型的值时,SpEL会尝试进行适当的类型转换。

  7. Java标准转换: Spring还会利用Java的自动装箱和拆箱特性,以及Number类的intValuelongValue等方法来进行基本的类型转换。

  8. 自定义转换服务: 你可以通过编程方式创建ConversionService的实例,并注册自定义的转换器,然后在你的应用程序中使用这个服务来进行类型转换。

类型转换器(Converter)接口设计实现

Spring 中的类型转换器(Converter)接口的底层生效思路主要涉及以下几个方面:

  1. 接口定义Converter<S, T> 是一个函数式接口,用于将类型 S 的对象转换为类型 T 的对象。这个接口定义了一个 convert 方法,该方法接受一个类型为 S 的源对象,并返回一个类型为 T 的目标对象。

  2. 转换器注册: 转换器可以通过实现 Converter 接口并注册到 ConversionService 中来生效。ConversionService 是 Spring 中的一个核心接口,负责管理所有的转换器,并根据需要调用适当的转换器来执行类型转换。

  3. 条件转换器ConditionalConverter 接口允许在转换过程中添加条件判断,以决定是否使用特定的转换器。这提供了更细粒度的控制,允许在运行时根据具体情况选择是否应用某个转换器。

  4. 转换器工厂ConverterFactory<S, R> 接口用于创建一系列相关转换器,它允许从类型 S 转换到类型 R 的子类型。这种设计模式支持了范围转换,即从一种类型到多种可能的子类型的转换。

  5. 泛型转换器GenericConverter 接口提供了在两个或多个类型之间进行转换的能力,它是最灵活但也是最为复杂的转换器SPI接口。GenericConverter 可以处理更复杂的转换场景,比如集合到集合的转换。

  6. 默认转换服务DefaultConversionServiceGenericConversionService 的一个实现,它提供了一个默认的转换服务,包含了许多内置的转换器,覆盖了大多数常用的类型转换场景。如果需要自定义转换逻辑,可以向 DefaultConversionService 添加自定义的转换器。

  7. 转换器查找和缓存: Spring 会根据源类型和目标类型查找合适的转换器。如果找到了匹配的转换器,Spring 会将其缓存起来以提高性能。如果缓存中没有找到匹配的转换器,Spring 会尝试查找并添加新的转换器,然后再次尝试转换。

  8. 转换器的链式调用Converter 接口中的 andThen 方法支持转换器的链式调用,允许将多个转换步骤串联起来,先应用一个转换器,然后将结果传递给下一个转换器。

业务背景

  1. 其实实现到本章节,关于IOC、AOP在日常使用和面试中高频出现的技术点都该涵盖了。那么为了补全整个框架内容的结构,方便大家后续在阅读 Spring 时不至于对类型转换的知识体系陌生,这里添加数据类型转换中关于类型转换器的实现。
  2. 类型转换也叫做数据转换,比如从String到Integer、从String到Date、从Double到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。

目标

基于当前实现的 Spring 框架,扩展类型转换功能,并熟悉spring组件服务的开发思路。

设计

如果我们来把只是看上去一个简单的类型转换操作抽象成框架,那么它就需要一个标准的接口,谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。

那么在有了这样接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。整体设计结构如下图:

image-20241107163620150
  • 首先从工厂出发我们需要实现一个 ConversionServiceFactoryBean 来对类型转换服务进行操作。

  • 而实现类型转换的服务,需要定义 Converter 转换类型、ConverterRegistry 注册类型转换功能,另外转换类型的操作较多,所以这里也会需要定义一个类型转换工厂 ConverterFactory 各个具体的转换操作来实现这个工厂接口。

实现

代码结构

image-20241107163702901

源码实现:https://github.com/swg209/spring-study/tree/main/step17-type-converter-factory

类图

image-20241107163812431

实现步骤

  1. 类型转换接口

    a. Converter 转换器,提供类型转换方法。

    public interface Converter<S, T> {/*** 将source从 S类型转换为T类型.*/T convert(S source);
    }

    b. ConverterFactory 转换工厂,用于获取转换器。

    public interface ConverterFactory<S, R> {/*** 获取转换器,将source从S类型转换为T类型,其中T也是R的实例。** @param targetType* @param <T>* @return*/<T extends R> Converter<S, T> getConverter(Class<T> targetType);}
    

    c. ConverterRegistry 转换注册中心,用于服务注册转换器、转换工厂。

    public interface ConverterRegistry {void addConverter(Converter<?, ?> converter);void addConverter(GenericConverter converter);void addConverterFactory(ConverterFactory<?, ?> converterFactory);}

    d. ConverterService 转换服务,提供 判断类型能否转换以及转换方法。

    public interface ConversionService {/*** 是否可以转换.** @param sourceType* @param targetType* @return*/boolean canConvert(Class<?> sourceType, Class<?> targetType);/*** 转换.** @param source* @param targetType* @param <T>* @return*/<T> T convert(Object source, Class<T> targetType);
    }
    

    e. GenericConverter 通用转换器,门面接口,并有一个内部类 ConvertiblePair,包装 源类型 与 目标类型。

    public interface GenericConverter {/*** Return the source and target types that this converter can convert between.* 返回此转换器可以在其之间转换的源和目标类型.*/Set<ConvertiblePair> getConvertibleTypes();/*** Convert the source object to the targetType described by the {@code TypeDescriptor}.* 将源对象转换为由{@code TypeDescriptor}描述的targetType.** @param source* @param sourceType* @param targetType* @return*/Object convert(Object source, Class sourceType, Class targetType);/*** Holder for a source-to-target class pair.* 源类型到目标类型的持有者.*/final class ConvertiblePair {private final Class<?> sourceType;private final Class<?> targetType;public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {Assert.notNull(sourceType, "source type must not be null");Assert.notNull(targetType, "target type must not be null");this.sourceType = sourceType;this.targetType = targetType;}public Class<?> getSourceType() {return this.sourceType;}public Class<?> getTargetType() {return this.targetType;}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj == null || obj.getClass() != ConvertiblePair.class) {return false;}ConvertiblePair other = (ConvertiblePair) obj;return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType);}@Overridepublic int hashCode() {return this.sourceType.hashCode() * 31 + this.targetType.hashCode();}}
    }
  2. 类型转换服务

a. GenericConversionService , 实现 ConversionService ,ConverterRegistry, 并添加 根据 转换器或工厂 生成对应类型的 ConvertiblePair 以及 遍历源和目标一系列超类的所有组合,获取转换器 的方法辅助,设有 内部类 ConverterFactoryAdapter ,ConverterAdapter 实现 GenericConverter,用于 适配 通用转换器,方便注册及调用。

public class GenericConversionService implements ConversionService, ConverterRegistry {// 转换器映射表.private Map<ConvertiblePair, GenericConverter> converters = new HashMap<>();@Overridepublic boolean canConvert(Class<?> sourceType, Class<?> targetType) {GenericConverter converter = getConvert(sourceType, targetType);return converter != null;}@Overridepublic <T> T convert(Object source, Class<T> targetType) {Class<?> sourceType = source.getClass();GenericConverter converter = getConvert(sourceType, targetType);return (T) converter.convert(source, sourceType, targetType);}@Overridepublic void addConverter(Converter<?, ?> converter) {GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter);ConverterAdapter converterAdapter = new ConverterAdapter(typeInfo, converter);for (GenericConverter.ConvertiblePair convertibleType : converterAdapter.getConvertibleTypes()) {converters.put(convertibleType, converterAdapter);}}@Overridepublic void addConverter(GenericConverter converter) {// 遍历转换器支持的类型,将其添加到转换器映射表中.for (GenericConverter.ConvertiblePair convertibleType : converter.getConvertibleTypes()) {converters.put(convertibleType, converter);}}@Overridepublic void addConverterFactory(ConverterFactory<?, ?> converterFactory) {ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory);ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory);for (ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {converters.put(convertibleType, converterFactoryAdapter);}}private GenericConverter getConvert(Class<?> sourceType, Class<?> targetType) {List<Class<?>> sourceCandidates = getClassHierarchy(sourceType);List<Class<?>> targetCandidates = getClassHierarchy(targetType);for (Class<?> sourceCandidate : sourceCandidates) {for (Class<?> targetCandidate : targetCandidates) {ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);GenericConverter converter = converters.get(convertiblePair);if (converter != null) {return converter;}}}return null;}private List<Class<?>> getClassHierarchy(Class<?> clazz) {List<Class<?>> hierarchy = new ArrayList<>();while (clazz != null) {hierarchy.add(clazz);clazz = clazz.getSuperclass();}return hierarchy;}//因为要同时兼容 Converter、ConverterFactory,所以这里传参为Object.private ConvertiblePair getRequiredTypeInfo(Object object) {Type[] types = object.getClass().getGenericInterfaces();ParameterizedType parameterizedType = (ParameterizedType) types[0];Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();Class sourceType = (Class) actualTypeArguments[0];Class targetType = (Class) actualTypeArguments[1];return new ConvertiblePair(sourceType, targetType);}private final class ConverterAdapter implements GenericConverter {private final ConvertiblePair typeInfo;private final Converter<Object, Object> converter;public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {this.typeInfo = typeInfo;this.converter = (Converter<Object, Object>) converter;}@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {return Collections.singleton(typeInfo);}@Overridepublic Object convert(Object source, Class sourceType, Class targetType) {return converter.convert(source);}}private final class ConverterFactoryAdapter implements GenericConverter {private final ConvertiblePair typeInfo;private final ConverterFactory<Object, Object> converterFactory;public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {this.typeInfo = typeInfo;this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;}@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {return Collections.singleton(typeInfo);}@Overridepublic Object convert(Object source, Class sourceType, Class targetType) {return converterFactory.getConverter(targetType).convert(source);}}
}

b. DefaultConversionService ,继承 GenericConversionService ,通过构造函数注册 内置的 类型转换器或工厂, 并对外作为一个 注册中心使用。

public class DefaultConversionService extends GenericConversionService {public DefaultConversionService() {addDefaultConverters(this);}public static void addDefaultConverters(ConverterRegistry converterRegistry) {// 添加各类类型转换工厂.converterRegistry.addConverterFactory(new StringToNumberConverterFactory());}
}
  1. 融入 Bean 的生命周期
    a. 类型转换工厂 ConversionServiceFactoryBean,实现 FactoryBean, InitializingBean,这样 在其初始化时就可以 注册类型转换器、工厂。

    public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {@Nullableprivate Set<?> converters;@Nullableprivate GenericConversionService conversionService;@Overridepublic ConversionService getObject() throws Exception {return conversionService;}@Overridepublic Class<?> getObjectType() {return conversionService.getClass();}@Overridepublic boolean isSingleton() {return true;}@Overridepublic void afterPropertiesSet() throws BeansException {this.conversionService = new DefaultConversionService();registerConverters(converters, conversionService);}private void registerConverters(Set<?> converters, ConverterRegistry registry) {if (converters != null) {for (Object converter : converters) {if (converter instanceof GenericConverter) {registry.addConverter((GenericConverter) converter);} else if (converter instanceof Converter<?, ?>) {registry.addConverter((Converter<?, ?>) converter);} else if (converter instanceof ConverterFactory<?, ?>) {registry.addConverterFactory((ConverterFactory<?, ?>) converter);} else {throw new IllegalArgumentException("Each converter object must implement one of the "+ "Converter, ConverterFactory, or GenericConverter interfaces"+ "Unsupported converter type: " + converter.getClass());}}}}public void setConverters(Set<?> converters) {this.converters = converters;}
    }

b. 通过 ConfigurableBeanFactory 添加 设置、获取类型转换服务方法,由 AbstractBeanFactory 实现,使得 在刷新容器 实例化单例对象前,可以优先获取到 ConversionServiceFactoryBean ,并设置 beanfactory 的 类型转换服务;并在 AbstractAutowireCapableBeanFactory 的 createBean 属性填充前 增加 类型转换的操作。

测试

事先准备

多类型属性的类 Husband

Husband

public class Husband {private String wifeName;private LocalDate marriageDate;public String getWifeName() {return wifeName;}public void setWifeName(String wifeName) {this.wifeName = wifeName;}public LocalDate getMarriageDate() {return marriageDate;}public void setMarriageDate(LocalDate marriageDate) {this.marriageDate = marriageDate;}@Overridepublic String toString() {return "Husband{" +"wifeName='" + wifeName + '\'' +", marriageDate=" + marriageDate +'}';}
}

属性配置文件

spring.xml

  1. 配置husband,类型转换服务工厂Bean, 类型转换服务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="husband" class="cn.suwg.springframework.test.bean.Husband"><property name="wifeName" value="老老"/><property name="marriageDate" value="2022-04-14"/></bean><bean id="conversionService" class="cn.suwg.springframework.context.support.ConversionServiceFactoryBean"><property name="converters" ref="converters"/></bean><bean id="converters" class="cn.suwg.springframework.test.converter.ConvertersFactoryBean"/>
</beans>

转换器工厂Bean

ConvertersFactoryBean

public class ConvertersFactoryBean implements FactoryBean<Set<?>> {@Overridepublic Set<?> getObject() throws Exception {HashSet<Object> converters = new HashSet<>();StringToLocalDateConverter stringToLocalDateConverter = new StringToLocalDateConverter("yyyy-MM-dd");converters.add(stringToLocalDateConverter);return converters;}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {return true;}
}

StringToIntegerConverter

public class StringToIntegerConverter implements Converter<String, Integer> {@Overridepublic Integer convert(String source) {return Integer.valueOf(source);}
}

StringToLocalDateConverter

public class StringToLocalDateConverter implements Converter<String, LocalDate> {private final DateTimeFormatter DATE_TIME_FORMATTER;public StringToLocalDateConverter(String pattern) {DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(pattern);}@Overridepublic LocalDate convert(String source) {return LocalDate.parse(source, DATE_TIME_FORMATTER);}
}

测试用例

public class ApiTest {@Testpublic void testConvert() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");Husband husband = applicationContext.getBean("husband", Husband.class);System.out.println("测试结果: " + husband);}//返回转换好的日期. 年-月-日@Testpublic void testStringToLocalDateConverter() {StringToLocalDateConverter converter = new StringToLocalDateConverter("yyyy-MM-dd hh:mm:ss");LocalDate convert = converter.convert("2024-11-07 10:00:11");LocalDate expect = LocalDate.of(2024, 11, 7);Assert.assertEquals(expect, convert);System.out.println(convert);}// 测试字符串转换为整数.@Testpublic void testStringToIntegerConverter() {StringToIntegerConverter converter = new StringToIntegerConverter();Integer convert = converter.convert("12345");Assert.assertEquals(Integer.valueOf(12345), convert);System.out.println(convert);}
}

测试结果:

image-20241107170138185

  • 从测试结果可以看到,字符串转日期转换器功能正常。

image-20241107170120413

  • 从测试结果可以看到,字符串转数字转换器功能正常。

image-20241107172248023

  • 从测试结果可以看到,对象属性值可以正常输出到日志。这个测试内容还是比较简单的,可以自行验证结果,虽然最终的结果看上去比较简单,但整个框架结构实现设计还是蛮复杂的,把这么一个转换操作抽象为接口适配、工厂模型等方式,还是很值得借鉴的。

总结

  • 本章节实现的类型转换操作如果只是功能性的开发,就像你自己承接的需求那样,可能只是简单的if判断就搞定了,但放在一个成熟的框架要中要考虑的是可复用性、可扩展性,所以会看到接口的定义、工厂的使用等等设计模式在这里体现。
  • 恭喜你,能够坚持学习到这一章节。如果你在学习过程中,每一章节都是通过阅读文章、编写代码、调试错误以及理解设计来完成的,那么一定会在这个过程中获得很多宝贵的经验和知识。以后再次阅读Spring源码时,就不会觉得那么困难了。

参考书籍:《手写Spring渐进式源码实践》

书籍源代码:https://github.com/fuzhengwei/small-spring

相关文章:

《手写Spring渐进式源码实践》实践笔记(第十七章 数据类型转换)

文章目录 第十七章 数据类型转换工厂设计实现背景技术背景Spring数据转换实现方式类型转换器&#xff08;Converter&#xff09;接口设计实现 业务背景 目标设计实现代码结构类图实现步骤 测试事先准备属性配置文件转换器工厂Bean测试用例测试结果&#xff1a; 总结 第十七章 数…...

W3C HTML 活动

关于W3C&#xff08;万维网联盟&#xff09;的HTML活动&#xff0c;我们可以从HTML的不同版本的发展历程中了解其主要的活跃时期和贡献。 HTML 2.0&#xff1a;这个版本的HTML是由Internet工程工作小组&#xff08;IETF&#xff09;的HTML工作组于1996年开发的。它是HTML的早期…...

机器学习—为什么我们需要激活函数

如果我们使用神经网络中每个神经元的线性激活函数&#xff0c;回想一下这个需求预测示例&#xff0c;如果对所有节点使用线性激活函数&#xff0c;在这个神经网络中&#xff0c;事实证明&#xff0c;这个大神经网络将变得与线性回归没有什么不同&#xff0c;所以这将挫败使用神…...

软考系统架构设计师论文:论软件的可靠性评价

试题四 论软件的可靠性评价 软件可靠性评价是软件可靠性活动的重要组成部分,既适用于软件开发过程,也可针对最 终软件系统。在软件开发过程中使用软件可靠性评价,可以使用软件可靠性模型,估计软件当前的可靠性,以确认是否可以终止测试并发布软件,同时还可以预计软件要达…...

C++:线程(thread)的创建、调用及销毁

在 C 中&#xff0c;线程的管理主要依赖于标准库 std::thread&#xff0c;自 C11 起&#xff0c;这一功能被标准化&#xff0c;使得我们能够更加方便地创建、管理和销毁线程。这里我们详细讲解线程的创建、调用和销毁流程。 1. 线程的创建 创建线程通常是为了在单独的线程中执…...

关于随身wifi,看了再决定要不要买!2024年最受欢迎的随身wifi品牌推荐!

话费、流量费缴纳起来肉疼&#xff0c;毕竟不是每个月都有很大需求&#xff0c;主打一个该省省该花花。特别是短租人群、在校学生、出差或旅游的人群、追求高性价比的人群&#xff0c;随身Wifi特别实用&#xff0c;出门当WiFi&#xff0c;在家当宽带&#xff0c;两不耽误&#…...

SpringMVC总结 我的学习笔记

SpringMVC总结 我的学习笔记 一、SpringMVC简介1.MVC2.SpringMVC概述3. SpringMVC中的核心组件4.SpringMVC核心架构流程 二、SpringMVC框架实例具体实现使用注解实现 四、数据处理及跳转1.结果跳转方式2.处理器方法的参数与返回值处理提交数据数据显示到前端 五、RestFul风格1.…...

DevCheck Pro手机硬件检测工具v5.33

前言 DevCheck Pro是一款手机硬件和操作系统信息检测查看工具&#xff0c;该软件的功能非常强大&#xff0c;为用户提供了系统、硬件、应用程序、相机、网络、电池等一系列信息查看功能 安装环境 [名称]&#xff1a;DevCheckPro [版本]&#xff1a;5.33 [大小]&a…...

数据分析ReAct工作流

让我用一个数据分析项目的例子来展示plan-and-execute框架的应用。这个例子会涉及数据处理、分析和可视化等任务。 from typing import List, Dict, Any from dataclasses import dataclass import json from enum import Enum import logging from datetime import datetime#…...

Rust-AOP编程实战

文章本天成&#xff0c;妙手偶得之。粹然无疵瑕&#xff0c;岂复须人为&#xff1f;君看古彝器&#xff0c;巧拙两无施。汉最近先秦&#xff0c;固已殊淳漓。胡部何为者&#xff0c;豪竹杂哀丝。后夔不复作&#xff0c;千载谁与期&#xff1f; ——《文章》宋陆游 【哲理】文章…...

Flutter鸿蒙next 中的 Expanded 和 Flexible 使用技巧详解

在 Flutter 开发中&#xff0c;Expanded 和 Flexible 是两个非常常用的布局控件&#xff0c;它们可以帮助开发者更加灵活地管理 UI 布局的空间分配。虽然它们看起来非常相似&#xff0c;但它们的功能和使用场景有所不同。理解这两者的区别&#xff0c;能帮助你在构建复杂 UI 布…...

【微信小游戏学习心得】

这里是引用 微信小游戏学习心得 简介了解微信小游戏理解2d游戏原理数据驱动视图总结 简介 本人通过学习了解微信小游戏&#xff0c;学习微信小游戏&#xff0c;加深了对前端框架&#xff0c;vue和react基于数据驱动视图的理解&#xff0c;及浏览器文档模型和javaScript之间的关…...

Python | Leetcode Python题解之第539题最小时间差

题目&#xff1a; 题解&#xff1a; def getMinutes(t: str) -> int:return ((ord(t[0]) - ord(0)) * 10 ord(t[1]) - ord(0)) * 60 (ord(t[3]) - ord(0)) * 10 ord(t[4]) - ord(0)class Solution:def findMinDifference(self, timePoints: List[str]) -> int:n len…...

Zookeeper运维秘籍:四字命令基础、详解及业务应用全解析

文章目录 一、四字命令基础二、四字命令详解三、四字命令的开启与配置四、结合业务解读四字命令confconsenvi命令Stat命令MNTR命令ruok命令dump命令wchswchp ZooKeeper&#xff0c;作为一款分布式协调服务&#xff0c;提供了丰富的四字命令&#xff08;也称为四字短语&#xff…...

Error: `slot-scope` are deprecated报错解决

本人新手菜鸡&#xff0c;文章为自己遇到问题的记录&#xff0c;如有错误或不足还请大佬批评指正 问题描述 在Vue3环境下使用slot插槽&#xff0c;出现‘slot-scope’ are deprecated报错问题&#xff0c;经过查找发现&#xff0c;是因为在slot插槽使用中&#xff0c;vue2和vu…...

Excel(图例)中使用上标下标

单元格中 1、在Excel单元格中刷黑要设置成上标的字符&#xff0c;如m2中的2&#xff1b; 2、单击右键&#xff0c;在弹出的对话框中选择“设置单元格格式”&#xff1b; 3、在弹出的“设置单元格格式”对话框中选择上标&#xff08;或下标&#xff09;&#xff1b; 4、最后…...

熔断和降级

目录 隔离和降级 FeignClient整合Sentinel 通过Feign设置服务降级 1.创建类实现FallbackFactory接口&#xff0c;并让这个类和使用FeignClient的接口类绑定 2.让order-service服务的feign开启sentinel 3.测试&#xff0c;只开启order-service服务&#xff0c;而不开启user-…...

【学习笔记】Linux系统基础知识 6 —— su命令详解

提示&#xff1a;学习Linux系统基础命令 su 命令详解&#xff0c;包含通过 su 命令切换用户实例 一、前期准备 1.已经正确安装并成功进入Linux系统 说明&#xff1a;本实验采用的 Redhat 系统&#xff08;因系统不一致&#xff0c;可能部分显示存在差异&#xff09; 二、学…...

docker-compose命令介绍

docker-compose命令介绍 docker-compose1. docker-compose是什么2. Compose file format3. 命令3.1 服务相关命令upruncreatestartrestartdownstopkillrmpauseunpause 3.2 镜像相关命令3.3 查看相关命令 docker-compose 学了docker&#xff0c;然后就直接去学k8s了。恍恍惚惚几…...

Spring学习笔记_29——@Transactional

Transactional 1. 介绍 Transactional 是 Spring 框架提供的一个注解&#xff0c;用于声明方法或类级别的事务属性。 Spring事务&#xff1a;Spring学习笔记_28——事务-CSDN博客 当你在一个方法或类上使用 Transactional 注解时&#xff0c;Spring 会为该方法或类创建一个…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...