Spring Boot環境でJava8日付型(JSR-310)を使うための設定

Springでは既にJava8で導入された新日付型(以下、JSR-310日付型)をサポートしていますが、実際に使用する際には各コンポーネントで設定が必要だったり、フォーマットを指定したかったりするので設定方法をまとめておきます。

サンプルはこちら。
https://github.com/tag1216/example-spring-boot-jsr310

合わせてSpringの各コンポーネントで標準で対応していない型(以下、非標準型)を扱う方法を軽く解説します。

# 構成

- Spring Boot(1.3.0.RELEASE)
- Spring MVC
- Spring Data JPA
- Jackson

# Spring Data JPA

## 非標準型の対応方法

Entityクラスやクエリーメソッドの引数で非標準型を使うには、[AttributeConverter][]インターフェースの実装クラスを作成して標準型への変換を行います。

## JSR-310の対応方法

Spring Data JPA では[Jsr310JpaConverters][]というクラスにJSR-310日付型の変換が用意してあります。
これをスキャン対象に設定すればでJSR-310日付型が使えるようになります。

```java:application.java
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;

@SpringBootApplication
@EntityScan(basePackageClasses = {Application.class, Jsr310JpaConverters.class})
public class Application {
```

# Jackson

## 非標準型の対応方法

Jacksonでは[JsonSerializer][]と[JsonDeserializer][]を実装して[ObjectMapper][]に登録することで非標準の型に対応できます。

## JSR-310の対応方法

JSR-310日付型のSerializer/Deserializerは[jackson-datatype-jsr310][]という追加モジュールに用意してあります。
これを依存関係に追加しておけばSpring Boot環境ではObjectMapperにモジュールを自動で追加してくれるのでJSR-310日付型が使えるようになります。

ただし、これだけだとEntityの属性に[@JsonFormat][]でフォーマットを指定しないと`[2015,12,1]`というような配列になってしまうのでイマイチです。

そこで、次のようにJSR310日付型のモジュールを追加する際にフォーマットを指定しておく事で、[@JsonFormat][]が指定されていない時のデフォルトとして使用されます。

```java:Application.java
    @Bean
    public ObjectMapper jsonObjectMapper() {
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
        module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);
        return mapper;
    }
```

# Spring MVC

## 非標準型の対応方法

Springでは[ConversionService][]に[Formatter][]や[Converter][]を登録して型変換を行います。

Spring BootではFormatterやConverterをコンポーネントとして登録しておけば自動でConversionServiceに追加されます。

[Spring Boot Reference Guide - 27.1.1 Spring MVC auto-configuration](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration)

## JSR-310の対応方法

[Jsr310DateTimeFormatAnnotationFormatterFactory][]クラスが予め登録されているので、[@DateTimeFormat][]を使用すればJSR-310日付型を使用できます。

```java:PlanRestController.java
  @RequestMapping(method = GET)
    public List<Plan> index(
            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy/MM/dd")  LocalDate date,
            @RequestParam(required = false) String title) {
```

また、以下のようにFormatterを登録しておけば`@DateTimeFormat`を使用せずにJSR-310日付型を使用する事ができます。
ただし、Formatterを登録すると`@DateTimeFormat`が効かなくなるようなので注意が必要です。

```java:Application.java
    @Bean
    public Formatter<LocalDate> localDateFormatter() {
        return new Formatter<LocalDate>() {
            @Override
            public String print(LocalDate object, Locale locale) {
                return object.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
            }
            @Override
            public LocalDate parse(String text, Locale locale) throws ParseException {
                return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy/MM/dd"));
            }
        };
    }
```


[AttributeConverter]:https://docs.oracle.com/javaee/7/api/javax/persistence/AttributeConverter.html

[Jsr310JpaConverters]:https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java

[ObjectMapper]:http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/ObjectMapper.html
[JsonSerializer]:http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/JsonSerializer.html
[JsonDeserializer]:http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/JsonDeserializer.html

[jackson-datatype-jsr310]:https://github.com/FasterXML/jackson-datatype-jsr310

[@JsonFormat]:https://github.com/FasterXML/jackson-annotations/blob/master/src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java

[ConversionService]:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/convert/ConversionService.html
[Formatter]:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/Formatter.html
[Converter]:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/convert/converter/Converter.html
[Jsr310DateTimeFormatAnnotationFormatterFactory]:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.html
[@DateTimeFormat]:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/annotation/DateTimeFormat.html