springBoot中自定義引數型別轉換
一、緣起
由於工作中的專案需要與第三方進行聯調,在對方呼叫我方的介面中請求接受Content-Type 為application/x-www-form-urlencoded,但是針對該請求方式的springBoot中無法封裝複雜型別的引數 例如一個VO物件中含有1、Date型別或者2、List物件
本片博文針對其中的兩個問題進行解決。
二、解決方式
1、構造VO物件
Order物件
/** * author xieqx * date 2018/9/7 * 訂單資訊 */ public class Order { /** * 訂單id */ private Long orderId; /** * 訂單編號 */ private String orderNo; /** * 預定時間 預設情況下的日期型別也無法進行封裝 需要新增自定義的預定時間 */ private Date bookingTime; /** * 訂單詳情列表,controller封裝的order物件中如果沒有自定義的型別轉換,預設情況下無法正確的封裝 */ private List<OrderDetail> orderDetailList; ...省略seter getter方法
OrderDetail物件
/**
* @author xieqx
* date 2018/9/7
* 訂單詳情
* 使用了lombok框架 簡化了bean的建立
*/
@AllArgsConstructor
@Getter
@Setter
public class OrderDetail {
private Long productId;
private String productName;
/**
* 日期型別
*/
private Date buyTime;
}
2、編寫自定義的型別轉換器
2.1 String型別轉換為Date型別
繼承Spring提供的org.springframework.core.convert.converter.Converter物件,重寫其中的convert()方法 其中是自己的轉換邏輯。
/** * author xieqx * date 2018/9/4 * 將String 轉換為Date集合 */ public class StringToDateConverter implements Converter<String, Date> { @Nullable @Override public Date convert(String json) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { return simpleDateFormat.parse(json); } catch (ParseException e) { e.printStackTrace(); } return null; } }
2.2 String型別轉換為List集合型別
與2.1轉換Date型別相似 這裡使用了jackson框架進行轉換
/**
* author xieqx
* date 2018/9/4
* 將String 轉換為list集合
*/
public class StringToListConverter implements Converter<String, List<OrderDetail>> {
@Override
public List<OrderDetail> convert(String json) {
List<OrderDetail> priceDetails = JsonUtil.str2List(json,OrderDetail.class);
return priceDetails;
}
}
2.3 轉換類完成後,還需要將其交由Spring的容器進行處理,這裡提供了兩種方式
1、繼承WebMvcConfigurationSupport類並將該物件建立進行新增
@Configuration
public class ApplicationConfig extends WebMvcConfigurationSupport {
/**
* 新增靜態資原始檔
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/img").addResourceLocations("/img/");
registry.addResourceHandler("/static/css").addResourceLocations("/css/");
registry.addResourceHandler("/static/js").addResourceLocations("/js/");
registry.addResourceHandler("/static/swf").addResourceLocations("/swf/");
registry.addResourceHandler("/static/media").addResourceLocations("/media/");
}
/**
* 新增自定義的Converters和Formatters.
*/
@Override
protected void addFormatters(FormatterRegistry registry) {
//新增字串轉換list的自定義轉換器
registry.addConverter(new StringToListConverter());
//新增字串轉換Date的自定義轉換器
registry.addConverter(new StringToDateConverter());
}
}
使用該方式會破壞SpringBoot預設載入靜態檔案的預設配置,需要重新進行新增. 切記
2、第二種方式
@Configuration
public class SpringDataConvert {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
/**
* 增加字串轉換為List集合
*/
@PostConstruct
public void addConversionConfig() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) requestMappingHandlerAdapter.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService)initializer.getConversionService();
//新增字串轉換為list集合的轉換機器
genericConversionService.addConverter(new StringToListConverter());
//新增字串轉換為日期型別的字串
genericConversionService.addConverter(new StringToDateConverter());
}
}
}
2.4 編寫controller進行測試
@Controller
@RequestMapping("/api")
public class DateConvertController {
private static final Logger logger = LoggerFactory.getLogger(DateConvertController.class);
/**
*
* @param order 訂單資訊
* @return 請求方式為application/x-www-form-urlencoded
*/
@ResponseBody
@RequestMapping(value = "test",method = RequestMethod.POST,
consumes = "application/x-www-form-urlencoded",produces = "application/json")
public Object checkInventoryForm(Order order){
if(order==null){
throw new RuntimeException("the hotelOrder is null");
}
return order;
}
}
3、啟動測試
3.1、不新增日期型別轉換器的post請求
提示String無法轉換為Date型別
3.2 不新增List型別轉換
提示String無法轉換為List
3.3、自定義的型別轉換器都註冊上
三、升級
3.1、 如果我的Order在新增一個Date型別 checkOutTime 格式為yyyy-MM-dd HH:mm:ss(區別於bookingTime yyyy-MM-dd)
如果使用上面的日期型別轉換,需要則無法處理怎可以使用@DateFormat註解來針對每一個日期型別的進行定製化的配置
並取消自定義的日期型別的轉換器
/**
* author xieqx
* date 2018/9/7
* 訂單資訊
*/
public class Order {
/**
* 訂單id
*/
private Long orderId;
/**
* 訂單編號
*/
private String orderNo;
/**
* 預定時間 預設情況下的日期型別也無法進行封裝 需要新增自定義的預定時間
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
//@JsonFormat( timezone = "GMT+8",pattern = "yyyy-MM-dd")
private Date bookingTime;
/**
* 離店時間
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date checkOutTime;
/**
* 訂單詳情列表,controller封裝的order物件中如果沒有自定義的型別轉換,預設情況下無法正確的封裝
*/
private List<OrderDetail> orderDetailList;
postman的請求結果
3.2、對於若干個相同集合的處理
新增一個使用者列表List<User>需要重新構造一個類似於StringToListConverter 去轉換User list集合挺麻煩的,我們可以對於StringToListConverter<T>構造成範型,如下程式碼
/**
* @author xieqx
* date 2018/9/4
* 將String 轉換為list集合 範型的處理
*/
public class StringToListConverter<T> implements Converter<String, List<T>> {
@Override
public List<T> convert(String json) {
Type type = (Class < T > ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Class clazz = type.getClass();
List<T> list = JsonUtil.str2List(json,clazz);
return list;
}
}
需要解析Bean的集合形式只需繼承即可
#解析OrderDetail 集合物件
public class StringToOrderDetailListConverter extends StringToListConverter<OrderDetail> {
}
#解析User 集合物件
public class StringToUserListConverter extends StringToListConverter<User> {
}
在spring中進行註冊
@Configuration
public class SpringDataConvert {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
/**
* 增加字串轉換為List集合
*/
@PostConstruct
public void addConversionConfig() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) requestMappingHandlerAdapter.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService)initializer.getConversionService();
//新增轉換OrderDetail 集合
genericConversionService.addConverter(new StringToOrderDetailListConverter());
//新增轉換User 集合
genericConversionService.addConverter(new StringToUserListConverter());
//新增字串轉換為日期型別的字串
//genericConversionService.addConverter(new StringToDateConverter());
}
}
}