目标
其实实现到本章节,关于IOC、AOP在日常使用中高频出现的技术点都该涵盖了。那么为了补全整个框架内容的结构,方便读者后续在阅读 Spring 时不至于对类型转换的知识体系陌生,这里再添加一些关于此类知识的实现。
类型转换也可以叫做数据转换,比如从String到Integer、从String到Date、从Dubbo到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。
设计
如果我们来把只是看上去一个简单的类型转换操作抽象成框架,那么它就需要一个标准的接口,谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。那么在有了这样接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。
首先从工厂出发我们需要实现一个 ConversionServiceFactoryBean 来对类型转换服务进行操作。而实现类型转换的服务,需要定义 Converter 转换类型、ConverterRegistry 注册类型转换功能,另外转换类型的操作较多,所以这里也会需要定义一个类型转换工厂 ConverterFactory 各个具体的转换操作来实现这个工厂接口。
实现
定义类型转换接口
package com.qingyun.springframework.core.convert.converter;
/**
* @description: 数据类型转换器
* @author: 張青云
* @create: 2021-08-28 21:02
**/
public interface Converter<S, T> {
/** 数据类型转换,从S到T */
T convert(S source);
}
package com.qingyun.springframework.core.convert.converter;
/**
* @description: 类型转化工厂
* @author: 張青云
* @create: 2021-08-28 21:05
**/
public interface ConverterFactory<S, R>{
/**
* 获取一个类型转换器,将S转换成T(T实现了R)
*/
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
package com.qingyun.springframework.core.convert.converter;
/**
* @description: 类型转换注册接口
* @author: 張青云
* @create: 2021-08-28 21:08
**/
public interface ConverterRegistry {
/**
* 向注册表中注册一个转换器
*/
void addConverter(Converter<?, ?> converter);
/**
* 添加一个通用注册器
*/
void addConverter(GenericConverter converter);
/**
* 增加一个类型转换工厂
*/
void addConverterFactory(ConverterFactory<?, ?> converterFactory);
}
Converter 、 ConverterFactory 、 ConverterRegistry ,都是用于定义类型转换操作的相关接口,后续所有的实现都需要围绕这些接口来实现,具体的代码功能可以进行调试验证。
实现类型转换服务
package com.qingyun.springframework.core.convert.converter;
import cn.hutool.core.lang.Assert;
import java.util.Set;
/**
* @description: 通用的类型转换器
* @author: 張青云
* @create: 2021-08-28 21:17
**/
public interface GenericConverter {
/**
* 获取这个类型转换器可以完成的类型转换类型
*/
Set<ConvertiblePair> getConvertibleTypes();
/**
* 进行数据类型转换
*/
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();
}
}
}
package com.qingyun.springframework.core.convert.support;
import com.qingyun.springframework.core.convert.ConversionService;
import com.qingyun.springframework.core.convert.ConvertException;
import com.qingyun.springframework.core.convert.converter.Converter;
import com.qingyun.springframework.core.convert.converter.ConverterFactory;
import com.qingyun.springframework.core.convert.converter.ConverterRegistry;
import com.qingyun.springframework.core.convert.converter.GenericConverter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
* @description:
* @author: 張青云
* @create: 2021-08-28 21:30
**/
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);
if (converter == null) {
throw new ConvertException(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 addConverterFactory(ConverterFactory<?, ?> converterFactory) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory);
ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory);
for (GenericConverter.ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {
converters.put(convertibleType, converterFactoryAdapter);
}
}
@Override
public void addConverter(GenericConverter converter) {
for (GenericConverter.ConvertiblePair convertibleType : converter.getConvertibleTypes()) {
converters.put(convertibleType, converter);
}
}
/**获取类型转换器的一些信息*/
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);
}
/**
* 获取从类型sourceType到类型targetType的转换器
*/
protected 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;
}
/**
* 获取clazz的类型
*/
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);
}
}
}
package com.qingyun.springframework.core.convert.support;
import com.qingyun.springframework.core.convert.converter.ConverterRegistry;
/**
* @description:
* @author: 張青云
* @create: 2021-08-28 21:29
**/
public class DefaultConversionService extends GenericConversionService{
public DefaultConversionService() {
addDefaultConverters(this);
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
// 添加各类的转换器和类型转换工厂
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(new StringToIntegerConverter());
}
}
DefaultConversionService 是继承 GenericConversionService 的实现类,GenericConversionService 实现了 ConversionService, ConverterRegistry 两个接口,用于 canConvert 判断和转换接口 convert 操作。
创建类型转换工厂
package com.qingyun.springframework.context.support;
import com.qingyun.springframework.beans.factory.FactoryBean;
import com.qingyun.springframework.beans.factory.InitializingBean;
import com.qingyun.springframework.core.convert.ConversionService;
import com.qingyun.springframework.core.convert.converter.Converter;
import com.qingyun.springframework.core.convert.converter.ConverterFactory;
import com.qingyun.springframework.core.convert.converter.ConverterRegistry;
import com.qingyun.springframework.core.convert.converter.GenericConverter;
import com.qingyun.springframework.core.convert.support.DefaultConversionService;
import com.qingyun.springframework.core.convert.support.GenericConversionService;
import com.sun.istack.internal.Nullable;
import java.util.Set;
/**
* @description: 提供创建 ConversionService 工厂
* @author: 張青云
* @create: 2021-08-28 21:37
**/
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();
// 添加默认的类型转换器
DefaultConversionService.addDefaultConverters(this.conversionService);
// 添加用户自定义的类型转换器
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 中在启动的过程加载。
类型转换服务使用
AbstractAutowireCapableBeanFactory类:
protected 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) {
// TODO 没有解决循环依赖问题
// A依赖B,获取B的实例
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
// 如果是FactoryBean则需要再getBean一次,因为上一次只是将FactoryBean当作工厂进行了实例化,
// 再来一次才是通过FactoryBean获取期待的对象
if (value instanceof FactoryBean) {
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);
}
}
在 AbstractAutowireCapableBeanFact ory#applyPropertyValues 填充属性的操作中,具体使用了类型转换的功能。在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 也有同样的属性类型转换操作。
将ConversionService融入到上下文中
AbstractApplicationContext类:
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 实例化之前,执行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
// 然后在接下来的每一个Bean创建的时候都会将这些BeanPostProcessor过一遍
registerBeanPostProcessors(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 FactoryBean) {
conversionService = beanFactory.getBean("conversionService");
}
if (conversionService instanceof ConversionService) {
beanFactory.setConversionService((ConversionService) conversionService);
}
}
// 提前实例化单例Bean对象
beanFactory.preInstantiateSingletons();
}
测试
准备
package com.qingyun.springframework.context.test.type;
import java.time.LocalDate;
/**
* @description:
* @author: 張青云
* @create: 2021-08-28 21:57
**/
public class Husband {
private String wifiName;
private LocalDate marriageDate;
public String getWifiName() {
return wifiName;
}
public void setWifiName(String wifiName) {
this.wifiName = wifiName;
}
public LocalDate getMarriageDate() {
return marriageDate;
}
public void setMarriageDate(LocalDate marriageDate) {
this.marriageDate = marriageDate;
}
@Override
public String toString() {
return "Husband{" +
"wifiName='" + wifiName + '\'' +
", marriageDate=" + marriageDate +
'}';
}
}
自定义类型转换器
package com.qingyun.springframework.context.test.type;
import com.qingyun.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* @description:
* @author: 張青云
* @create: 2021-08-28 21:58
**/
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);
}
}
提供自定义类型转换器的集合
package com.qingyun.springframework.context.test.type;
import com.qingyun.springframework.beans.factory.FactoryBean;
import java.util.HashSet;
import java.util.Set;
/**
* @description:
* @author: 張青云
* @create: 2021-08-28 21:57
**/
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 Set.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
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.qingyun.springframework.context.test.type.Husband">
<property name="wifiName" value="你猜"/>
<property name="marriageDate" value="2021-08-08"/>
</bean>
<bean id="conversionService" class="com.qingyun.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" ref="converters"/>
</bean>
<!--用户自定义类型转换器的集合-->
<bean id="converters" class="com.qingyun.springframework.context.test.type.ConvertersFactoryBean"/>
</beans>
单元测试
@Test
public void test_convert() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:type-test.xml");
Husband husband = applicationContext.getBean("husband", Husband.class);
System.out.println("测试结果:" + husband);
}
结果
可以发现类型转换成功!