我的项目环境:
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);
}
}
先写到这吧,如果还有其它的地方需要注意或错误,我没想到,望指正。