手写Spring:第18章-数据类型转换工厂设计实现

文章目录

  • 一、目标:数据类型转换工厂
  • 二、设计:数据类型转换工厂
  • 三、实现:数据类型转换工厂
    • 3.1 工程结构
    • 3.2 数据类型转换工厂类图
    • 3.3 定义类型转换接口
      • 3.3.1 类型转换处理接口
      • 3.3.2 类型转换工厂
      • 3.3.3 通用类型转换接口
      • 3.3.4 类型转换注册接口
    • 3.4 实现类型转换服务
      • 3.4.1 数字工具类
      • 3.4.2 字符串转数字类型转换工厂
      • 3.4.3 类型转换抽象接口
      • 3.4.4 通用类型转换类
      • 3.4.5 实现类型转换服务
      • 3.4.6 创建类型转换工厂
    • 3.5 类型转换服务使用
      • 3.5.1 Bean工厂接口添加方法
      • 3.5.2 配置Bean工厂接口
      • 3.5.3 抽象Bean工厂基类实现Bean工厂接口
      • 3.5.4 应用上下文抽象类
      • 3.5.5 实现默认Bean创建的抽象Bean工厂超类
  • 四、测试:数据类型转换工厂
    • 4.1 添加测试配置
      • 4.1.1 老公类
      • 4.1.2 字符串转Int类型
      • 4.1.3 字符串转LocalDate
      • 4.1.4 类型转换工厂
      • 4.1.5 Spring属性配置文件
    • 4.2 单元测试
      • 4.2.1 单元测试
      • 4.2.2 字符串转Int测试
      • 4.2.3 字符串转数字测试
  • 五、总结:数据类型转换工厂

一、目标:数据类型转换工厂

💡 数据类型转换在 Spring 中是如何实现?

  • 类型转换也叫做 数据转换。比如:StringIntegerStringDateDoubleLong 等等。
  • 但是这些操作不能在已经使用框架的情况下还需要手动处理,所以要把功能扩展到 Spring 框架中。

二、设计:数据类型转换工厂

💡 设计:数据类型转换

  • 将一个简单的类型转换操作抽象成框架,那么它需要一个标准的接口。谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。
  • 那么有了这样的接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。

在这里插入图片描述

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

三、实现:数据类型转换工厂

3.1 工程结构

spring-step-17
|-src|-main| |-java|   |-com.lino.springframework|     |-aop|     | |-aspectj|     | | |-AspectJExpressionPointcut.java|     | | |-AspectJExpressionPointcutAdvisor.java|     | |-framework|     | | |-adapter|     | | | |-MethodBeforeAdviceInterceptor.java|     | | |-autoproxy|     | | | |-DefaultAdvisorAutoProxyCreator.java|     | | |-AopProxy.java|     | | |-Cglib2AopProxy.java|     | | |-JdkDynamicAopProxy.java|     | | |-ProxyFactory.java|     | | |-ReflectiveMethodInvocation.java|     | |-AdvisedSupport.java|     | |-Advisor.java|     | |-BeforeAdvice.java|     | |-ClassFilter.java|     | |-MethodBeforeAdvice.java|     | |-MethodMatcher.java|     | |-Pointcut.java|     | |-PointcutAdvisor.java|     | |-TargetSource.java|     |-beans|     | |-factory|     | | |-annotation|     | | | |-Autowired.java|     | | | |-AutowiredAnnotationBeanPostProcessor.java|     | | | |-Qualifier.java|     | | | |-Value.java|     | | |-config|     | | | |-AutowireCapableBeanFactory.java|     | | | |-BeanDefinition.java|     | | | |-BeanFactoryPostProcessor.java|     | | | |-BeanPostProcessor.java|     | | | |-BeanReference.java|     | | | |-ConfigurableBeanFactory.java|     | | | |-InstantiationAwareBeanPostProcessor.java|     | | | |-SingletonBeanRegistry.java|     | | |-support|     | | | |-AbstractAutowireCapableBeanFactory.java|     | | | |-AbstractBeabDefinitionReader.java|     | | | |-AbstractBeabFactory.java|     | | | |-BeabDefinitionReader.java|     | | | |-BeanDefinitionRegistry.java|     | | | |-CglibSubclassingInstantiationStrategy.java|     | | | |-DefaultListableBeanFactory.java|     | | | |-DefaultSingletonBeanRegistry.java|     | | | |-DisposableBeanAdapter.java|     | | | |-FactoryBeanRegistrySupport.java|     | | | |-InstantiationStrategy.java|     | | | |-SimpleInstantiationStrategy.java|     | | |-xml|     | | | |-XmlBeanDefinitionReader.java|     | | |-Aware.java|     | | |-BeanClassLoaderAware.java|     | | |-BeanFactory.java|     | | |-BeanFactoryAware.java|     | | |-BeanNameAware.java|     | | |-ConfigurableListableBeanFactory.java|     | | |-DisposableBean.java|     | | |-FactoryBean.java|     | | |-HierarcgicalBeanFactory.java|     | | |-InitializingBean.java|     | | |-ListableBeanFactory.java|     | | |-ObjectFactory.java|     | | |-PropertyPlaceholderConfigurer.java|     | |-BeansException.java|     | |-PropertyValue.java|     | |-PropertyValues.java|     |-context|     | |-annotation|     | | |-ClassPathBeanDefinitionScanner.java|     | | |-ClassPathScanningCandidateComponentProvider.java|     | | |-Scope.java|     | |-event|     | | |-AbstractApplicationEventMulticaster.java|     | | |-ApplicationContextEvent.java|     | | |-ApplicationEventMulticaster.java|     | | |-ContextclosedEvent.java|     | | |-ContextRefreshedEvent.java|     | | |-SimpleApplicationEventMulticaster.java|     | |-support|     | | |-AbstractApplicationContext.java|     | | |-AbstractRefreshableApplicationContext.java|     | | |-AbstractXmlApplicationContext.java|     | | |-ApplicationContextAwareProcessor.java|     | | |-ClassPathXmlApplicationContext.java|     | | |-ConversionServiceFactoryBean.java|     | |-ApplicationContext.java|     | |-ApplicationContextAware.java|     | |-ApplicationEvent.java|     | |-ApplicationEventPublisher.java|     | |-ApplicationListener.java|     | |-ConfigurableApplicationContext.java|     |-core|     | |-convert|     | | |-converter|     | | | |-Converter.java|     | | | |-ConverterFactory.java|     | | | |-ConverterRegistry.java|     | | | |-GenericConverter.java|     | | |-support|     | | | |-DefaultConversionService.java|     | | | |-GenericConversionService.java|     | | | |-StringToNumberConverterFactory.java|     | | |-ConversionService|     | |-io|     | | |-ClassPathResource.java|     | | |-DefaultResourceLoader.java|     | | |-FileSystemResource.java|     | | |-Resource.java|     | | |-ResourceLoader.java|     | | |-UrlResource.java|     |-stereotype|     | |-Component.java|     |-util|     | |-ClassUtils.java|     | |-StringValueResolver.java|-test|-java|-com.lino.springframework.test|-bean| |-Husband.java|-converter| |-ConvertersFactoryBean.java| |-StringToIntegerConverter.java| |-StringToLocalDateConverter.java|-ApiTest.java|-resources|-spring.xml

3.2 数据类型转换工厂类图

在这里插入图片描述

  • 首先,通过添加类型转换接口,类型转换工厂和类型转换的具体操作服务,选择需要被转换的类型,如字符串类型转换为数值类型。
  • 然后,通过与 Spring Bean 工厂的整合把类型转换的服务包装进来,便于配置 Bean 对象的属性信息 applyPropertyValues,在填充属性时可以进行自动转换处理。

3.3 定义类型转换接口

3.3.1 类型转换处理接口

Converter.java

package com.lino.springframework.core.convert.converter;/*** @description: 类型转换处理接口*/
public interface Converter<S, T> {/*** 类型转换** @param source 来源对象* @return 转换后的对象*/T convert(S source);
}

3.3.2 类型转换工厂

ConverterFactory.java

package com.lino.springframework.core.convert.converter;/*** @description: 类型转换工厂*/
public interface ConverterFactory<S, R> {/*** 获取类型转换对象** @param targetType 类类型* @param <T>        泛型* @return 类型转换对象*/<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

3.3.3 通用类型转换接口

GenericConverter.java

package com.lino.springframework.core.convert.converter;import cn.hutool.core.lang.Assert;
import java.util.Set;/*** @description: 通用的转换接口*/
public interface GenericConverter {/*** 获取类型转换列表*/Set<ConvertiblePair> getConvertibleTypes();/*** 类型转换** @param source     目标对象* @param sourceType 目标类型* @param targetType 转换类型* @return 转换后的对象*/Object convert(Object source, Class sourceType, Class targetType);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();}}
}

3.3.4 类型转换注册接口

ConverterRegistry.java

package com.lino.springframework.core.convert.converter;/*** @description: 类型转换注册接口*/
public interface ConverterRegistry {/*** 添加类型转换对象** @param converter 类型转换对象*/void addConverter(Converter<?, ?> converter);/*** 添加类型转换对象** @param converter 通用的转换接口*/void addConverter(GenericConverter converter);/*** 添加类型转换工厂** @param converterFactory 类型转换工厂*/void addConverterFactory(ConverterFactory<?, ?> converterFactory);
}
  • ConverterConverterFactoryConverterRegistry,都是用于定义类型转换操作的相关接口。

3.4 实现类型转换服务

3.4.1 数字工具类

NumberUtils.java

package com.lino.springframework.util;import cn.hutool.core.lang.Assert;
import com.sun.istack.internal.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;/*** @description: 数字工具类*/
public class NumberUtils {private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);/*** Standard number types (all immutable):* Byte, Short, Integer, Long, BigInteger, Float, Double, BigDecimal.*/public static final Set<Class<?>> STANDARD_NUMBER_TYPES;static {Set<Class<?>> numberTypes = new HashSet<>(8);numberTypes.add(Byte.class);numberTypes.add(Short.class);numberTypes.add(Integer.class);numberTypes.add(Long.class);numberTypes.add(BigInteger.class);numberTypes.add(Float.class);numberTypes.add(Double.class);numberTypes.add(BigDecimal.class);STANDARD_NUMBER_TYPES = Collections.unmodifiableSet(numberTypes);}/*** Convert the given number into an instance of the given target class.** @param number      the number to convert* @param targetClass the target class to convert to* @return the converted number* @throws IllegalArgumentException if the target class is not supported*                                  (i.e. not a standard Number subclass as included in the JDK)* @see Byte* @see Short* @see Integer* @see Long* @see BigInteger* @see Float* @see Double* @see BigDecimal*/@SuppressWarnings("unchecked")public static <T extends Number> T convertNumberToTargetClass(Number number, Class<T> targetClass)throws IllegalArgumentException {Assert.notNull(number, "Number must not be null");Assert.notNull(targetClass, "Target class must not be null");if (targetClass.isInstance(number)) {return (T) number;} else if (Byte.class == targetClass) {long value = checkedLongValue(number, targetClass);if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {raiseOverflowException(number, targetClass);}return (T) Byte.valueOf(number.byteValue());} else if (Short.class == targetClass) {long value = checkedLongValue(number, targetClass);if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {raiseOverflowException(number, targetClass);}return (T) Short.valueOf(number.shortValue());} else if (Integer.class == targetClass) {long value = checkedLongValue(number, targetClass);if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {raiseOverflowException(number, targetClass);}return (T) Integer.valueOf(number.intValue());} else if (Long.class == targetClass) {long value = checkedLongValue(number, targetClass);return (T) Long.valueOf(value);} else if (BigInteger.class == targetClass) {if (number instanceof BigDecimal) {// do not lose precision - use BigDecimal's own conversionreturn (T) ((BigDecimal) number).toBigInteger();} else {// original value is not a Big* number - use standard long conversionreturn (T) BigInteger.valueOf(number.longValue());}} else if (Float.class == targetClass) {return (T) Float.valueOf(number.floatValue());} else if (Double.class == targetClass) {return (T) Double.valueOf(number.doubleValue());} else if (BigDecimal.class == targetClass) {// always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double)// (see BigDecimal javadoc for details)return (T) new BigDecimal(number.toString());} else {throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" +number.getClass().getName() + "] to unsupported target class [" + targetClass.getName() + "]");}}/*** Check for a {@code BigInteger}/{@code BigDecimal} long overflow* before returning the given number as a long value.** @param number      the number to convert* @param targetClass the target class to convert to* @return the long value, if convertible without overflow* @throws IllegalArgumentException if there is an overflow* @see #raiseOverflowException*/private static long checkedLongValue(Number number, Class<? extends Number> targetClass) {BigInteger bigInt = null;if (number instanceof BigInteger) {bigInt = (BigInteger) number;} else if (number instanceof BigDecimal) {bigInt = ((BigDecimal) number).toBigInteger();}// Effectively analogous to JDK 8's BigInteger.longValueExact()if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) {raiseOverflowException(number, targetClass);}return number.longValue();}/*** Raise an <em>overflow</em> exception for the given number and target class.** @param number      the number we tried to convert* @param targetClass the target class we tried to convert to* @throws IllegalArgumentException if there is an overflow*/private static void raiseOverflowException(Number number, Class<?> targetClass) {throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" +number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow");}/*** Parse the given {@code text} into a {@link Number} instance of the given* target class, using the corresponding {@code decode} / {@code valueOf} method.* <p>Trims all whitespace (leading, trailing, and in between characters) from* the input {@code String} before attempting to parse the number.* <p>Supports numbers in hex format (with leading "0x", "0X", or "#") as well.** @param text        the text to convert* @param targetClass the target class to parse into* @return the parsed number* @throws IllegalArgumentException if the target class is not supported*                                  (i.e. not a standard Number subclass as included in the JDK)* @see Byte#decode* @see Short#decode* @see Integer#decode* @see Long#decode* @see #decodeBigInteger(String)* @see Float#valueOf* @see Double#valueOf* @see BigDecimal#BigDecimal(String)*/@SuppressWarnings("unchecked")public static <T extends Number> T parseNumber(String text, Class<T> targetClass) {Assert.notNull(text, "Text must not be null");Assert.notNull(targetClass, "Target class must not be null");String trimmed = trimAllWhitespace(text);if (Byte.class == targetClass) {return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));} else if (Short.class == targetClass) {return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));} else if (Integer.class == targetClass) {return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));} else if (Long.class == targetClass) {return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));} else if (BigInteger.class == targetClass) {return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));} else if (Float.class == targetClass) {return (T) Float.valueOf(trimmed);} else if (Double.class == targetClass) {return (T) Double.valueOf(trimmed);} else if (BigDecimal.class == targetClass || Number.class == targetClass) {return (T) new BigDecimal(trimmed);} else {throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");}}/*** Parse the given {@code text} into a {@link Number} instance of the* given target class, using the supplied {@link NumberFormat}.* <p>Trims the input {@code String} before attempting to parse the number.** @param text         the text to convert* @param targetClass  the target class to parse into* @param numberFormat the {@code NumberFormat} to use for parsing (if*                     {@code null}, this method falls back to {@link #parseNumber(String, Class)})* @return the parsed number* @throws IllegalArgumentException if the target class is not supported*                                  (i.e. not a standard Number subclass as included in the JDK)* @see NumberFormat#parse* @see #convertNumberToTargetClass* @see #parseNumber(String, Class)*/public static <T extends Number> T parseNumber(String text, Class<T> targetClass, @Nullable NumberFormat numberFormat) {if (numberFormat != null) {Assert.notNull(text, "Text must not be null");Assert.notNull(targetClass, "Target class must not be null");DecimalFormat decimalFormat = null;boolean resetBigDecimal = false;if (numberFormat instanceof DecimalFormat) {decimalFormat = (DecimalFormat) numberFormat;if (BigDecimal.class == targetClass && !decimalFormat.isParseBigDecimal()) {decimalFormat.setParseBigDecimal(true);resetBigDecimal = true;}}try {Number number = numberFormat.parse(trimAllWhitespace(text));return convertNumberToTargetClass(number, targetClass);} catch (ParseException ex) {throw new IllegalArgumentException("Could not parse number: " + ex.getMessage());} finally {if (resetBigDecimal) {decimalFormat.setParseBigDecimal(false);}}} else {return parseNumber(text, targetClass);}}public static String trimAllWhitespace(String str) {if (!hasLength(str)) {return str;}int len = str.length();StringBuilder sb = new StringBuilder(str.length());for (int i = 0; i < len; i++) {char c = str.charAt(i);if (!Character.isWhitespace(c)) {sb.append(c);}}return sb.toString();}public static boolean hasLength(@Nullable String str) {return (str != null && !str.isEmpty());}/*** Determine whether the given {@code value} String indicates a hex number,* i.e. needs to be passed into {@code Integer.decode} instead of* {@code Integer.valueOf}, etc.*/private static boolean isHexNumber(String value) {int index = (value.startsWith("-") ? 1 : 0);return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index));}/*** Decode a {@link BigInteger} from the supplied {@link String} value.* <p>Supports decimal, hex, and octal notation.** @see BigInteger#BigInteger(String, int)*/private static BigInteger decodeBigInteger(String value) {int radix = 10;int index = 0;boolean negative = false;// Handle minus sign, if present.if (value.startsWith("-")) {negative = true;index++;}// Handle radix specifier, if present.if (value.startsWith("0x", index) || value.startsWith("0X", index)) {index += 2;radix = 16;} else if (value.startsWith("#", index)) {index++;radix = 16;} else if (value.startsWith("0", index) && value.length() > 1 + index) {index++;radix = 8;}BigInteger result = new BigInteger(value.substring(index), radix);return (negative ? result.negate() : result);}}

3.4.2 字符串转数字类型转换工厂

StringToNumberConverterFactory.java

package com.lino.springframework.core.convert.support;import com.lino.springframework.core.convert.converter.Converter;
import com.lino.springframework.core.convert.converter.ConverterFactory;
import com.lino.springframework.util.NumberUtils;
import com.sun.istack.internal.Nullable;/*** @description: 字符串转数字类型转换工厂*/
public class StringToNumberConverterFactory implements ConverterFactory<String, Number> {@Overridepublic <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {return new StringToNumber<>(targetType);}private static class StringToNumber<T extends Number> implements Converter<String, T> {private final Class<T> targetType;public StringToNumber(Class<T> targetType) {this.targetType = targetType;}@Override@Nullablepublic T convert(String source) {if (source.isEmpty()) {return null;}return NumberUtils.parseNumber(source, this.targetType);}}
}

3.4.3 类型转换抽象接口

ConversionService.java

package com.lino.springframework.core.convert;import com.sun.istack.internal.Nullable;/*** @description: 类型转换抽象接口*/
public interface ConversionService {/*** 判断类型转换** @param sourceType 目标对象类型* @param targetType 结果对象类型* @return 是否转换*/boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);/*** 类型转换** @param source     目标对象* @param targetType 结果类型* @param <T>        泛型* @return 转换后的对象*/<T> T convert(Object source, Class<T> targetType);
}

3.4.4 通用类型转换类

GenericConversionService.java

package com.lino.springframework.core.convert.support;import com.lino.springframework.core.convert.ConversionService;
import com.lino.springframework.core.convert.converter.Converter;
import com.lino.springframework.core.convert.converter.ConverterFactory;
import com.lino.springframework.core.convert.converter.ConverterRegistry;
import com.lino.springframework.core.convert.converter.GenericConverter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;/*** @description: 通用类型转换类*/
public class GenericConversionService implements ConversionService, ConverterRegistry {private Map<GenericConverter.ConvertiblePair, GenericConverter> converters = new HashMap<>();@Overridepublic boolean canConvert(Class<?> sourceType, Class<?> targetType) {GenericConverter converter = getConverter(sourceType, targetType);return converter != null;}@Overridepublic <T> T convert(Object source, Class<T> targetType) {Class<?> sourceType = source.getClass();GenericConverter converter = getConverter(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) {GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory);ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory);for (GenericConverter.ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {converters.put(convertibleType, converterFactoryAdapter);}}private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object object) {Type[] types = object.getClass().getGenericInterfaces();ParameterizedType parameterized = (ParameterizedType) types[0];Type[] actualTypeArguments = parameterized.getActualTypeArguments();Class sourceType = (Class) actualTypeArguments[0];Class targetType = (Class) actualTypeArguments[1];return new GenericConverter.ConvertiblePair(sourceType, targetType);}private GenericConverter getConverter(Class<?> sourceType, Class<?> targetType) {List<Class<?>> sourceCandidates = getClassHierarchy(sourceType);List<Class<?>> targetCandidates = getClassHierarchy(targetType);for (Class<?> sourceCandidate : sourceCandidates) {for (Class<?> targetCandidate : targetCandidates) {GenericConverter.ConvertiblePair convertiblePair = new GenericConverter.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;}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);}}
}

3.4.5 实现类型转换服务

DefaultConversionService.java

package com.lino.springframework.core.convert.support;import com.lino.springframework.core.convert.converter.ConverterRegistry;/*** @description: 实现类型转换服务*/
public class DefaultConversionService extends GenericConversionService {public DefaultConversionService() {addDefaultConverters(this);}public static void addDefaultConverters(ConverterRegistry converterRegistry) {// 添加各类类型转换工厂converterRegistry.addConverterFactory(new StringToNumberConverterFactory());}
}
  • DefaultConversionService 继承 GenericConversionService 的实现类,而 GenericConversionService 实现了 ConversionService, ConverterRegistry 两个接口,用于 canConvert 判断和转换接口 convert 操作。

3.4.6 创建类型转换工厂

ConversionServiceFactoryBean.java

package com.lino.springframework.context.support;import com.lino.springframework.beans.factory.FactoryBean;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.core.convert.ConversionService;
import com.lino.springframework.core.convert.converter.Converter;
import com.lino.springframework.core.convert.converter.ConverterFactory;
import com.lino.springframework.core.convert.converter.ConverterRegistry;
import com.lino.springframework.core.convert.converter.GenericConverter;
import com.lino.springframework.core.convert.support.DefaultConversionService;
import com.lino.springframework.core.convert.support.GenericConversionService;
import com.sun.istack.internal.Nullable;
import java.util.Set;/*** @description: 提供创建 ConversionService 工厂*/
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 Exception {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");}}}}public void setConverters(Set<?> converters) {this.converters = converters;}
}
  • 有了 FactoryBean 的实现就可以完成工程对象的操作,可以提供出转换对象的服务 GenericConversionService
  • 另外在 afterPropertiesSet 中调用了注册操作的类。最终这个类会被配置到 spring.xml 中在启动的过程加载。

3.5 类型转换服务使用

3.5.1 Bean工厂接口添加方法

BeanFactory.java

package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;/*** @description: 定义 Bean 工厂接口*/
public interface BeanFactory {/*** 返回 Bean 的实例对象** @param name 要检索的bean的名称* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name) throws BeansException;/*** 返回含构造函数的 Bean 实例对象** @param name 要检索的bean的名称* @param args 构造函数入参* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name, Object... args) throws BeansException;/*** 返回指定泛型的对象** @param name         要检索的bean的名称* @param requiredType 类型* @param <T>          泛型* @return 实例化的的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/<T> T getBean(String name, Class<T> requiredType) throws BeansException;/*** 返回指定泛型的对象** @param requiredType 类型* @param <T>          泛型* @return 实例化的的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/<T> T getBean(Class<T> requiredType) throws BeansException;/*** 根据对象名称判断是否包含此对象** @param name 对象名称* @return 是否包含*/boolean containsBean(String name);
}
  • 添加 containsBean(String name) 方法判断是否包含此对象。

3.5.2 配置Bean工厂接口

ConfigurableBeanFactory.java

package com.lino.springframework.beans.factory.config;import com.lino.springframework.beans.factory.HierarchicalBeanFactory;
import com.lino.springframework.core.convert.ConversionService;
import com.lino.springframework.util.StringValueResolver;
import com.sun.istack.internal.Nullable;/*** @description: 配置Bean工厂接口*/
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";/*** 添加修改新实例化 Bean 对象的扩展点** @param beanPostProcessor 新实例化 Bean 对象*/void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);/*** 销毁单例*/void destroySingletons();/*** 添加字符串解析器** @param valueResolver 解析器*/void addEmbeddedValueResolver(StringValueResolver valueResolver);/*** 解析嵌入值** @param value 嵌入值* @return 解析后的结果*/String resolveEmbeddedValue(String value);/*** 设置类型转换抽象接口** @param conversionService 类型转换抽象接口*/void setConversionService(ConversionService conversionService);/*** 获取类型转换抽象接口** @return 类型转换抽象接口*/@NullableConversionService getConversionService();
}

3.5.3 抽象Bean工厂基类实现Bean工厂接口

AbstractBeanFactory.java

package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.FactoryBean;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.lino.springframework.core.convert.ConversionService;
import com.lino.springframework.util.ClassUtils;
import com.lino.springframework.util.StringValueResolver;
import java.util.ArrayList;
import java.util.List;/*** @description: 抽象的 Bean 工厂基类,定义模板方法*/
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<>();private ConversionService conversionService;@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null);}@Overridepublic Object getBean(String name, Object... args) throws BeansException {return doGetBean(name, args);}@Overridepublic <T> T getBean(String name, Class<T> requiredType) throws BeansException {return (T) getBean(name);}@Overridepublic boolean containsBean(String name) {return containsBeanDefinition(name);}/*** 根据对象名判断是否包含此对象** @param beanName 对象名称* @return 是否包含此对象*/protected abstract boolean containsBeanDefinition(String beanName);...@Overridepublic void setConversionService(ConversionService conversionService) {this.conversionService = conversionService;}@Overridepublic ConversionService getConversionService() {return conversionService;}...
}

3.5.4 应用上下文抽象类

AbstractApplicationContext.java

package com.lino.springframework.context.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.context.ConfigurableApplicationContext;
import com.lino.springframework.context.event.ApplicationEventMulticaster;
import com.lino.springframework.context.event.ContextClosedEvent;
import com.lino.springframework.context.event.ContextRefreshedEvent;
import com.lino.springframework.context.event.SimpleApplicationEventMulticaster;
import com.lino.springframework.core.convert.ConversionService;
import com.lino.springframework.core.io.DefaultResourceLoader;
import java.util.Collection;
import java.util.Map;/*** @description: 抽象应用上下文*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";private ApplicationEventMulticaster applicationEventMulticaster;@Overridepublic void refresh() throws BeansException {// 1.创建 BeanFactory,并加载 BeanDefinitionrefreshBeanFactory();// 2.获取 BeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();// 3.添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContextbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 4.在 Bean 实例化之前,执行 BeanFactoryPostProcessinvokeBeanFactoryPostProcessor(beanFactory);// 5.BeanPostProcessor 需要提前与其他 Bean 对象实例化之前执行注册操作registerBeanPostProcessor(beanFactory);// 6.初始化事件发布者initApplicationEventMulticaster();// 7.注册事件监听器registerListeners();// 8.设置类型转换器、提前实例化单例 Bean 对象finishBeanFactoryInitialization(beanFactory);// 9.发布容器刷新完成事件finishRefresh();}protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// 设置类型转换器if (beanFactory.containsBean("conversionService")) {Object conversionService = beanFactory.getBean("conversionService");if (conversionService instanceof ConversionService) {beanFactory.setConversionService((ConversionService) conversionService);}}// 提前实例化单例 Bean 对象beanFactory.preInstantiateSingletons();}...@Overridepublic boolean containsBean(String name) {return getBeanFactory().containsBean(name);}...}
  • AbstractApplicationContext#finishBeanFactoryInitialization 设置类型转换器。

3.5.5 实现默认Bean创建的抽象Bean工厂超类

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.beans.factory.support;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.*;
import com.lino.springframework.beans.factory.config.*;
import com.lino.springframework.core.convert.ConversionService;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** @description: 实现默认bean创建的抽象bean工厂超类*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {// 判断是否返回代理 Bean 对象Object bean = resolveBeforeInstantiation(beanName, beanDefinition);if (null != bean) {return bean;}return doCreateBean(beanName, beanDefinition, args);}protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {Object bean = null;try {// 实例化Beanbean = createBeanInstance(beanDefinition, beanName, args);// 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来if (beanDefinition.isSingleton()) {Object finalBean = bean;addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));}// 实例化后判断boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);if (!continueWithPropertyPopulation) {return bean;}// 在设置Bean属性之前,允许 BeanPostProcessor修改属性值applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);// 给bean填充属性applyPropertyValues(beanName, bean, beanDefinition);// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法bean = initializeBean(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}// 注册实现 DisposableBean 接口的 Bean 对象registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);// 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPEObject exposedObject = bean;if (beanDefinition.isSingleton()) {// 获取代理对象exposedObject = getSingleton(beanName);registerSingletonBean(beanName, exposedObject);}return exposedObject;}...private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {try {PropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {String name = propertyValue.getName();Object value = propertyValue.getValue();if (value instanceof BeanReference) {// A 依赖 B,获取 B 的实例化BeanReference beanReference = (BeanReference) value;value = getBean(beanReference.getBeanName());}// 类型转换else {Class<?> sourceType = value.getClass();Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);ConversionService conversionService = getConversionService();if (conversionService != null) {if (conversionService.canConvert(sourceType, targetType)) {value = conversionService.convert(value, targetType);}}}// 属性填充BeanUtil.setFieldValue(bean, name, value);}} catch (Exception e) {throw new BeansException("Error setting property values: " + beanName + "message: " + e);}}...
}
  • AbstractAutowireCapableBeanFactory#applyPropertyValues 填充属性的操作中,具体使用了类转换的功能。
  • AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 也有同样的属性类型转换操作。

四、测试:数据类型转换工厂

4.1 添加测试配置

4.1.1 老公类

Husband.java

package com.lino.springframework.test.bean;import java.time.LocalDate;/*** @description: 老公类*/
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 +'}';}
}

4.1.2 字符串转Int类型

StringToIntegerConverter.java

package com.lino.springframework.test.converter;import com.lino.springframework.core.convert.converter.Converter;/*** @description: 字符串转int*/
public class StringToIntegerConverter implements Converter<String, Integer> {@Overridepublic Integer convert(String source) {return Integer.valueOf(source);}
}

4.1.3 字符串转LocalDate

StringToLocalDateConverter.java

package com.lino.springframework.test.converter;import com.lino.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;/*** @description: 字符串转LocalDate*/
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);}
}

4.1.4 类型转换工厂

ConvertersFactoryBean.java

package com.lino.springframework.test.converter;import com.lino.springframework.beans.factory.FactoryBean;
import java.util.HashSet;
import java.util.Set;/*** @description: 类型转换工厂对象*/
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;}
}

4.1.5 Spring属性配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="husband" class="com.lino.springframework.test.bean.Husband"><property name="wifeName" value="张三"/><property name="marriageDate" value="2022-12-08"/></bean><bean id="conversionService" class="com.lino.springframework.context.support.ConversionServiceFactoryBean"><property name="converters" ref="converters"/></bean><bean id="converters" class="com.lino.springframework.test.converter.ConvertersFactoryBean"/></beans>

4.2 单元测试

4.2.1 单元测试

ApiTest.java

@Test
public void test_convert() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");Husband husband = applicationContext.getBean("husband", Husband.class);System.out.println("测试结果:" + husband);
}

测试结果

测试结果:Husband{wifeName='张三', marriageDate=2022-12-08}
  • 测试结果看,正常通过了。

4.2.2 字符串转Int测试

ApiTest.java

@Test
public void test_StringToIntegerConverter() {StringToIntegerConverter converter = new StringToIntegerConverter();Integer num = converter.convert("1234");System.out.println("测试结果:" + num);
}

测试结果

测试结果:1234

4.2.3 字符串转数字测试

测试结果:1234

@Test
public void test_StringToNumberConverterFactory() {StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory();Converter<String, Integer> stringToIntegerConverter = converterFactory.getConverter(Integer.class);System.out.println("测试结果:" + stringToIntegerConverter.convert("1234"));Converter<String, Long> stringToLongConverter = converterFactory.getConverter(Long.class);System.out.println("测试结果:" + stringToLongConverter.convert("1234"));
}

测试结果

测试结果:1234
测试结果:1234

五、总结:数据类型转换工厂

  • 本节实现的类型转换操作如果只是功能性的开发,可能只是简单 if 判断就可以了。但是放在一个成熟的框架中要考虑的是可复用性、可扩展性。
    • 所以会看到接口的定义、工厂的使用等等设计模式的使用。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『Bug挖掘机 - 赠书02期』|〖Effective软件测试〗

大家好&#xff0c;我是洋子&#xff0c;前段时间给大家推荐了《测试设计思想》&#xff0c;今天再给大家推荐一本软件测试领域的新书 这本书就比较接地气了&#xff0c;是一本软件测试的入门书籍&#xff0c;但同样适用于1-3年软件测试经验的读者阅读 这本书第一章就用Java代…

LinuxUbuntu安装OpenWAF

Linux&Ubuntu安装OpenWAF 官方GitHub地址 介绍 OpenWAF&#xff08;Web Application Firewall&#xff09;是一个开源的Web应用防火墙&#xff0c;用于保护Web应用程序免受各种网络攻击。它通过与Web服务器集成&#xff0c;监控和过滤对Web应用程序的流量&#xff0c;识…

优化VUE Element UI的上传插件

默认ElmentUI的文件列表只有一个删除按钮&#xff0c;我需要加预览、下载、编辑等&#xff0c;就需要优化显示结果。 优化后没用上传进度条&#xff0c;又加了一个进度条效果 代码 <template><div><el-uploadclass"upload-demo"action"/"…

09_瑞萨GUI(LVGL)移植实战教程之拓展练习

本系列教程配套出有视频教程&#xff0c;观看地址&#xff1a;https://www.bilibili.com/video/BV1gV4y1e7Sg 9. 拓展练习 本节安排三个实验检验学习成果&#xff0c;实验示例源码在资料包的这个位置&#xff1a; DShanMCU-RA6M5配套学习资料\2_配套源码\02_瑞萨电子MCU GUI(…

《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》中文翻译

《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》- 思维树&#xff1a;用大型语言模型有意识地解决问题 论文信息摘要1. 介绍2. 背景3. 思想树&#xff1a;用 LM 有意识地解决问题4. 实验4.1 24 人游戏4.2 创意写作4.3 迷你填字游戏 5. 相关工作6…

基于大规模测量和多任务深度学习的电子鼻系统目标识别、浓度预测和状态判断

Target discrimination, concentration prediction, and status judgment of electronic nose system based on large-scale measurement and multi-task deep learning 摘要 为了实现响应特征的自动提取&#xff0c;简化模型的训练和应用过程&#xff0c;设计了一种双块知识…

【数据结构--二叉树】平衡二叉树

题目描述&#xff1a; 代码实现&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int TreeHeight(struct TreeNode* root) {if(rootNULL)return 0;//左右子树中大的…

Linux 中的 chpasswd 命令及示例

chpasswd命令用于更改密码,尽管passwd命令也可以执行相同的操作。但它一次更改一个用户的密码,因此对于多个用户,使用chpasswd 。下图显示了passwd命令的使用。使用passwd我们正在更改来宾用户的密码。首先,您必须输入当前签名用户的密码,然后更改任何其他用户的密码。必须…

Java认识异常(超级详细)

目录 异常的概念和体系结构 异常的概念 异常的体系结构 异常的分类 1.编译时异常 2.运行时异常 异常的处理 防御式编程 LBYL EAFP 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 finally 异常的处理流程 异常的概念和体系结构 异常的概念 在Java中…

RabbtiMQ的安装与在Springboot中的使用!!!

一、安装Erlang与Rabbitmq 安装教程本教程是在centos8下试验的&#xff0c;其实linux系统的都差不多RabbitMQ官方&#xff1a;Messaging that just works — RabbitMQRabbitMQ是开源AMQP实现&#xff0c;服务器端用Erlang语言编写&#xff0c;Python、Ruby、 NET、Java、JMS、c…

二十、MySQL多表关系

1、概述 在项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求以及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种对应关系 2、多表关系分类 &#xff08;1&#xff0…

你用过 Maven Shade 插件吗?

文章首发地址 Maven Shade插件是Maven构建工具的一个插件&#xff0c;用于构建可执行的、可独立运行的JAR包。它解决了依赖冲突的问题&#xff0c;将项目及其所有依赖&#xff08;包括传递依赖&#xff09;合并到一个JAR文件中。 下面是对Maven Shade插件的一些详解&#xff…

202330读书笔记|《中国百年文学经典桥梁书(全8册)》——故乡,匆匆,春,背影,白鹅,百草园

202330读书笔记|《中国百年文学经典桥梁书&#xff08;全8册&#xff09;》——故乡&#xff0c;匆匆&#xff0c;春&#xff0c;背影&#xff0c;白鹅&#xff0c;百草园 《中国百年文学经典桥梁书&#xff08;全8册&#xff09;》作者朱自清&#xff0c;鲁迅等。很多都是小学…

从软件工程师角度聊聊 Kubernetes

作为软件工程师&#xff0c;我们应该熟悉 K8s&#xff0c;尽管它有点像 DevOps&#xff0c;但它能让我们更好地了解幕后发生的事情&#xff0c;让我们与部署工作更密切相关&#xff0c;更有责任感。本文将从软件工程师的角度探讨 Kubernetes (K8s)&#xff0c;我们将介绍其动机…

无涯教程-JavaScript - IMSECH函数

描述 IMSECH函数以x yi或x yj文本格式返回复数的双曲正割。复数的双曲正割被定义为双曲余弦的倒数,即 六(z) 1/cosh(z) 语法 IMSECH (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the hyperbolic secant.Required Notes Ex…

zookeeper/HA集群配置

1.zookeep配置 1.1 安装4台虚拟机 &#xff08;1&#xff09;按照如下设置准备四台虚拟机&#xff0c;其中三台作为zookeeper&#xff0c;配置每台机器相应的IP&#xff0c;hostname&#xff0c;下载vim&#xff0c;ntpdate配置定时器定时更新时间&#xff0c;psmisc&#xff…

linux 进程隔离Namespace 学习

一、linux namespace 介绍 1.1、概念 Linux Namespace是Linux内核提供的一种机制&#xff0c;它用于隔离不同进程的资源视图&#xff0c;使得每个进程都拥有独立的资源空间&#xff0c;从而实现进程之间的隔离和资源管理。 Linux Namespace的设计目标是为了解决多个进程之间…

Android Jetpack架构组件库:Hilt

一、开发者官网关于Hilt库使用链接如下 使用 Hilt 实现依赖项注入 Hilt版本说明 二、工程目录图 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 代码&#xff1a;LearnJetpack-hilt&#xff1a;hilt版本2.48 代…

国标GB28181协议视频平台EasyGBS国标平台设备播放断流现象的排查分析及解决

EasyGBS平台基于GB28181国标协议&#xff0c;支持多路设备接入&#xff0c;并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等多种格式的视频流。平台可为大数据等综合性监管平台提供极强的视频能力&#xff0c;已经在大量的项目中落地应用&#xff0c;如明厨亮灶、平安…

【C++心愿便利店】No.5---构造函数和析构函数

文章目录 前言一、类的6个默认成员函数二、构造函数三、析构函数 前言 &#x1f467;个人主页&#xff1a;小沈YO. &#x1f61a;小编介绍&#xff1a;欢迎来到我的乱七八糟小星球&#x1f31d; &#x1f4cb;专栏&#xff1a;C 心愿便利店 &#x1f511;本章内容&#xff1a;类…