Spring Web Flow —— 視圖狀態 - 100

基礎

指定view-state的view屬性的幾種方式

  • 按照默認名稱在相對路徑下查找view
<!-- 在flow配置文件同目錄下查找名爲enterBookingDetails的視圖 -->
<view-state id="enterBookingDetails">
  • 指明view名稱,在相對路徑下查找view
<!-- 在flow配置文件同目錄下查找名爲bookingDetails.xhtml的視圖 -->
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">
  • 絕對路徑查找view
<!-- 直接查找/WEB-INF/hotels/booking/bookingDetails.xhtml的視圖 -->
<view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml">
  • 按照邏輯ID定位view
    這是結合Spring提供的其它viewResolver來定位到其他組件中的view,如Tiles等。在前面 配置 一章中有講
<!-- 結合viewResolver共同確定視圖位 -->
<view-state id="enterBookingDetails" view="bookingDetails">

在viewScope中分配變量

  • 直接分配
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />
  • 依靠運算結果分配
<on-render>
     <evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>

在viewScope中操作對象

如下例子展示瞭如何在同一個view state的不同時機操作一系列對象

<view-state id="searchResults">
    <on-render>
        <evaluate expression="bookingService.findHotels(searchCriteria)"
                 result="viewScope.hotels" />
    </on-render>
    <transition on="next">
        <evaluate expression="searchCriteria.nextPage()" />
        <render fragments="searchResultsFragment" />
    </transition>
    <transition on="previous">
        <evaluate expression="searchCriteria.previousPage()" />
        <render fragments="searchResultsFragment" />
    </transition>
</view-state>
<!-- 渲染view之前,首先執行findHotels方法,將結果放到viewScope的hotels中 -->
<!-- 當返回next事件時,執行nextPage()方法, 然後渲染查找結果部分 -->
<!-- 當返回previous事件時,執行前一頁操作,然後渲染查找結果部分 -->

<on-render>

在view渲染前可以執行一個或多個action,這些action將會在視圖最開始渲染以及後續的任何刷新,甚至視圖局部的重新渲染執行。以上面的代碼爲例,在重新渲染結果之前,還會先執行一次findHotels方法。

數據綁定

在view-state上綁定model

使用model屬性,可以將一個對象綁定到view中的表單中。web flow可以幫助完成對象屬性和表單域的綁定和驗證。

<view-state id="enterBookingDetails" model="booking">

綁定時機是當view的event發生時

  • view-to-model綁定:在view完成並回發時,用戶的輸入域會被綁定到指定對象的屬性上
  • model驗證:綁定後,如果需要驗證,驗證邏輯會被調用。
  • 需要指出的是:只有當驗證成功後才能transition到別的state,驗證失敗時會重新渲染該view,要求用戶重新輸入。

綁定model時的類型轉換

基礎

由於客戶端上傳的表單數據都是字符串類型的, 因此需要進行類型轉換

  • Web Flow類型轉換和Spring MVC的類型轉換的關係
    在web-flow 2.1以前,Sprign MVC和web-flow使用不同的類型轉換機制,但是2.1以後,二者使用相同的類型轉換
    以前,Web Flow使用spring-binding-2.4.6.RELEASE.jar包提供的API進行類型轉換,相關的類有org.springframework.binding.convert.service.DefaultConversionServiceorg.springframework.binding.convert.converters.Converter等,通過實現Converter接口完成自定義轉換器,再通過DefaultConversionService進行註冊,就像如下所示的方式1那樣;而且這樣還能夠註冊帶命名ID的轉換器,可以結合<bingding>converter屬性進行使用,但這種方式已經非常不建議了。
    目前, Web Flow在執行conversionService時依然使用org.springframework.binding.convert.service.DefaultConversionService,但該服務已經不會去註冊任何默認的轉換器和格式化器了,而是將轉換和格式任務全部委託給來自spring-core-4.3.7.RELEASE.jar包的org.springframework.core.convert.ConversionService完成。DefaultConversionService內部維護一個ConversionService對象,對DefaultConversionService中進行的大多數操作都被轉變成對ConversionService的操作(對帶命名ID轉換器的管理除外)。值得一提的是,命名ID的轉換器非常沒有必要,因爲在檢測到相應類型後,系統會自動調用合適的轉換器。

  • 方式一 傳統方式添加Converter(不建議使用)

import org.springframework.binding.convert.converters.Converter;
// 定義自己的轉換器
public class DateConverter implements Converter {

    public Class<?> getSourceClass() {
        return String.class;
    }

    public Class<?> getTargetClass() {
        return java.util.Date.class;
    }

    public Object convertSourceToTargetClass(Object source, Class<?> targetClass) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        return sdf.parse(source.toString());
    }

}
// 註冊自己的轉換器
public class ApplicationConversionService extends DefaultConversionService {
    public ApplicationConversionService() {
        addConverter(new DateConverter());
    }
}
<!-- 使用轉換器,什麼都不用指定,當檢測到model中的checkinDate屬性爲Date類型時,DateConverter會自動被調用 -->
<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="checkinDate"/>
    </binder>
</view-state>
  • 方式二 添加帶ID的Converter(已過時,不推薦使用)
DateConverter的定義不變
// 註冊時指定id
public class ApplicationConversionService extends DefaultConversionService {
    public ApplicationConversionService() {
        // 該方法已被標爲deprecated
        addConverter("dateConverter", new DateConverter());
    }
}
<!-- 使用時指定id -->
<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="checkinDate" converter="dateConverter"/>
    </binder>
</view-state>
  • 方式三 - 配置通用的Converter和Formatter(推薦使用,當前版本鼓勵的方式)
// 首先實現FormattingConversionServiceFactoryBean,添加自定義格式化器和轉換器
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {

    @Override
    public void setConverters(Set<?> converters) {
        Converter<String, Date> converter = new Converter<String, Date>() {
            public Date convert(String source) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.parse(source);
            }
        };

        Set<Converter<String, Date>> set = new HashSet<Converter<String,Date>>();
        set.add(converter);
        super.setConverters(set);
    }

    @Override
    public void setFormatters(Set<?> formatters) {
        Set<Formatter<Date>> set= new HashSet<Formatter<Date>>();
        set.add(new Formatter<Date>() {

            public String print(Date object, Locale locale) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.format(object);
            }

            public Date parse(String text, Locale locale) throws ParseException {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.parse(text);
            }

        });

        super.setFormatters(set);
    }

}
// 創建由ApplicationConversionServiceFactoryBean產生的ConversionService
// 該ConversionService是Spring MVC中類型轉換的核心,可以同時配置給Spring MVC和Web Flow
@Bean("applicationConversionService")
public ConversionService applicationConversionService() {
    FormattingConversionServiceFactoryBean factory = new ApplicationConversionServiceFactoryBean();
    // 添加自定義格式化器
    factory.setFormatters(null);
    // 添加自定義格式轉換器
    factory.setConverters(null);
    // 生成ConversionService 
    factory.afterPropertiesSet();
    // 返回ConversionService 
    return factory.getObject();
}
// 下面三個步驟用於配置給Web Flow

@Bean("defaultConversionService")
public DefaultConversionService conversionService(@Autowired ConversionService applicationConversionService) {
    return new DefaultConversionService(applicationConversionService);
}

@Bean
public FlowBuilderServices flowBuilderServices(@Autowired MvcViewFactoryCreator mvcViewFactoryCreator, @Autowired DefaultConversionService defaultConversionService) {
    return getFlowBuilderServicesBuilder()
            .setConversionService(defaultConversionService)
            .build();
}

// 註冊flow
@Bean("flowRegistry")
public FlowDefinitionRegistry flowRegistry(@Autowired FlowBuilderServices flowBuilderServices) {

    return getFlowDefinitionRegistryBuilder()
            .setBasePath("/WEB-INF/jsp/flow")
            .addFlowLocation("/config/search-flow.xml")
            .setFlowBuilderServices(flowBuilderServices)
            .build();
}
<!-- 下面的步驟用於註冊給Spring MVC -->
<mvc:annotation-driven conversion-service="applicationConversionService" />
  • 針對上述三種方式的說明
    • 方式一表面上使用的是舊的轉換器添加方式,但內部實現還是按照新的方式進行,在添加Converter時,會通過一個適配器類轉換將org.springframework.binding.convert.converters.Converter轉換爲org.springframework.core.convert.converter.GenericConverter
// 源碼片段
public void addConverter(Converter converter) {
    ((ConverterRegistry) delegate).addConverter(new SpringBindingConverterAdapter(converter));
    if (converter instanceof TwoWayConverter) {
        TwoWayConverter twoWayConverter = (TwoWayConverter) converter;
        ((ConverterRegistry) delegate).addConverter(new SpringBindingConverterAdapter(new ReverseConverter(
                twoWayConverter)));
    }
}
  • 方式二已過時,這裏僅展示
  • 方式三推薦使用,但這裏僅展示了我自己實驗成功的java config配置方式,xml配置方式可以參見官網
  • 實際使用時,會發現方式一從視圖提交到model時類型轉換能夠正常進行,但是從model回填到視圖時並非自己最開始輸入的數值,那是因爲我們只設置了Converter,沒有設置Formatter。
  • 關於轉換器的配置,只要認識到ConversionService是類型轉換的核心,就會省事很多

Converter和Formatter的區別

  • Converter: 是spring-core-4.3.7.RELEASE.jar包提供的,用於Object to Object的轉換
  • Formatter: 是spring-context-4.3.7.RELEASE.jar包提供的,用於Object to String的轉換。

格式化註解

新的類型轉換提供兩個有用的註解,可以放在model類的屬性上,和被@Controller類的方法參數中。

  • NumberFormat
  • DateTimeFormat: 該註解默認使用Joda Time,需要在類路徑中包含Joda Time的包。默認情況下Spring MVC和web flow都沒有其他的日期相關的轉換器和格式化器。因此定義我們自己的日期相關轉換器和格式化器非常重要。
  • 此外,我們還可以參照上述兩個註解定義自己的註解.

關於綁定的另外兩點

  • 取消綁定
    可以使用bind屬性在特定情況下取消綁定。如下當觸發cancel事件時不會執行綁定操作
<view-state id="enterBookingDetails" model="booking">
    <transition on="proceed" to="reviewBooking">
    <transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
  • 顯式地指明綁定的字段
    使用標籤可以顯式指明需要綁定的字段,同時可以指明需要使用到的轉換器,和是否允許爲空。
<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="checkinDate" converter="shortDate" required="true" />
        <binding property="checkoutDate" converter="shortDate" required="true" />
        <binding property="creditCard" required="true" />
        <binding property="creditCardName" required="true" />
        <binding property="creditCardExpiryMonth" required="true" />
        <binding property="creditCardExpiryYear" required="true" />
    </binder>
    <transition on="proceed" to="reviewBooking">
    <transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>

注意事項
1. 沒有顯式指定綁定字段時,所有model對象的公共屬性都會被綁定;指定綁定字段時,則只有顯式指定的字段會被綁定
2. 沒有顯式聲明轉換器時,會使用自動檢測的轉換器
3. 聲明不允許爲空時,若出現空,則會產生驗證錯誤,並會重新繪製視圖並報錯。

綁定數據的驗證

Web Flow支持自定義驗證條件和JSR-303 Bean驗證框架

JSR-303 Bean Validation

  • 基礎配置
    首先類路徑中需要有一個validator的jar包,然後按照如下配置後,validator會應用到所有的添加了條件註解的model屬性中
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
<webflow:flow-builder-services id="flowBuilderServices" validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

  form中按如下方式配置

@NotNull
@Size(min = 2, max = 30, message="at least 3 chars" )
private String name;

  前端按如下配置,使用<sf:errors>可將name屬性的錯誤信息顯示出來

<sf:form action="${flowExecutionUrl}&_eventId=submit" commandName="searchForm" method="post">
    Name: <sf:input path="name"/> <sf:errors path="name"></sf:errors><br/>
       <input type="submit" value="Submit">
</sf:form>

  效果如下
這裏寫圖片描述

  • 部分驗證
    JSR-303支持部分驗證,通過驗證組的方式,使用如下(我在驗證該方式時是不行的,提示viwe-state標籤不允許出現validation-hints屬性)
@NotNull
@Size(min = 2, max = 30, groups = State1.class)
private String name;
<view-state id="state1" model="myModel" validation-hints="'group1,group2'">

  對該方法不做詳細解釋,最好還是參考一下JSR-303再來看

自定義驗證

JSR-303僅支持對Bean的驗證,如非空,字符串長度等。我們經常需要自定義驗證邏輯,有如下兩種方式進行自定義

  • 一是在model類內部定義一個以validate${stateId}(ValidationContext)爲簽名的方法,在view提交時會自動調用該驗證方法。stateId是view-state的id
// 官方示例
public class Booking {
    private Date checkinDate;
    private Date checkoutDate;
    ...

    public void validateEnterBookingDetails(ValidationContext context) {
        MessageContext messages = context.getMessageContext();
        if (checkinDate.before(today())) {
            messages.addMessage(new MessageBuilder().error().source("checkinDate").
                defaultText("Check in date must be a future date").build());
        } else if (!checkinDate.before(checkoutDate)) {
            messages.addMessage(new MessageBuilder().error().source("checkoutDate").
                defaultText("Check out date must be later than check in date").build());
        }
    }
}
  • 二是單獨定義一個類,類名爲${model}Validator,在其內部定義一個以validate${stateId}(${model}, ValidationContext)爲簽名的方法,然後將該類裝載到Spring中。
// 官方示例
@Component
public class BookingValidator {
    public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
        MessageContext messages = context.getMessageContext();
        if (booking.getCheckinDate().before(today())) {
            messages.addMessage(new MessageBuilder().error().source("checkinDate").
                defaultText("Check in date must be a future date").build());
        } else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
            messages.addMessage(new MessageBuilder().error().source("checkoutDate").
                defaultText("Check out date must be later than check in date").build());
        }
    }
}

  針對第二種情況也可以定義一個validate(${model}, ValidationContext)方法,這樣無論在哪個view-state的view返回時,只要綁定了該moel,都會調用該驗證方法。
  當validate(${model}, ValidationContext)和validate${stateId}(${model}, ValidationContext)都存在時,會先調用後者,再調用前者。

多說兩點

  • 失能驗證
    通過如下方式可以在局部使得驗證失效
<view-state id="chooseAmenities" model="booking">
    <transition on="proceed" to="reviewBooking">
    <transition on="back" to="enterBookingDetails" validate="false" />
</view-state>

轉移

在一個view-state中,可能發生各種轉移

  • 轉移之前執行操作
    可以在轉移之前執行特定的操作,如一個方法, 當方法返回false或者發生錯誤時,轉移不會繼續進行下去。而是重新渲染相應的部分
<transition on="submit" to="bookingConfirmed">
    <evaluate expression="bookingAction.makeBooking(booking, messageContext)" />
</transition>
  • 全局轉移
    定義全局有效的轉移操作
<global-transitions>
    <transition on="login" to="login" />
    <transition on="logout" to="logout" />
</global-transitions>
  • 事件處理器
    可以利用transition標籤只響應事件,而不做任何跳轉操作,從而作爲事件處理器
<transition on="event">
    <!-- Handle event -->
</transition>
  • 局部渲染
    利用<render>標籤可以進行局部渲染,一般用於Ajax的局部刷新操作
<transition on="next">
    <evaluate expression="searchCriteria.nextPage()" />
    <render fragments="searchResultsFragment" />
</transition>

  如上,當發生next事件時,首先執行翻頁操作,然後重新刷新查詢結果區域。fragment屬性應該引用想要刷新的view的id,當刷新多個區域時,使用逗號隔開。

view的回退控制

當我們從一個view跳轉到另一個view時,通過瀏覽器的回退按鈕,可以返回上一個view,我們可以對這個功能進行配置

  • 失能一個回退view,即當前view不能再下一個view上回退,回退到的是前一個view
<transition on="cancel" to="bookingCancelled" history="discard">
  • 失能所有回退view,即當前及之前所有view都是不能夠被回退的。
<transition on="confirm" to="bookingConfirmed" history="invalidate">

MessageContext

Spring web flow的MessageContext是用來記錄在flow執行期間的信息的。而其中包含的信息都由MessageBuilder產生,可以手動添加,也可以由系統自動產生。

手動添加信息

  • 手動添加普通文本
MessageContext context = ...(這裏一般是從上一級獲取到context對象)
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate")
    .defaultText("Check in date must be a future date").build());
context.addMessage(builder.warn().source("smoking")
    .defaultText("Smoking is bad for your health").build());
context.addMessage(builder.info()
    .defaultText("We have processed your reservation - thank you and enjoy your stay").build());
  • 添加Spring MessageSource得到的信息
MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate").code("checkinDate.notFuture").build());
context.addMessage(builder.warn().source("smoking").code("notHealthy")
    .resolvableArg("smoking").build());
context.addMessage(builder.info().code("reservationConfirmation").build());
  • 添加message bundle獲取的信息
    可以直接在view或flow中使用resourceBundle這個EL變量來獲取資源文件中的內容。(這個需要在web-flow同目錄下放置一個message.properties文件)
<input value="#{resourceBundle.reservationConfirmation}" />

系統自動產生信息

  有多重情況下web-flow會自動生成message,其中一種重要的情況是當view-to-model驗證失敗時,生成規則是,首先到資源文件中查找key爲${model}l.${property}.${errCode}的資源,如果找不到,則直接查找key爲${errCode}的資源。
   舉例:有model名爲booking,驗證其中的checkDate屬性,當出現類型不匹配時,系統給出的錯誤代碼爲typeMismatch,則會在資源文件中查找如下key的資源
booking.checkDate.typeMismatchtypeMismatch

下面大致列舉一下Spring MVC會自動註冊的轉換器和格式化器(調用ConversionServicetoString()方法)

@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@1cb91eff
@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@2918eadb
@org.springframework.format.annotation.DateTimeFormat java.time.LocalTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.LocalTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@6d563cf9
@org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.OffsetDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@2f20d514
@org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.OffsetTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@19212c1c
@org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.time.ZonedDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@2e061032
@org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed
@org.springframework.format.annotation.NumberFormat java.lang.Byte -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
@org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@839fe61
java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@30f9d9cf
java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2062b73e
java.lang.Enum -> java.lang.Integer : org.springframework.core.convert.support.EnumToIntegerConverter@65bd69cd
java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@24ccca42
java.lang.Integer -> java.lang.Enum : org.springframework.core.convert.support.IntegerToEnumConverterFactory@22b0410c
java.lang.Long -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$LongToInstantConverter@36c61628
java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@7a56bca3,java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@32680fd1
java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@21f63842,java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@3ae27639
java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@37dca65c
java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@5ad62efe
java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@5dbb5323
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.LocalDate: org.springframework.format.datetime.standard.TemporalAccessorParser@1a482c16
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.LocalDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@726b4b72
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.LocalTime: org.springframework.format.datetime.standard.TemporalAccessorParser@7d025d62
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.OffsetDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@7b82b59
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.OffsetTime: org.springframework.format.datetime.standard.TemporalAccessorParser@5d6bf8bc
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@13c06099,java.lang.String -> java.time.ZonedDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@4100b1dd
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Byte: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@806d8b
java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@3dd765a2
java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@4b28d17b
java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@5a85577c
java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@4a4bac52
java.lang.String -> java.nio.charset.Charset : org.springframework.core.convert.support.StringToCharsetConverter@77b7395f
java.lang.String -> java.time.Duration: org.springframework.format.datetime.standard.DurationFormatter@223f8ade
java.lang.String -> java.time.Instant: org.springframework.format.datetime.standard.InstantFormatter@744a1e70
java.lang.String -> java.time.MonthDay: org.springframework.format.datetime.standard.MonthDayFormatter@48e0bd1
java.lang.String -> java.time.Period: org.springframework.format.datetime.standard.PeriodFormatter@326e1143
java.lang.String -> java.time.YearMonth: org.springframework.format.datetime.standard.YearMonthFormatter@2013283d
java.lang.String -> java.util.Currency : org.springframework.core.convert.support.StringToCurrencyConverter@73d62889
java.lang.String -> java.util.Date: cn.floyd.pw.flow.converter.ApplicationConversionServiceFactoryBean$2@c01b01e,java.lang.String -> java.util.Date : cn.floyd.pw.flow.converter.ApplicationConversionServiceFactoryBean$1@bda7edf,java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed
java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@15f3ecb1
java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@1734f8d8
java.lang.String -> java.util.TimeZone : org.springframework.core.convert.support.StringToTimeZoneConverter@219e655b
java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@7a2a1350
java.nio.charset.Charset -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@79cac565
java.time.Duration -> java.lang.String : org.springframework.format.datetime.standard.DurationFormatter@223f8ade
java.time.Instant -> java.lang.Long : org.springframework.format.datetime.standard.DateTimeConverters$InstantToLongConverter@bbf9adc
java.time.Instant -> java.lang.String : org.springframework.format.datetime.standard.InstantFormatter@744a1e70
java.time.LocalDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalDateConverter@224c05f6
java.time.LocalDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalTimeConverter@bb6fc38
java.time.MonthDay -> java.lang.String : org.springframework.format.datetime.standard.MonthDayFormatter@48e0bd1
java.time.OffsetDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToInstantConverter@7e4083f
java.time.OffsetDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateConverter@1e188fca
java.time.OffsetDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateTimeConverter@79b804b1
java.time.OffsetDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalTimeConverter@178638ba
java.time.OffsetDateTime -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToZonedDateTimeConverter@5dd8aa91
java.time.Period -> java.lang.String : org.springframework.format.datetime.standard.PeriodFormatter@326e1143
java.time.YearMonth -> java.lang.String : org.springframework.format.datetime.standard.YearMonthFormatter@2013283d
java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@375fe360
java.time.ZonedDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToInstantConverter@5b6ff572
java.time.ZonedDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateConverter@44caea49
java.time.ZonedDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateTimeConverter@748fb300
java.time.ZonedDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalTimeConverter@362e6386
java.time.ZonedDateTime -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToOffsetDateTimeConverter@4864c695
java.time.ZonedDateTime -> java.util.Calendar : org.springframework.core.convert.support.ZonedDateTimeToCalendarConverter@53757723
java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@3effd16f,java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@6625da72
java.util.Calendar -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToInstantConverter@3ea63a04
java.util.Calendar -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateConverter@311b3b
java.util.Calendar -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateTimeConverter@b827cbe
java.util.Calendar -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalTimeConverter@27334ef2
java.util.Calendar -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToOffsetDateTimeConverter@18a5b69c
java.util.Calendar -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToZonedDateTimeConverter@2181b391
java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@43409a9e,java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@25a7d510
java.util.Currency -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@59874f76
java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@2d72afcb,java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@60b0c620
java.util.Date -> java.lang.String : cn.floyd.pw.flow.converter.ApplicationConversionServiceFactoryBean$2@c01b01e,@org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@3de4aaed
java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@635f0388,java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@7b746ecc
java.util.Locale -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@572faa97
java.util.Properties -> java.lang.String : org.springframework.core.convert.support.PropertiesToStringConverter@387ae6b6
java.util.UUID -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@19b50f9c
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章