Spring中类型转换服务ConversionService 以及 Converter 类型转换器

前面我也已经了解了整个spring启动的大致流程, 即

ClassPathXmlApplicationContext构造方法执行过程中调用 AbstractApplicationContext 类中的refresh()方法。

refresh() 方法中 调用了13个具体方法。其中有一个方法finishBeanFactoryInitialization(beanFactory) 是为剩余未实例化, 非懒加载的BeanDefinition 进行实例化。

这这个方法执行的第一部分逻辑,就是我们今天要讨论的内容:

ConversionService 类型转换服务

0、概述

​ 在spring的bean创建过程中,首先进行的是bean的实例化过程, 实例化过程中会遇到各种属性值的解析与类型转换, 这个过程就离不开Converter 去做相应的处理。 那么这些Converter又是存在放在哪里, 并且什么时候加入到对应的集合中去的呢? 下面请看具体的源码分析

1、源码分析

1.1、ConversionService 接口的源码

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.convert;

import org.springframework.lang.Nullable;

/**
 * 类型转换服务
 *
 * A service interface for type conversion. This is the entry point into the convert system.
 * Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
 *
 */
public interface ConversionService {

	/**
	 * 判断能否进行类型转换
	 */
	boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);

	/**
	 * 判断是否可以进行类型转换
	 */
	boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

	/**
	 * 类型转换,获取合适的转换器进行类型的转换,默认是DefaultConversionService,也可以是自定义的
	 */
	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);

	@Nullable
	Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}

1.2、实现类有哪些?

image-20220506221325053

  • 这里,比较核心且我们应该关心的一般有两个,这里主要提一下 DefaultConversionService

    • DefaultConversionService
    • ConfigurableConversionService
  • DefaultConversionService 中有几个方法需要我们关注一下

public static void addDefaultConverters(ConverterRegistry converterRegistry) {
   addScalarConverters(converterRegistry);
   addCollectionConverters(converterRegistry);
	
   //  想 converterRegistry 中添加 类型转换服务
   converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
   converterRegistry.addConverter(new StringToTimeZoneConverter());
   converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
   converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

   converterRegistry.addConverter(new ObjectToObjectConverter());
   converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
   converterRegistry.addConverter(new FallbackObjectToStringConverter());
   converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}

2、Converter 分析与自定义

2.1、Converter 分类

image-20220506222727231

从上图大致我们可以了解到, ConversionService 管理着一系列的 Converter ,进行类型转换服务, 具体的分类如下:

  • Converter : 一般用于一对一的一个类型转换 S类型 -> T类型
    • T convert(S source);
  • GenericConverter : 一般用于将一种类型转换成多个目标类型的描述
    • Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
  • ConverterFactory: “范围”转换器的工厂,可以将对象从 S 转换为 R 的子类型
    • <T extends R> Converter<S, T> getConverter(Class<T> targetType);

2.2、如何自定义Converter 并添加到spring 容器中进行使用

这里我们可以参考一下spring官方文档中的介绍

参考链接:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#core-convert-Spring-config

image-20220506222216953

上面截图我们可以看出来, 可以通过xml方法想 spring 容器中注入我们自定义的 Converter

3.2、如何具体实现

3.2.1、先自定义一个Converter
import org.springframework.core.convert.converter.Converter;

public class StudentConverter implements Converter<String,Student> {
    @Override
    public Student convert(String source) {
        System.out.println("-----");
        Student s  = new Student();
        String[] splits = source.split("_");
        s.setId(Integer.parseInt(splits[0]));
        s.setName(splits[1]);
        return s;
    }
}
3.2.2、实现一个辅助类: 学生类
public class Student {

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
3.2.3、通过xml添加到容器中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:msb="http://www.mashibing.com/schema/user"
       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
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
        http://www.mashibing.com/schema/user http://www.mashibing.com/schema/user.xsd">

	

    <bean id="studentConverter" class="com.qzk.selfConverter.StudentConverter"></bean>
    <bean id ="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="studentConverter"></ref>
            </set>
        </property>
    </bean>
</beans>
3.2.4、 启动测试启动类
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @ClassName TestConverter
 * @Description TODO
 * @Author qzk
 * @Date 2022/5/6 10:40 下午
 * @Version 1.0
 **/
public class TestConverter {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentConverter bean = context.getBean(StudentConverter.class);
        System.out.println(bean);
    }
}

image-20220506224251023

如上图,我们已经看到,在容器中已经有这个转换器了, 但是这边看, 还是不太清晰,我们来看下面这张图

image-20220506224646779

image-20220506224832303

image-20220506225024946

注意:

  • 这里此时有53个,是因为有部分是spring自带的类型转换服务, 我们自己定义的就只有一个。这个在上面 DefaultConversionService 中的 addDefaultConverters 等方法中也可以看到, 有添加spring自带的类型转换服务的。

  • protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    		// Initialize conversion service for this context.
    		// 为上下文初始化类型转换器
    		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
    				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
    			beanFactory.setConversionService(
    					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    		}
       .....
       .....
    }
    
  • 上面的代码中的这段, 只是在`beanFactory 中设置了这些类型转换服务, 还没有去具体使用。

posted on 2022-05-06 23:01  QzkRainPig  阅读(592)  评论(0编辑  收藏  举报