我的项目环境:
 spring-boot 2.6.2
 java version 1.8
下面是我的maven pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring-learning-converter</module>
    </modules>
    <packaging>pom</packaging>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
首先建一个Animal类和它的子类:
public class Animal {
    public void run() {
        System.out.println("run.");
    }
    public static <T extends Animal> T getAnimal(String feature,Class<T> targetClass) {
        if (Dog.class == targetClass) {
            return (T)new Dog(feature);
        } else if (Lion.class == targetClass) {
            return (T) new Lion(feature);
        } else if (Tiger.class == targetClass) {
            return (T) new Tiger(feature);
        } else if (Pig.class == targetClass) {
            return (T) new Pig(feature);
        } else {
            throw new IllegalArgumentException("Cannot convert String ["
                    + feature + "] to target class ["
                    + targetClass.getName() + "]");
        }
    }
}
public class Tiger extends Animal {
    private Feature feature;
    public Tiger(String feature) {
        this.feature = new Feature(feature);
    }
    public Feature getFeature() {
        return feature;
    }
    public void setFeature(Feature feature) {
        this.feature = feature;
    }
}
public class Lion extends Animal {
    private Feature feature;
    public Lion(String feature) {
        this.feature = new Feature(feature);
    }
    public Feature getFeature() {
        return feature;
    }
    public void setFeature(Feature feature) {
        this.feature = feature;
    }
}
public class Dog extends Animal {
    private Feature feature;
    public Dog(String feature) {
        this.feature = new Feature(feature);
    }
    public Feature getFeature() {
        return feature;
    }
    public void setFeature(Feature feature) {
        this.feature = feature;
    }
}
Animal子类中的描述动物特征的类:Feature
public class Feature {
    private String behave;
    public Feature() {}
    public Feature(String behave) {
        this.behave = behave;
    }
    public String getBehave() {
        return behave;
    }
    public void setBehave(String behave) {
        this.behave = behave;
    }
}
下面写一个String -> Animal的ConverterFactory类:
@Component
public class StringToAnimalConverterFactory implements ConverterFactory<String,Animal> {
    @Override
    public <T extends Animal> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToAnimalConverter<>(targetType);
    }
    private final class StringToAnimalConverter<T extends Animal> implements Converter<String,T> {
        private Class<T> animalType;
        public StringToAnimalConverter(Class<T> animalType) {
            this.animalType = animalType;
        }
        @Override
        public T convert(String source) {
            Feature feature = new Feature(source);
            return (T) Animal.getAnimal(source,animalType);
        }
    }
}
上面这个转换器工厂类在Spring Boot环境中只需要加@Component注解就行了,不需要在其它地方注册,就能自动的注册进去。
最后写个Controller测试下:
@RestController
public class MyController extends CommonController{
    @GetMapping("/findAnimal")
    public String findAnimal(@RequestParam("feature") Lion animal) {
        return animal.getFeature().getBehave();
    }
}
用postman发起请求:
 
 如果想要把字符串转换成老虎,只需要把参数改成Tiger类就行了。如下:
@RestController
public class MyController extends CommonController{
    @GetMapping("/findAnimal")
    public String findAnimal(@RequestParam("feature") Tiger animal) {
        return animal.getFeature().getBehave();
    }
}
请求结果如下:
 
 另一个Dog类和上面一样,就不测试了。
需要注意的一点是:
方法参数前面的@RequestParam(“feature")一定要加,不然就找不到转换器,返回值为空。如下:
@RestController
public class MyController extends CommonController{
    @GetMapping("/findAnimal")
    public String findAnimal(Tiger animal) {
        return animal.getFeature().getBehave();
    }
}
返回空:
 
 当然,因为只是测试,有些地方的就省略了参数的校验,下面看下StringToNumberConverterFactory的源码,大家写自己的类时,可以参考:
package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.NumberUtils;
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
    StringToNumberConverterFactory() {
    }
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumberConverterFactory.StringToNumber(targetType);
    }
    private static final class StringToNumber<T extends Number> implements Converter<String, T> {
        private final Class<T> targetType;
        public StringToNumber(Class<T> targetType) {
            this.targetType = targetType;
        }
        @Nullable
        public T convert(String source) {
            return source.isEmpty() ? null : NumberUtils.parseNumber(source, this.targetType);
        }
    }
}
还有convert()方法中的解析操作NumberUtils.parseNumber(source,this.targetType):
    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 = StringUtils.trimAllWhitespace(text);
        if (Byte.class == targetClass) {
            return isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed);
        } else if (Short.class == targetClass) {
            return isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed);
        } else if (Integer.class == targetClass) {
            return isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed);
        } else if (Long.class == targetClass) {
            return isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed);
        } else if (BigInteger.class == targetClass) {
            return isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed);
        } else if (Float.class == targetClass) {
            return Float.valueOf(trimmed);
        } else if (Double.class == targetClass) {
            return Double.valueOf(trimmed);
        } else if (BigDecimal.class != targetClass && Number.class != targetClass) {
            throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");
        } else {
            return new BigDecimal(trimmed);
        }
    }
先写到这吧,如果还有其它的地方需要注意或错误,我没想到,望指正。










