[スプリングコア]3データバインド抽象


  • データバインドメディア
  • 技術観点:
  • 機能、ターゲットオブジェクトの属性値を設定する
  • ユーザーの観点:ユーザー入力値を適用します.ドメインモデルへの動的割当て
  • ❤を割り当てるときにバインドする必要がある理由:ユーザーが入力した値は主に文字列であり、オブジェクトの異なる属性タイプに一致させるためである.
  • スプリングが提供するインタフェース
  • PropertyEditor


    (最も古典的なデータバインド方法を表示)
  • main\java\me\jinmin\Event
    package me.jinmin;
    
    public class Event {
    
        private Integer id;
    
        private String title;
    
        public Event(Integer id) {
            this.id = id;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        @Override
        public String toString() {
            return "Event{" +
                    "id=" + id +
                    ", title='" + title + '\'' +
                    '}';
        }
    }
  • main\java\me\jinmin\EventController
    package me.jinmin;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class EventController {
    
        @GetMapping("/event/{evnet}")
        public String getEvent(@PathVariable Event event){
            System.out.println(event);
            return event.getId().toString();
        }
    }
  • {event}:イベントのid

  • 入力した数値タイプのidをイベントタイプ(ドメインタイプ)に変換し、スプリングで受信する必要があります.

  • ❗Test (Spring boot WebMvc Test)

  • MockMvc:WebApp.スプリングMVC動作をサーバに配備せずに再生する.

  • 必要な依存性のテスト
    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
  • test\java\me\jinmin\EventControllerTest
    package me.jinmin;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    @RunWith(SpringRunner.class)
    @WebMvcTest
    public class EventControllerTest {
    
        @Autowired
        MockMvc mockMvc;
    
        @Test
        public void getTest() throws Exception {
            mockMvc.perform(get("/event/1"))
                    .andExpect(status().isOk())
                    .andExpect(content().string("1"));
        }
    }

  • 結果:エラー
  • main\java\me\jinmin\EventEditor
    package me.jinmin;
    
    import java.beans.PropertyEditorSupport;
    
    public class EventEditor extends PropertyEditorSupport {
    
        @Override
        public String getAsText() {
            Event event = (Event) getValue();
            return event.getId().toString();
        }
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            
            setValue(new Event(Integer.parseInt(text));
        }
    }

  • 共有するValueは、PropertyEditorが所有する値です.♥(重要)この値は異なるスレッドに共有され、状態は完全→Non-Thread-Safeです.→実装体を複数のスレッドで共有することはできません.(=beanとして登録できません.)

  • では、登録しないで空にして、どうやって使いますか?◇EventControllerにてDataBinderの実装体を実現する.
    package me.jinmin;
    
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.InitBinder;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class EventController {
    
        @InitBinder
        public void init(WebDataBinder webDataBinder){
            webDataBinder.registerCustomEditor(Event.class, new EventEditor());
        }
    
        @GetMapping("/event/{evnet}")
        public String getEvent(@PathVariable Event event){
            System.out.println(event);
            return event.getId().toString();
        }
    }
  • test\java\me\jinmin\EventControllerTest駆動結果.
    print:
    Event{id=1, title='null'}

  • 💜n/a.結論💜, Controllerが任意の要求を処理する前に、データバインド
    @InitBinder
        public void init(WebDataBinder webDataBinder){
            webDataBinder.registerCustomEditor(Event.class, new EventEditor());
        }

  • メソッドのPropertyEditor(WebDataBinder)を使用して、Editorから受信したValue값(=文字列の1)を数値に変換し、Eventオブジェクトに変換します.テストに問題はありません.

  • 欠点:オブジェクトとStringの間でのみ変換できます(ほとんどの場合、非常に注意して使用します).
  • Converter & Formatter


    Converter


  • Conレガシー:ソースタイプをTタイプに変換する非常に一般的な変換器です.

  • ステータス情報なし=ステータスなし=Thread-Safe

  • Thread-Safeは、各クラスを空に登録できますが、ConverterRegistry(\\直接x,Spring Web MVCコンフィギュレータ)に登録して使用します.
  • \main\me\jinmin\EventController
    package me.jinmin;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class EventController {
    
        @GetMapping("/event/{event}")
        public String getEvent(@PathVariable Event event){
            System.out.println(event);
            return event.getId().toString();
        }
    }
  • \main\me\jinmin\EventConverter
    package me.jinmin;
    
    import org.springframework.core.convert.converter.Converter;
    
    public class EventConverter {
    
        public static class StringToEventConverter implements Converter<String, Event> {
            @Override
            public Event convert(String source) {
                return new Event(Integer.parseInt(source));
            }
        }
    
        public static class EventToStringConverter implements Converter<Event, String>{
            @Override
            public String convert(Event source) {
                return source.getId().toString();
            }
        }
    }
  • \main\me\jinmin\WebConfig
    package me.jinmin;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(new EventConverter.StringToEventConverter());
        }
    }
  • 💥 Spring Web MVC ConfigurerConverterを登録します.
  • \test\java\me\jinmin\EventControllerTestおよびテスト駆動結果
    package me.jinmin;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    @RunWith(SpringRunner.class)
    @WebMvcTest
    public class EventControllerTest {
    
        @Autowired
        MockMvc mockMvc;
    
        @Test
        public void getTest() throws Exception {
            mockMvc.perform(get("/event/1"))
                    .andExpect(status().isOk())
                    .andExpect(content().string("1"));
        }
    
    }
    print:
    Event{id=1, title='null'}
  • 試験要求の/event/1のうちの1は、Converterによってイベントタイプに変換され、Controllerからイベントタイプが受信できることを示す.
  • Formatter


  • PropertyEddiorの代替製品

  • オブジェクトとStringの変換

  • オプションの文字列ローカライズ(Localeベース)

  • 「読み込み-保存」のため、空に登録できますが、FormateRegistryに登録して使用できます.
  • \main\java\me\jinmin\EventControllerは上のConverterと同じです
  • \main\java\me\jinmin\EventFormatter
    package me.jinmin;
    
    import org.springframework.format.Formatter;
    
    import java.text.ParseException;
    import java.util.Locale;
    
    public class EventFormatter implements Formatter<Event> {
        @Override
        public Event parse(String text, Locale locale) throws ParseException {
            return new Event(Integer.parseInt(text));
        }
    
        @Override
        public String print(Event object, Locale locale) {
            return object.getId().toString();
        }
    }
  • \main\java\me\jinmin\WebConfig
    package me.jinmin;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addFormatter(new EventFormatter());
        }
    }
  • \test\java\me\jinmin\EventControllerTestコードおよび結果はConverterと同じである.
  • ConversionService


  • PropertyEditorとは異なり、実際の変換はConversionServiceインタフェースを使用し、「読み取り-セキュリティ」機能を備えています.

  • Spring MVC、Spring xml空設定ファイル、Spring Expression Languageで使用します.

  • ❗DefaultFormattingConversionService

  • 複数のデフォルトConverterとFormateを登録します(継承関係)
    package me.jinmin;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.stereotype.Component;
    
    @Component
    public class AppRunner implements ApplicationRunner {
    
        @Autowired
        ConversionService conversionService;
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println(conversionService.getClass().toString());
        }
    }
    print:
    class org.springframework.boot.autoconfigure.web.format.WebConversionService

  • 出力はConversionServiceではなく、WebConversionService(スプリングガイドが提供するクラス)である.(実際の使用率は高くありませんが、トレーニングで理解する必要があります)

  • SpringBootでは、FormatterConverterの空きが登録されていると、その空きが見つかり自動的に登録されます.(つまり、既存のWebConfigで登録する必要はありません.)(確認して!)

  • まず、WebConfigを削除し、EventConverterの内部クラスを@Componentに登録してアプリケーションを起動します.
    package me.jinmin;
    
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.stereotype.Component;
    
    public class EventConverter {
    
        @Component
        public static class StringToEventConverter implements Converter<String, Event> {
            @Override
            public Event convert(String source) {
                return new Event(Integer.parseInt(source));
            }
        }
    
        @Component
        public static class EventToStringConverter implements Converter<Event, String>{
            @Override
            public String convert(Event source) {
                return source.getId().toString();
            }
        }
    }

  • 結果


  • 第二に、WebConfigを削除し、EventFormatter@Componentとして登録し、アプリケーションを起動する.
    package me.jinmin;
    
    import org.springframework.format.Formatter;
    import org.springframework.stereotype.Component;
    
    import java.text.ParseException;
    import java.util.Locale;
    
    @Component
    public class EventFormatter implements Formatter<Event> {
        
        @Override
        public Event parse(String text, Locale locale) throws ParseException {
            return new Event(Integer.parseInt(text));
        }
    
        @Override
        public String print(Event object, Locale locale) {
            return object.getId().toString();
        }
    }
  • 結果は、1番目(Conveter)と同じ:

  • テストではどのように起動しますか?

  • 発(補足説明)階層テスト→ページ関連の空のみ登録する.主にController人しか登録していませんが、FormatterConverterが正しく登録されていなければ、テストは破られます.
  • \test\java\me\jinmin\EventControllerTest

  • Formatter Test
  • @WebMvcTest@WebMvcTest({EventFormatter.class, EventController.class})

  • Conveter Test
  • @WebMvcTest@WebMvcTest({EventConverter.StringToEventConverter.class, EventController.class})

  • 2つの結果
    print:
    Event{id=1, title='null'}

  • 🔔(推奨)Formateの使用:主にWebで使用されます.△退役した老兵も悪くない.