文章目录
- 一、目标:数据类型转换工厂
- 二、设计:数据类型转换工厂
- 三、实现:数据类型转换工厂
- 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 中是如何实现?
- 类型转换也叫做 数据转换。比如:
String
到Integer
、String
到Date
、Double
到Long
等等。 - 但是这些操作不能在已经使用框架的情况下还需要手动处理,所以要把功能扩展到 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;
}
@Override
public 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);
}
@Override
public 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);
}
Converter
、ConverterFactory
、ConverterRegistry
,都是用于定义类型转换操作的相关接口。
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 conversion
return (T) ((BigDecimal) number).toBigInteger();
} else {
// original value is not a Big* number - use standard long conversion
return (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> {
@Override
public <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
@Nullable
public 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<>();
@Override
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
GenericConverter converter = getConverter(sourceType, targetType);
return converter != null;
}
@Override
public <T> T convert(Object source, Class<T> targetType) {
Class<?> sourceType = source.getClass();
GenericConverter converter = getConverter(sourceType, targetType);
return (T) converter.convert(source, sourceType, targetType);
}
@Override
public 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);
}
}
@Override
public void addConverter(GenericConverter converter) {
for (GenericConverter.ConvertiblePair convertibleType : converter.getConvertibleTypes()) {
converters.put(convertibleType, converter);
}
}
@Override
public 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;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(typeInfo);
}
@Override
public 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;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(typeInfo);
}
@Override
public 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 {
@Nullable
private Set<?> converters;
@Nullable
private GenericConversionService conversionService;
@Override
public ConversionService getObject() throws Exception {
return conversionService;
}
@Override
public Class<?> getObjectType() {
return conversionService.getClass();
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public 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 类型转换抽象接口
*/
@Nullable
ConversionService 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;
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, args);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return (T) getBean(name);
}
@Override
public boolean containsBean(String name) {
return containsBeanDefinition(name);
}
/**
* 根据对象名判断是否包含此对象
*
* @param beanName 对象名称
* @return 是否包含此对象
*/
protected abstract boolean containsBeanDefinition(String beanName);
...
@Override
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public 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;
@Override
public void refresh() throws BeansException {
// 1.创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();
// 2.获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3.添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4.在 Bean 实例化之前,执行 BeanFactoryPostProcess
invokeBeanFactoryPostProcessor(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();
}
...
@Override
public 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();
@Override
protected 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 {
// 实例化Bean
bean = 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_PROTOTYPE
Object 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;
}
@Override
public 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> {
@Override
public 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);
}
@Override
public 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<?>> {
@Override
public Set<?> getObject() throws Exception {
HashSet<Object> converters = new HashSet<>();
StringToLocalDateConverter stringToLocalDateConverter = new StringToLocalDateConverter("yyyy-MM-dd");
converters.add(stringToLocalDateConverter);
return converters;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public 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/beans
http://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
判断就可以了。但是放在一个成熟的框架中要考虑的是可复用性、可扩展性。- 所以会看到接口的定义、工厂的使用等等设计模式的使用。