Skip to content

Commit

Permalink
类型转换二
Browse files Browse the repository at this point in the history
  • Loading branch information
DerekYRC committed Jan 17, 2021
1 parent 838821e commit 21b831a
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 5 deletions.
77 changes: 77 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1312,3 +1312,80 @@ ConversionService是类型转换体系的核心接口,将以上三种类型转

测试见TypeConversionFirstPartTest。

## 类型转换(二)
> 分支:type-conversion-second-part
上一节实现了spring中的类型转换体系,本节将类型转换的能力整合到容器中。

为了方便使用,提供了创建ConversionService的FactoryBean——ConversionServiceFactoryBean。

如果有定义ConversionService,在AbstractApplicationContext#finishBeanFactoryInitialization方法中设置到容器中。

类型转换的时机有两个:

- 为bean填充属性时,见AbstractAutowireCapableBeanFactory#applyPropertyValues
- 处理@Value注解时,见AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

你可能会有疑问,如果没有定义ConversionService,是怎么进行基本类型的转换的?其实spring为了向下兼容保留了一套比较旧的类型转换机制,没有定义ConversionService时会使用其进行基本类型的转换工作,不必关注旧的类型转换机制。

测试:
```
public class Car {
private int price;
private LocalDate produceDate;
}
```
```
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);
}
}
```
type-conversion-second-part.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="car" class="org.springframework.test.bean.Car">
<property name="price" value="1000000"/>
<property name="produceDate" value="2021-01-01"/>
</bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" ref="converters"/>
</bean>
<bean id="converters" class="org.springframework.test.common.ConvertersFactoryBean"/>
</beans>
```
```
public class TypeConversionSecondPartTest {
@Test
public void testConversionService() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:type-conversion-second-part.xml");
Car car = applicationContext.getBean("car", Car.class);
assertThat(car.getPrice()).isEqualTo(1000000);
assertThat(car.getProduceDate()).isEqualTo(LocalDate.of(2021, 1, 1));
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ public interface BeanFactory {
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

boolean containsBean(String name);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.springframework.beans.factory.annotation;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.TypeUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.core.convert.ConversionService;

import java.lang.reflect.Field;

Expand All @@ -33,8 +35,19 @@ public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean,
for (Field field : fields) {
Value valueAnnotation = field.getAnnotation(Value.class);
if (valueAnnotation != null) {
String value = valueAnnotation.value();
value = beanFactory.resolveEmbeddedValue(value);
Object value = valueAnnotation.value();
value = beanFactory.resolveEmbeddedValue((String) value);

//类型转换
Class<?> sourceType = value.getClass();
Class<?> targetType = (Class<?>) TypeUtil.getType(field);
ConversionService conversionService = beanFactory.getConversionService();
if (conversionService != null) {
if (conversionService.canConvert(sourceType, targetType)) {
value = conversionService.convert(value, targetType);
}
}

BeanUtil.setFieldValue(bean, field.getName(), value);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.springframework.beans.factory.config;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.StringValueResolver;

/**
Expand All @@ -22,4 +23,9 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
void addEmbeddedValueResolver(StringValueResolver valueResolver);

String resolveEmbeddedValue(String value);

void setConversionService(ConversionService conversionService);

ConversionService getConversionService();

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.*;
import org.springframework.core.convert.ConversionService;

import java.lang.reflect.Method;

Expand Down Expand Up @@ -170,6 +172,16 @@ protected void applyPropertyValues(String beanName, Object bean, BeanDefinition
// beanA依赖beanB,先实例化beanB
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);
}
}
}

//通过反射设置属性
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.StringValueResolver;

import java.util.ArrayList;
Expand All @@ -24,6 +25,9 @@ public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry i

private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<StringValueResolver>();

private ConversionService conversionService;


@Override
public Object getBean(String name) throws BeansException {
Object sharedInstance = getSingleton(name);
Expand Down Expand Up @@ -73,6 +77,13 @@ public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return ((T) getBean(name));
}

@Override
public boolean containsBean(String name) {
return containsBeanDefinition(name);
}

protected abstract boolean containsBeanDefinition(String beanName);

protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
Expand All @@ -99,4 +110,14 @@ public String resolveEmbeddedValue(String value) {
}
return result;
}

@Override
public ConversionService getConversionService() {
return conversionService;
}

@Override
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.DefaultResourceLoader;

import java.util.Collection;
Expand All @@ -26,6 +27,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader i

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

public static final String CONVERSION_SERVICE_BEAN_NAME = "conversionService";

private ApplicationEventMulticaster applicationEventMulticaster;

@Override
Expand All @@ -49,13 +52,26 @@ public void refresh() throws BeansException {
//注册事件监听器
registerListeners();

//提前实例化单例bean
beanFactory.preInstantiateSingletons();
//注册类型转换器和提前实例化单例bean
finishBeanFactoryInitialization(beanFactory);

//发布容器刷新完成事件
finishRefresh();
}

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//设置类型转换器
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) {
Object conversionService = beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME);
if (conversionService instanceof ConversionService) {
beanFactory.setConversionService((ConversionService) conversionService);
}
}

//提前实例化单例bean
beanFactory.preInstantiateSingletons();
}

/**
* 创建BeanFactory,并加载BeanDefinition
*
Expand Down Expand Up @@ -118,6 +134,11 @@ public void publishEvent(ApplicationEvent event) {
applicationEventMulticaster.multicastEvent(event);
}

@Override
public boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.springframework.context.support;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;

import java.util.Set;

/**
* @author derekyi
* @date 2021/1/17
*/
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {

private Set<?> converters;

private GenericConversionService conversionService;

@Override
public void afterPropertiesSet() throws Exception {
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");
}
}
}
}

@Override
public ConversionService getObject() throws Exception {
return conversionService;
}

@Override
public boolean isSingleton() {
return true;
}

public void setConverters(Set<?> converters) {
this.converters = converters;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.springframework.core.convert.support;

import cn.hutool.core.convert.BasicType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
Expand Down Expand Up @@ -28,6 +29,7 @@ public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
@Override
public <T> T convert(Object source, Class<T> targetType) {
Class<?> sourceType = source.getClass();
targetType = (Class<T>) BasicType.wrap(targetType);
GenericConverter converter = getConverter(sourceType, targetType);
return (T) converter.convert(source, sourceType, targetType);
}
Expand Down Expand Up @@ -83,6 +85,8 @@ protected GenericConverter getConverter(Class<?> sourceType, Class<?> targetType

private List<Class<?>> getClassHierarchy(Class<?> clazz) {
List<Class<?>> hierarchy = new ArrayList<>();
//原始类转为包装类
clazz = BasicType.wrap(clazz);
while (clazz != null) {
hierarchy.add(clazz);
clazz = clazz.getSuperclass();
Expand Down
Loading

0 comments on commit 21b831a

Please sign in to comment.