《手写Spring渐进式源码实践》实践笔记(第十七章 数据类型转换)
文章目录
- 第十七章 数据类型转换工厂设计实现
- 背景
- 技术背景
- Spring数据转换实现方式
- 类型转换器(Converter)接口设计实现
- 业务背景
- 目标
- 设计
- 实现
- 代码结构
- 类图
- 实现步骤
- 测试
- 事先准备
- 属性配置文件
- 转换器工厂Bean
- 测试用例
- 测试结果:
- 总结
第十七章 数据类型转换工厂设计实现
背景
技术背景
Spring数据转换实现方式
Spring提供了多种机制来实现数据转换,以下是一些主要的方式:
-
类型转换器(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会自动在需要的时候使用它。
-
属性编辑器(PropertyEditor):
PropertyEditor
是Java的一个老机制,Spring也支持它。通过实现PropertyEditor
接口,你可以定义如何将字符串转换为其他类型。不过,从Spring 5开始,推荐使用Converter
接口,因为PropertyEditor
将在未来版本中被弃用。 -
格式化程序(Formatter):
Formatter
是Java的一个接口,用于格式化和解析对象。Spring通过FormattingConversionService
支持Formatter
,可以在Spring MVC中使用它来格式化和解析数据。 -
自动应用的转换器(Global Converters): 在Spring MVC中,可以通过实现
WebMvcConfigurer
接口的addFormatters
方法来添加全局的转换器,这些转换器会对所有的控制器方法参数和返回值进行转换。java
@Override public void addFormatters(FormatterRegistry registry) {registry.addConverter(new StringToIntegerConverter()); }
-
局部转换器(Local Converters): 除了全局转换器,你还可以在方法级别使用
@InitBinder
注解来添加局部转换器,这些转换器只对特定的控制器或请求参数有效。 -
Spring Expression Language(SpEL): SpEL也支持类型转换,当你在表达式中使用不同类型的值时,SpEL会尝试进行适当的类型转换。
-
Java标准转换: Spring还会利用Java的自动装箱和拆箱特性,以及
Number
类的intValue
、longValue
等方法来进行基本的类型转换。 -
自定义转换服务: 你可以通过编程方式创建
ConversionService
的实例,并注册自定义的转换器,然后在你的应用程序中使用这个服务来进行类型转换。
类型转换器(Converter)接口设计实现
Spring 中的类型转换器(Converter)接口的底层生效思路主要涉及以下几个方面:
-
接口定义:
Converter<S, T>
是一个函数式接口,用于将类型S
的对象转换为类型T
的对象。这个接口定义了一个convert
方法,该方法接受一个类型为S
的源对象,并返回一个类型为T
的目标对象。 -
转换器注册: 转换器可以通过实现
Converter
接口并注册到ConversionService
中来生效。ConversionService
是 Spring 中的一个核心接口,负责管理所有的转换器,并根据需要调用适当的转换器来执行类型转换。 -
条件转换器:
ConditionalConverter
接口允许在转换过程中添加条件判断,以决定是否使用特定的转换器。这提供了更细粒度的控制,允许在运行时根据具体情况选择是否应用某个转换器。 -
转换器工厂:
ConverterFactory<S, R>
接口用于创建一系列相关转换器,它允许从类型S
转换到类型R
的子类型。这种设计模式支持了范围转换,即从一种类型到多种可能的子类型的转换。 -
泛型转换器:
GenericConverter
接口提供了在两个或多个类型之间进行转换的能力,它是最灵活但也是最为复杂的转换器SPI接口。GenericConverter
可以处理更复杂的转换场景,比如集合到集合的转换。 -
默认转换服务:
DefaultConversionService
是GenericConversionService
的一个实现,它提供了一个默认的转换服务,包含了许多内置的转换器,覆盖了大多数常用的类型转换场景。如果需要自定义转换逻辑,可以向DefaultConversionService
添加自定义的转换器。 -
转换器查找和缓存: Spring 会根据源类型和目标类型查找合适的转换器。如果找到了匹配的转换器,Spring 会将其缓存起来以提高性能。如果缓存中没有找到匹配的转换器,Spring 会尝试查找并添加新的转换器,然后再次尝试转换。
-
转换器的链式调用:
Converter
接口中的andThen
方法支持转换器的链式调用,允许将多个转换步骤串联起来,先应用一个转换器,然后将结果传递给下一个转换器。
业务背景
- 其实实现到本章节,关于IOC、AOP在日常使用和面试中高频出现的技术点都该涵盖了。那么为了补全整个框架内容的结构,方便大家后续在阅读 Spring 时不至于对类型转换的知识体系陌生,这里添加数据类型转换中关于类型转换器的实现。
- 类型转换也叫做数据转换,比如从String到Integer、从String到Date、从Double到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。
目标
基于当前实现的 Spring 框架,扩展类型转换功能,并熟悉spring组件服务的开发思路。
设计
如果我们来把只是看上去一个简单的类型转换操作抽象成框架,那么它就需要一个标准的接口,谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。
那么在有了这样接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。整体设计结构如下图:

-
首先从工厂出发我们需要实现一个 ConversionServiceFactoryBean 来对类型转换服务进行操作。
-
而实现类型转换的服务,需要定义 Converter 转换类型、ConverterRegistry 注册类型转换功能,另外转换类型的操作较多,所以这里也会需要定义一个类型转换工厂 ConverterFactory 各个具体的转换操作来实现这个工厂接口。
实现
代码结构

源码实现:https://github.com/swg209/spring-study/tree/main/step17-type-converter-factory
类图
实现步骤
-
类型转换接口
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();}} }
-
类型转换服务
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());}
}
-
融入 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
- 配置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);}
}
测试结果:
- 从测试结果可以看到,字符串转日期转换器功能正常。
- 从测试结果可以看到,字符串转数字转换器功能正常。
- 从测试结果可以看到,对象属性值可以正常输出到日志。这个测试内容还是比较简单的,可以自行验证结果,虽然最终的结果看上去比较简单,但整个框架结构实现设计还是蛮复杂的,把这么一个转换操作抽象为接口适配、工厂模型等方式,还是很值得借鉴的。
总结
- 本章节实现的类型转换操作如果只是功能性的开发,就像你自己承接的需求那样,可能只是简单的if判断就搞定了,但放在一个成熟的框架要中要考虑的是可复用性、可扩展性,所以会看到接口的定义、工厂的使用等等设计模式在这里体现。
- 恭喜你,能够坚持学习到这一章节。如果你在学习过程中,每一章节都是通过阅读文章、编写代码、调试错误以及理解设计来完成的,那么一定会在这个过程中获得很多宝贵的经验和知识。以后再次阅读Spring源码时,就不会觉得那么困难了。
参考书籍:《手写Spring渐进式源码实践》
书籍源代码:https://github.com/fuzhengwei/small-spring
相关文章:

《手写Spring渐进式源码实践》实践笔记(第十七章 数据类型转换)
文章目录 第十七章 数据类型转换工厂设计实现背景技术背景Spring数据转换实现方式类型转换器(Converter)接口设计实现 业务背景 目标设计实现代码结构类图实现步骤 测试事先准备属性配置文件转换器工厂Bean测试用例测试结果: 总结 第十七章 数…...
W3C HTML 活动
关于W3C(万维网联盟)的HTML活动,我们可以从HTML的不同版本的发展历程中了解其主要的活跃时期和贡献。 HTML 2.0:这个版本的HTML是由Internet工程工作小组(IETF)的HTML工作组于1996年开发的。它是HTML的早期…...

机器学习—为什么我们需要激活函数
如果我们使用神经网络中每个神经元的线性激活函数,回想一下这个需求预测示例,如果对所有节点使用线性激活函数,在这个神经网络中,事实证明,这个大神经网络将变得与线性回归没有什么不同,所以这将挫败使用神…...
软考系统架构设计师论文:论软件的可靠性评价
试题四 论软件的可靠性评价 软件可靠性评价是软件可靠性活动的重要组成部分,既适用于软件开发过程,也可针对最 终软件系统。在软件开发过程中使用软件可靠性评价,可以使用软件可靠性模型,估计软件当前的可靠性,以确认是否可以终止测试并发布软件,同时还可以预计软件要达…...
C++:线程(thread)的创建、调用及销毁
在 C 中,线程的管理主要依赖于标准库 std::thread,自 C11 起,这一功能被标准化,使得我们能够更加方便地创建、管理和销毁线程。这里我们详细讲解线程的创建、调用和销毁流程。 1. 线程的创建 创建线程通常是为了在单独的线程中执…...

关于随身wifi,看了再决定要不要买!2024年最受欢迎的随身wifi品牌推荐!
话费、流量费缴纳起来肉疼,毕竟不是每个月都有很大需求,主打一个该省省该花花。特别是短租人群、在校学生、出差或旅游的人群、追求高性价比的人群,随身Wifi特别实用,出门当WiFi,在家当宽带,两不耽误&#…...

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

DevCheck Pro手机硬件检测工具v5.33
前言 DevCheck Pro是一款手机硬件和操作系统信息检测查看工具,该软件的功能非常强大,为用户提供了系统、硬件、应用程序、相机、网络、电池等一系列信息查看功能 安装环境 [名称]:DevCheckPro [版本]: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编程实战
文章本天成,妙手偶得之。粹然无疵瑕,岂复须人为?君看古彝器,巧拙两无施。汉最近先秦,固已殊淳漓。胡部何为者,豪竹杂哀丝。后夔不复作,千载谁与期? ——《文章》宋陆游 【哲理】文章…...
Flutter鸿蒙next 中的 Expanded 和 Flexible 使用技巧详解
在 Flutter 开发中,Expanded 和 Flexible 是两个非常常用的布局控件,它们可以帮助开发者更加灵活地管理 UI 布局的空间分配。虽然它们看起来非常相似,但它们的功能和使用场景有所不同。理解这两者的区别,能帮助你在构建复杂 UI 布…...

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

Python | Leetcode Python题解之第539题最小时间差
题目: 题解: 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,作为一款分布式协调服务,提供了丰富的四字命令(也称为四字短语ÿ…...
Error: `slot-scope` are deprecated报错解决
本人新手菜鸡,文章为自己遇到问题的记录,如有错误或不足还请大佬批评指正 问题描述 在Vue3环境下使用slot插槽,出现‘slot-scope’ are deprecated报错问题,经过查找发现,是因为在slot插槽使用中,vue2和vu…...
Excel(图例)中使用上标下标
单元格中 1、在Excel单元格中刷黑要设置成上标的字符,如m2中的2; 2、单击右键,在弹出的对话框中选择“设置单元格格式”; 3、在弹出的“设置单元格格式”对话框中选择上标(或下标); 4、最后…...

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

【学习笔记】Linux系统基础知识 6 —— su命令详解
提示:学习Linux系统基础命令 su 命令详解,包含通过 su 命令切换用户实例 一、前期准备 1.已经正确安装并成功进入Linux系统 说明:本实验采用的 Redhat 系统(因系统不一致,可能部分显示存在差异) 二、学…...
docker-compose命令介绍
docker-compose命令介绍 docker-compose1. docker-compose是什么2. Compose file format3. 命令3.1 服务相关命令upruncreatestartrestartdownstopkillrmpauseunpause 3.2 镜像相关命令3.3 查看相关命令 docker-compose 学了docker,然后就直接去学k8s了。恍恍惚惚几…...
Spring学习笔记_29——@Transactional
Transactional 1. 介绍 Transactional 是 Spring 框架提供的一个注解,用于声明方法或类级别的事务属性。 Spring事务:Spring学习笔记_28——事务-CSDN博客 当你在一个方法或类上使用 Transactional 注解时,Spring 会为该方法或类创建一个…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...