The use of springmvc converter converter

1: write in front

The parameters passed by the page are all strings, and the type of parameters received in the controller is uncertain. For basic data types, springmvc has provided a type converter. For unsupported target types, such as date types, custom object types , It can be achieved by implementing org.springframework.core.convert.converter.Converterthe method of the interface interface, which is defined as follows:

@FunctionalInterface
public interface Converter<S, T> {
    
    
	@Nullable
	T convert(S source);
}

<S, T>The S in the generic type represents the type of page parameter, generally String, and T represents the type of parameter received in the controller. The method convert(source)is the method we want to implement from S to T. Next, we implement a little chestnut that converts a string to a custom object.

2: Define the receiving parameter object

public class GoodsModel {
    
    
	private String goodsname;
	private double goodsprice;
	private int goodsnumber;
	...getter and setter...
}

3: Define the type converter

We assume that the passed parameter is a comma-separated string. After the string is split, the value needed to build the GoodModel object can be obtained. The source code is as follows:

public class MyGoodsModelConverter implements Converter<String, GoodsModel> {
    
    

	public MyGoodsModelConverter() {
    
    
		System.out.println("MyGoodsModelConverter no args constructor invoked!!!");
	}
	@Override
	public GoodsModel convert(String source) {
    
    
		String logHead = "MyGoodsModelConverter_convert";
		System.out.println(logHead + " begin, source is: " + source);
		// 创建一个Goods实例
		GoodsModel goods = new GoodsModel();
		// 以英文逗号,分隔
		String stringvalues[] = source.split(",");
		if (stringvalues != null && stringvalues.length == 3) {
    
    
			// 为Goods实例赋值
			goods.setGoodsname(stringvalues[0]);
			goods.setGoodsprice(Double.parseDouble(stringvalues[1]));
			goods.setGoodsnumber(Integer.parseInt(stringvalues[2]));
			System.out.println(logHead + " end, goods is: " + goods);
			return goods;
		} else {
    
    
			throw new IllegalArgumentException(String.format(
					"类型转换失败, 需要格式'apple, 10.58,200 ',但格式是[% s ] ", source));
		}

	}
}

4: Register type converter

The converter that automatically converts from String to dongshi.converter.GoodsModel needs to note that the converter here belongs to the category of MVC, so it must be configured in the springMVC configuration file, that is, in the {dispatcher-servlet-name}-spring.xml file, Such as

<servlet>
  	<servlet-name>letsGO</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	...
  	<!-- 值越小优先级越高 -->
  	<load-on-startup>1</load-on-startup>
</servlet>

The configuration file that needs to be configured is letsGO-servlet.xmlas follows:

<bean id="myConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<property name="converters">
		<set>
			<bean class="dongshi.converter.MyGoodsModelConverter"/>
		</set>
	</property>
</bean>
<mvc:annotation-driven conversion-service="myConversionService">
	...消息转换等配置,和自定义转换器配置没有关系...
</mvc:annotation-driven>

5:controller

@RequestMapping("/mytest")
@Controller
public class MyTestController {
    
    

	@RequestMapping("/testConverter")
	@ResponseBody
	public GoodsModel testConverter(@RequestParam("goods") GoodsModel testConverter) {
    
    
		System.out.println(testConverter);
		return testConverter;
	}
}

6: Test

Respectively, in the converter return goods;and the controler return testConverter;break point testing by curl curl http://localhost:8080/Gradle___org_springframework___dongsir_testspringmvc_5_1_18_BUILD_SNAPSHOT_war__exploded_/mytest/testConverter?goods=3,4,5, the converter follows:
Insert picture description here
continue down code to the breakpoint controller, as shown below:
Insert picture description here
Back after performing:
{"goodsname":"3","goodsprice":4.0,"goodsnumber":5}.

7: springboot environment

If it is in the springboot environment, you only need to define the converter as a spring bean. Springboot will automatically scan from the container and DefaultFormattingConversionServicecomplete the registration through the class. The source code of the automatic registration is as follows:

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#addFormatters
@Override
public void addFormatters(FormatterRegistry registry) {
    
    
	ApplicationConversionService.addBeans(registry, this.beanFactory);
}
org.springframework.boot.convert.ApplicationConversionService#addBeans
public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
    
    
    Set<Object> beans =new LinkedHashSet();
    
    // 注册自定义的GenericConverter
    beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
    // 注册自定义Converter(本例重点)
    beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
    // 忽略
    beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
    // 忽略
    beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
    ...

}

Let's start the example below.

7.1: Define the controller

@RestController
public class HelloWorldController {
    
    

    @RequestMapping("/testSpringBootConverter")
    @ResponseBody
    public GoodsModel testSpringBootConverter(@RequestParam("springboot-goods") GoodsModel testConverter) {
    
    
        System.out.println(testConverter);
        return testConverter;
    }
}

Access test without registering custom converter:
Insert picture description here

7.2: Defining the converter

Pay attention to adding the @Componentregistration to the spring container to become a spring bean:


public class MySpringBootGoodsModelConverter implements Converter<String, GoodsModel> {
    
    

	public MySpringBootGoodsModelConverter() {
    
    
		System.out.println("MyGoodsModelConverter no args constructor invoked!!!");
	}
	@Override
	public GoodsModel convert(String source) {
    
    
		String logHead = "MyGoodsModelConverter_convert";
		System.out.println(logHead + " begin, source is: " + source);
		// 创建一个Goods实例
		GoodsModel goods = new GoodsModel();
		// 以英文逗号,分隔
		String stringvalues[] = source.split(",");
		if (stringvalues != null && stringvalues.length == 3) {
    
    
			// 为Goods实例赋值
			goods.setGoodsname(stringvalues[0]);
			goods.setGoodsprice(Double.parseDouble(stringvalues[1]));
			goods.setGoodsnumber(Integer.parseInt(stringvalues[2]));
			System.out.println(logHead + " end, goods is: " + goods);
			return goods;
		} else {
    
    
			throw new IllegalArgumentException(String.format(
					"类型转换失败, 需要格式'apple, 10.58,200 ',但格式是[% s ] ", source));
		}

	}
}

That's it, springboot will automatically complete the registration, and debug as follows when starting the application:
Insert picture description here
debug location org.springframework.boot.convert.ApplicationConversionService#addBeans.

7.3: Access test

Insert picture description here

8:GenericConverter

The previous Converter is a one-to-one converter interface, that is, the source and target of the conversion are both a single object. GenericConverter can be considered as an enhancement to the Converter and supports one-to-many. The source code is as follows:

public interface GenericConverter {
    
    
	@Nullable
	Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

That is, the parameters of the controller can be of type List, which has been provided in spring StringToCollectionConverterto provide String to collection conversion by default. The source code is as follows:

org.springframework.core.convert.support.StringToCollectionConverter#convert
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
    
    
	// 强转http中的字符串
	String string = (String) source;
	// 按照英文逗号分割为字符串数组(重点!!!)
	String[] fields = StringUtils.commaDelimitedListToStringArray(string);
	// 获取http参数的类型描述器,这里一般都是字符串
	TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
	Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
			(elementDesc != null ? elementDesc.getType() : null), fields.length);
	// 一般不会进if,所以只看else逻辑
	if (elementDesc == null) {
    
    
		for (String field : fields) {
    
    
			target.add(field.trim());
		}
	}
	else {
    
    
		// 遍历每个字段值,调用convertionService,实际上就是GenericConversionService方法,该方法内部会通过
		// sourceType+targetType(elementDesc)组合的方式来寻找可进行转换的转换器进行转换,转换后添加到最终的结果集中
		for (String field : fields) {
    
    
			Object targetElement = this.conversionService.convert(field.trim(), sourceType, elementDesc);
			target.add(targetElement);
		}
	}
	// 返回结果集
	return target;
}

The converter first obtains the string array by cutting the string, and then for each element, the http原始类型+控制器中的元素类型corresponding converter is obtained by matching and then converted. Therefore, in the previous example, we can define a List controller Parameters, and then http parameters are separated by commas, and then multi-value transmission can be carried out.

8.1: Define the controller

@RequestMapping("/testSpringBootConverterWithList")
@ResponseBody
public List<GoodsModel> testSpringBootConverter(@RequestParam("springboot-goods-list") List<GoodsModel> testConverterList) {
    
    
    System.out.println(testConverterList);
    return testConverterList;
}

8.2: Transform the converter

Because our previous converter is also separated by English commas, here it conflicts with StringToCollectionConverter, so we use _split instead :

将:
String stringvalues[] = source.split(",");
改为:
String stringvalues[] = source.split("_");

8.3: Testing

Insert picture description here

Guess you like

Origin blog.csdn.net/wang0907/article/details/112910875