# 第18章:挂印封刀,数据类型转换工厂设计实现

作者:小傅哥
博客:https://bugstack.cn (opens new window)
星球:https://articles.zsxq.com/id_d0cpbs31880x.html (opens new window)

沉淀、分享、成长,让自己和他人都能有所收获!😄

# 零、优秀作业

# 一、前言

值得的,总是在精雕细琢!

在你写的程序开发中,你有为一个类名、方法名、属性名,反复斟酌吗?代码格式间隔大小、编写方式、注释描述不断的提升吗?你有为一个功能逻辑的实现不断的重构吗?我有,我一直都有,为了能写好一块代码,甚至会忘记时间从上午到下午,当能实现完成后,会欣赏似的看待自己的代码,也根本不舍得把他交给别人

如果你也是这样的工程师,其实在你不去刻意追求大厂、高薪、好职位的时候,也会把你送到那个位置上去。想不被这个已经有些内卷的行业打下去,那么基本就需要选择一条能沉淀下来核心知识的路径来提升自己,做好长期规划,让以后你的30岁有30岁的能力,35岁有35岁的经历!

# 二、目标

其实实现到本章节,关于IOC、AOP在日常使用和面试中高频出现的技术点都该涵盖了。那么为了补全整个框架内容的结构,方便读者后续在阅读 Spring 时不至于对类型转换的知识体系陌生,这里再添加一些关于此类知识的实现。

类型转换也可以叫做数据转换,比如从String到Integer、从String到Date、从Dubbo到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。

# 三、设计

如果我们来把只是看上去一个简单的类型转换操作抽象成框架,那么它就需要一个标准的接口,谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。那么在有了这样接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。整体设计结构如下图:

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

# 四、实现

# 1. 工程结构

small-spring-step-17
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework
    │           ├── aop
    │           │   ├── aspectj
    │           │   │   └── AspectJExpressionPointcut.java
    │           │   │   └── AspectJExpressionPointcutAdvisor.java
    │           │   ├── framework 
    │           │   │   ├── adapter
    │           │   │   │   └── MethodBeforeAdviceInterceptor.java
    │           │   │   ├── autoproxy
    │           │   │   │   └── MethodBeforeAdviceInterceptor.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
    │           │   │   │   ├── AbstractBeanDefinitionReader.java
    │           │   │   │   ├── AbstractBeanFactory.java
    │           │   │   │   ├── BeanDefinitionReader.java
    │           │   │   │   ├── BeanDefinitionRegistry.java
    │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   │   ├── DefaultListableBeanFactory.java
    │           │   │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── FactoryBeanRegistrySupport.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── FactoryBean.java
    │           │   │   ├── HierarchicalBeanFactory.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.java     
    │           │   └── io    
    │           │   	  ├── ClassPathResource.java 
    │           │       ├── DefaultResourceLoader.java 
    │           │       ├── FileSystemResource.java 
    │           │       ├── Resource.java 
    │           │       ├── ResourceLoader.java
    │           │       └── UrlResource.java
    │           ├── stereotype
    │           │   └── Component.java
    │           └── utils
    │               ├── ClassUtils.java
    │               └── StringValueResolver.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   └── Husband.java
                ├── bean
                │   ├── ConvertersFactoryBean.java
                │   ├── StringToIntegerConverter.java  
                │   └── StringToLocalDateConverter.java  
                └── ApiTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

工程源码公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码

# 2. 定义类型转换接口

包:cn.bugstack.springframework.core.convert.converter

类型转换处理接口

public interface Converter<S, T>  {

    /** Convert the source object of type {@code S} to target type {@code T}. */
    T convert(S source);

}
1
2
3
4
5
6

类型转换工厂

public interface ConverterFactory<S, R>{

    /**
     * Get the converter to convert from S to target type T, where T is also an instance of R.
     * @param <T> the target type
     * @param targetType the target type to convert to
     * @return a converter from S to T
     */
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);

}
1
2
3
4
5
6
7
8
9
10
11

类型转换注册接口

public interface ConverterRegistry {

    /**
     * Add a plain converter to this registry.
     * The convertible source/target type pair is derived from the Converter's parameterized types.
     * @throws IllegalArgumentException if the parameterized types could not be resolved
     */
    void addConverter(Converter<?, ?> converter);

    /**
     * Add a generic converter to this registry.
     */
    void addConverter(GenericConverter converter);

    /**
     * Add a ranged converter factory to this registry.
     * The convertible source/target type pair is derived from the ConverterFactory's parameterized types.
     * @throws IllegalArgumentException if the parameterized types could not be resolved
     */
    void addConverterFactory(ConverterFactory<?, ?> converterFactory);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • Converter、ConverterFactory、ConverterRegistry,都是用于定义类型转换操作的相关接口,后续所有的实现都需要围绕这些接口来实现,具体的代码功能可以进行调试验证。

# 3. 实现类型转换服务

cn.bugstack.springframework.core.convert.support.DefaultConversionService

public class DefaultConversionService extends GenericConversionService{

    public DefaultConversionService() {
        addDefaultConverters(this);
    }

    public static void addDefaultConverters(ConverterRegistry converterRegistry) {
        // 添加各类类型转换工厂
        converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
  • DefaultConversionService 是继承 GenericConversionService 的实现类,而 GenericConversionService 实现了 ConversionService, ConverterRegistry 两个接口,用于 canConvert 判断和转换接口 convert 操作。

# 4. 创建类型转换工厂

cn.bugstack.springframework.context.support.ConversionServiceFactoryBean

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;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  • 有了 FactoryBean 的实现就可以完成工程对象的操作,可以提供出转换对象的服务 GenericConversionService,另外在 afterPropertiesSet 中调用了注册转换操作的类。最终这个类会被配置到 spring.xml 中在启动的过程加载。

# 5. 类型转换服务使用

cn.bugstack.springframework.beans.factory.support.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) {
                // 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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • 在 AbstractAutowireCapableBeanFactory#applyPropertyValues 填充属性的操作中,具体使用了类型转换的功能。
  • 在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 也有同样的属性类型转换操作。

# 五、测试

# 1. 事先准备

public class Husband {

    private String wifiName;

    private Date marriageDate; // 添加一个日期类的转换操作
 		
    // ... get/set
}    
1
2
3
4
5
6
7
8

转换时间的操作类

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);
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • Husband 是一个基础对象类设置了时间属性,之后再添加一个类型转换的操作用于转换时间信息。

# 2. 属性配置文件

spring.xml

<bean id="husband" class="cn.bugstack.springframework.test.bean.Husband">
    <property name="wifiName" value="你猜"/>
    <property name="marriageDate" value="2021-08-08"/>
</bean>

<bean id="conversionService" class="cn.bugstack.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters" ref="converters"/>
</bean>

<bean id="converters" class="cn.bugstack.springframework.test.converter.ConvertersFactoryBean"/>
1
2
3
4
5
6
7
8
9
10
  • 配置基础Bean对象,设置属性的日期,同时再添加类型转换的服务和自己实现的 ConvertersFactoryBean

# 3. 单元测试

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

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

@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"));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

测试结果

测试结果:Husband{wifiName='你猜', marriageDate=Sun Aug 08 00:00:00 CST 2021}

Process finished with exit code 0
1
2
3
  • 这个测试内容还是比较简单的,可以自行验证结果,虽然最终的结果看上去比较简单,但整个框架结构实现设计还是蛮复杂的,把这么一个转换操作抽象为接口适配、工厂模型等方式,还是很值得借鉴的。

# 六、总结

  • 本章节实现的类型转换操作如果只是功能性的开发,就像你自己承接的需求那样,可能只是简单的if判断就搞定了,但放在一个成熟的框架要中要考虑的是可复用性、可扩展性,所以会看到接口的定义、工厂的使用等等设计模式在这里体现。
  • 最后非常感谢你能坚持学习到这个章节,如果你在学习的过程也是每一个章节都是对着文章、写着代码代码、调试着bug,感悟着设计,那么你一定会在这个过程中得到很多很多,以后再阅读Spring的源码也就不会感觉那么难了。