Spring WebMVCをやってみる (18) - AnnotationFormatterFactory -

2014-01-04T00:00:00+00:00 Java Spring Framework

前回でも書いたように、@DateTimeFormatのようなアノテーションによってFormatterが作用する仕組みっていうのを作る事も出来る。それを担うのがAnnotationFormatterFactoryになる模様

要件

前回のをそのまま利用するけどコントローラー側では@SampleFormatっていうアノテーションを利用することによりFormatterを作用させるようにする。でそのFormatter自体は前回とまったくそのまま

package sample;

import java.text.ParseException;
import java.util.Locale;

import org.springframework.format.Formatter;

public class SampleFormatter implements Formatter<Sample> {

    @Override
    public String print(Sample object, Locale locale) {
        return object.getName().toLowerCase();
    }

    @Override
    public Sample parse(String text, Locale locale) throws ParseException {
        return Sample.valueOf(text.toUpperCase());
    }
}

を使う

SampleFormat.java

引数実装とかもやっておけばAnnotationFormatterFactoryを通じてFormatterに渡す事も可能だと思うのだけど今回はパス

package sample;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface SampleFormat {
}

まぁ見て分かるようにアノテーションのインターフェースなだけ

SampleFormmaterFactory.java

package sample;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Parser;
import org.springframework.format.Printer;

public class SampleFormatterFactory implements AnnotationFormatterFactory<SampleFormat> {

    @Override
    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(Arrays.asList(Sample.class));
    }

    @Override
    public Printer<?> getPrinter(SampleFormat annotation, Class<?> fieldType) {
        return new SampleFormatter();
    }

    @Override
    public Parser<?> getParser(SampleFormat annotation, Class<?> fieldType) {
        return new SampleFormatter();
    }
}

Formatterはorg.springframework.format.Parser自体を継承しているインターフェースなので、そのままインスタンス作ってぶん投げれば良い

spring-web-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <context:component-scan base-package="sample" />
    <mvc:annotation-driven conversion-service="conversionService" />

    <bean
        id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

        <property name="formatters">
            <set>
                <bean class="sample.SampleFormatterFactory" />
            </set>
        </property>
    </bean>

</beans>

まぁ前回同様にformattersにAnnotationFormatterFactoryの実装クラスを指定してやりゃいい模様で

SampleController.java

package sample;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/sample")
public class SampleController {

    @RequestMapping("/test")
    public void test(@RequestParam("name") @SampleFormat Sample sample, ModelMap model) {
        model.addAttribute("sample", sample);
    }
}

っていう感じで利用できる。

ただ不明な所として1点上げると、「getPrinter実装してもどういう作用で発生するのかが謎」。普通にFormatterを使う場合とかだと<spring:bind expression="" />等で作用されるっていうはあるのだけど、このAnnotationFormatterFactoryの事象だけはgetPrinterメソッドが作用する事が無い。ここがよう分からんっていうところかなと

んまぁFormatterに関するネタはこれで終わり(なはず)なんだけど、疑問点として「フォームコンポーネントにフォーマット化した文字列を処理させたい場合はどうなのか」っていう点。<form:input>とか使ってもFormatter#printが作用してくれないので

<input type="text" value="<spring:eval expression="省略" />" />

みたいに書かないとダメなのかっていう点。そこも微妙なのでわかり次第書く予定

余談: FormatterRegistrarを使う場合

package sample;

import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;

public class SampleFormatterRegistrar implements FormatterRegistrar {

    @Override
    public void registerFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldAnnotation(new SampleFormatterFactory());
    }
}

みたいにやれば良いっぽい

fetchmailを使ってgmail上のメールを取得する方法 Spring WebMVCをやってみる (17) - Formatter -