LoginSignup
1
0

More than 3 years have passed since last update.

Spring BootでModelMapperにカスタムのConverterを楽に追加できるようにする

Posted at

概要

ModelMapperを使うとObject間の変換ロジックをそこに集約できて便利です。
こちらの記事 がModelMapperのメリットをわかりやすく説明しています。
ModelMapperは変換元と変換先のフィールド名が同じならmodelMapper.map(input, Output.class)とすれば変換できますが、フィールド名や型が違うときはカスタムのConverterを追加する必要があります (参考記事)。
カスタムのConverterを作ったときにわざわざmodelMapper.addConverter(newConverter)とするのが大変なのでしなくて良い方法を提示します。

動作確認環境

対応

ModelMapperの登録

build.gradleに以下を追加
implementation 'org.modelmapper:modelmapper:2.3.7'

以下でModelMapperをSpringのアプリケーションへDIする。
このとき、List<Converter<?, ?>>をDIしているのがポイント。Spring BootはListやMapであるクラスをDIすることができる(参考記事)。
ListでDIしたConverterクラス(後述)をすべてModelMapperへ登録する。

import java.util.List;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppBean {

  private List<Converter<?, ?>> converters;

  public AppBean(List<Converter<?, ?>> converters) {
    this.converters = converters;
  }

  @Bean
  public ModelMapper modelMapper() {
    var modelMapper = new ModelMapper();
    for (Converter<?, ?> converter : converters) {
      modelMapper.addConverter(converter);
    }
    return modelMapper;
  }
}

実装例: https://github.com/yoshikipom/runlog-api/blob/451979c464e2de085ce4d603f5422e70e8d66dc3/src/main/java/jp/yoshikipom/runlogapi/AppBean.java

Conveterの登録

ここのクラスが複数になる想定。
org.modelmapper.AbstractConverter のConvertメソッドを実装すればよい。型引数に<Inputクラス, Outputクラス>を入れる。
ModelMapperの登録部分へDIする必要があるため@Componentをつけるのを忘れないこと。

import org.modelmapper.AbstractConverter;
import org.springframework.stereotype.Component;

@Component
public class SomethingConverter extends AbstractConverter<Inout, Output> {

  @Override
  protected Output convert(Input source) {
    // your conversion rules
    return output;
  }
}

実装例: https://github.com/yoshikipom/runlog-api/blob/master/src/main/java/jp/yoshikipom/runlogapi/infra/record/RecordEntityConverter.java

呼び出しかた

ここまでくると呼び出せる。

@Service 
public class SomethingService {

  private final ModelMapper modelMapper;

  public SomethingService(ModelMapper modelMapper) {
    this.modelMapper = modelMapper;
  }

  public Output method() {
    // something
    return modelMapper.map(input, Output.class))
  }

実装例: https://github.com/yoshikipom/runlog-api/blob/master/src/main/java/jp/yoshikipom/runlogapi/infra/record/RecordRepoImpl.java

(おまけ)テスト

楽に追加する話とはあまり関係がないがModelMapperを使うことで変換ロジックが隠蔽されるためテストもシンプルになる。

@SpringBootTest
class SomethingServiceTest {

  @Autowired
  private SomethingService target;
  @MockBean
  private ModelMapper modelMapper;

  @Mock
  private Input input;
  @Mock
  private Output output;

  @BeforeEach
  void setUp() {
    when(modelMapper.map(input, Output.class)).thenReturn(output);
  }

  @Test
  void test_success() {
    // something
    var actual = target.method();
    assertEquals(output, actual);
  }
}

実装例: https://github.com/yoshikipom/runlog-api/blob/master/src/test/java/jp/yoshikipom/runlogapi/infra/record/RecordRepoImplTest.java

参考

1
0
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
1
0