8
10

More than 5 years have passed since last update.

Spring Boot キャンプ : Spring Boot + ModelMapper編

Posted at

Spring Bootキャンプシリーズ、Spring Boot + ModelMapper編です。

今回の目的

Spring BootアプリでModelMapperを利用してBeanのコピーを行います。

本記事では、独自に開発したStarterを使用します。
StarterはMaven Centralで公開しており、誰でも利用できます。

https://github.com/yoshikawaa/modelmapper-spring-boot-starter

今回使用するライブラリ

  • spring-boot-starter:2.2.0.M4
  • modelmapper-spring-boot-starter:0.1.0
  • spring-boot-starter-test:2.2.0.M4
  • lombok:1.18.8

ModelMapperとは

Java Beanのコピーを隠蔽し、コードをすっきりさせます。

public Todo convert(TodoForm form) {

    Todo todo = new Todo();
    todo.setTodoId(form.getTodoId());
    todo.setTodoTitle(form.getTodoTitle());
    todo.setFinished(form.isFinished());
    todo.setCreatedAt(form.getCreatedAt());
    return todo;
}

これが以下のようにすっきりします。

public Todo convert(TodoForm form) {

    ModelMapper modelMapper = new ModelMapper();
    return modelMapper.map(form, Todo.class);
}

ModelMapperの使い方はこちらの記事が詳しいので、ぜひ確認してみてください!

Spring Bootへの対応

本題に入りますが、ModelMapper公式ではSpring Boot Starterが提供されていません。
Related Projectとして個人が開発したStarterがアナウンスされています。

ただ、このStarterだとちょっと気になる点がありました。

  • サポートバージョンの古さ
    • modelmapper:0.7.5
    • spring-boot-starter:1.2.5.RELEASE
  • カスタマイズの貧弱さ
    • Property MappingとConverter以外をカスタマイズする場合、Starterのメリットがない

作ってみました!

ということで、自分でStarterを作ってMaven Centralで公開してみました!
今回の記事では、こちらのStarterを利用していきます。

modelmapper-spring-boot-starter

Spring BootでModelMapperを利用するためのスターターです。

Spring BootのAuto Configurationの仕組みを利用することで、Spring BootアプリでModelMapperを使用するためのBean定義を自動的に行ってくれます。開発者は依存関係にmodelmapper-spring-boot-starterを追加するだけでOKです。

pom.xml

    <dependencies>
        <dependency>
            <groupId>io.github.yoshikawaa.modelmapper.spring.boot</groupId>
            <artifactId>modelmapper-spring-boot-starter</artifactId>
            <version>0.1.0</version>
        </dependency>
    </dependencies>

src/main/java/*/Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

メインクラスはSpring Bootアプリのデフォルトから何も変更していません。

ModelMapperを使用するクラス

ここではControllerで使用してみます。
ModelMapperを使う以外の要素は極力省略しています。

src/main/java/*/web/TodoController.java

@Controller
@RequestMapping("todo")
public class TodoController {

    // (1)
    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private TodoService todoService;

    @PostMapping
    public String create(TodoForm form) {

        // (2)
        Todo todo = modelMapper.map(form, Todo.class);
        todoService.create(todo);

        // ommitted.
    }
}

(1) ModelMapperは自動的にセットアップされるので、インジェクションするだけで利用できます。

(2) ModelMapperのmapメソッドでBeanをコピーします。

カスタムマッピング

ModelMapperのデフォルト設定では、コピー元と先で名前が同じプロパティを自動的にコピーします。
名前が異なるプロパティをコピーするには、カスタムマッピングが必要になります。

src/main/java/*/ModelMapperConfig.java

@Configuration
public class ModelMapperConfig {

    @Bean
    TypeMapConfigurer<TodoForm, Todo> typeMap() {
        return new TypeMapConfigurer<TodoForm, Todo>() {
            @Override
            public void configure(TypeMap<TodoForm, Todo> typeMap) {
                typeMap.addMapping(TodoForm::getTodoTitle, Todo::setTitle);
            }
        };
    }
}

カスタムマッピングのため、TypeMapConfigurer<コピー元, コピー先>のBeanを定義します。
TypeMapConfigurerはBean定義すれば、自動的に適用されます。

Note.
@Beanではなく@Componentで定義してもOKです。
@Componentの場合は、1カスタムマッピング=1クラスになります。

カスタムマッピングの検証

カスタムマッピングを定義したら、プロパティに抜け漏れがないことを検証することをお勧めします。

src/main/resources/application.yml

modelmapper:
  validate-enabled: true

ModelMapperのセットアップでvalidateメソッドが実行され、プロパティのマッピングに抜け漏れがあればエラーになります。

振る舞いの変更

ModelMapperではコピー時の振る舞いをカスタマイズすることで、より柔軟なコピーが可能です。
ここでは、コピー元プロパティがnullの時はコピーしないよう変更してみます。

src/main/resources/application.yml

modelmapper:
  skip-null-enabled: true

ModelMapperのセットアップでsetSkipNullEnabledメソッドが実行され、コピー元プロパティがnullの時はコピーされなくなります。

ModelMapperの設定を確認する

セットアップされたModelMapperの設定を確認するには、TRACEレベルのログを出力すればOKです。
ログ出力するレベルは、Spring Bootのloggingプロパティを利用して設定します。

src/main/resources/application.yml

logging:
  level:
    io.github.yoshikawaa.modelmapper.spring.boot.autoconfigure: trace

JUnitテストケース

JUnitテストケースでは、使用するテスト用Auto-Configにより設定が異なるので注意しましょう。

@SpringBootTestを利用する場合

すべてのBeanを読み込みます。
この場合は、ModelMapperを利用するために意識することはありません。

@WebMvcTestなどを利用する場合

軽量化のため限定的にBeanを読み込むため、独自のAuto-Configや@Configurationクラス等は読み込まれません。
この場合は、ModelMapperを利用するための設定が必要になります。

  • @ImportAutoConfiguration -> ModelMapperのAuto-Configを読み込みます。
  • @Import -> カスタムマッピングを読み込みます。

src/test/java/*/web/TodoControllerTest.java

@WebMvcTest
@ImportAutoConfiguration(ModelMapperAutoConfiguration.class) // (1)
@Import(ModelMapperConfig.class) // (2)
class TodoControllerTest {

    @Autowired
    MockMvc mvc;

    @Test
    void testCreate() throws Exception {
        // execute & assert
        mvc.perform(post("/todo").param("todoTitle", "sample"))
            .andExpect(status().isOk());
    }

(1) @ImportAutoConfigurationでModelMapperのセットアップを行うModelMapperAutoConfigurationクラスを読み込みます。

(2) @Importでカスタムマッピングの@Configurationクラスを読み込みます。配列で複数読み込むことも可能です。

Note.
カスタムマッピングを@Componentで作った場合、@Importの代わりに@ComponentScanで読み込めばOKです。

まとめ

今回は満足なStarterがなかったので、それなりに手を加えることになりましたが、Starterの作り方も分かったので良かったですw

ModelMapperを実開発で使う場合には、全体的な振る舞いをどこに集約するか、カスタムマッピングをどう管理するか、あたりが課題になると思うので、Starterを使うことによりルール付けされると良いですね。

今回作成したStarterは、記事に書いた以外にもModelMapperの豊富なカスタマイズをサポートしています。
利用される方はStarterのリファレンスModelMapperのリファレンスを確認してみてください!

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10