12. Spring Frameworkとの連携

DI(Depenency Injection) 機能のフレームワーク Spring Framework と連携することができます。

Spring Framework のコンテナで管理可能、DI可能な部分は、次の箇所になります。

これらの機能・箇所は、 BeanFactory によるインスタンスを新しく作成する箇所であり、その実装を SpringBeanFactory に切り替え得ることで、DIを実現します。

表 - 12.1 Spring Frameworkとの連携可能な箇所

機能・箇所

説明

独自の書式を指定する機能

TextFormatter の実装クラスがSpringBeanとして管理可能です。

独自の変換処理の実装機能

ConversionProcessorFactory の実装クラスがSpringBeanとして管理可能です。

独自のカラム値の検証の実装機能

ConstraintProcessorFactory の実装クラスがSpringBeanとして管理可能です。

独自のValidatorの実装機能

CsvValidator の実装クラスがSpringBeanとして管理可能です。

独自のリスナーの実装機能

リスナクラスがSpringBeanとして管理可能です。

独自のProcessorBuilder実装機能

ProcessorBuilder の実装クラスがSpringBeanとして管理可能です。

BeanValidationの連携機能

BeanValidation の検証用の実装クラスがSpringBeanとして管理可能です。

エラーメッセージのカスタマイズ機能

MessageResolver の内容を MessagSource から参照可能です。

12.1. ライブラリの追加

Spring Frameworkを利用する際には、ライブリを追加します。 Mavenを利用している場合は、pom.xmlに以下を追加します。

Spring Frameworkのバージョンは、3.0以上を指定してください。

1<dependency>
2    <groupId>org.springframework</groupId>
3    <artifactId>spring-context</artifactId>
4    <version>4.3.2.RELEASE</version>
5</dependency>

12.2. SpringBeanFactoryの設定方法

12.2.1. XMLによるコンテナの設定

XMLによる設定方法を説明します。

コンテナの定義の基本は次のようになります。

  • アノテーションによるDIの有効化を行います。

  • コンポーネントスキャン対象のパッケージの指定を行います。

  • com.github.mygreen.supercsv.builder.SpringBeanFactory をSpringBeanとして登録します。

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!-- XMLによるコンテナの定義 -->
 3<beans xmlns="http://www.springframework.org/schema/beans"
 4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5    xmlns:context="http://www.springframework.org/schema/context"
 6    xsi:schemaLocation="http://www.springframework.org/schema/beans
 7        http://www.springframework.org/schema/beans/spring-beans.xsd
 8        http://www.springframework.org/schema/context
 9        http://www.springframework.org/schema/context/spring-context.xsd
10    ">
11
12    <!-- アノテーションによるDIの有効化の定義 -->
13    <context:annotation-config />
14
15    <!-- コンポーネントスキャン対象のパッケージの指定 -->
16    <context:component-scan base-package="sample.spring" />
17
18    <!-- Springのコンテナを経由するCSV用のBeanFactoryの定義 -->
19    <bean id="springBeanFactory" class="com.github.mygreen.supercsv.builder.SpringBeanFactory" />
20
21</beans>

12.2.2. JavaConfigによるコンテナの設定

Spring Framework3.0から追加された、JavaソースによるSpringBean定義の方法を説明します。

JavaConfigによる設定を使用する場合は、Spring Frameworkのバージョンをできるだけ最新のものを使用してください。 特に、機能が豊富なバージョン4.0以上の使用を推奨します。

 1import org.springframework.context.annotation.Bean;
 2import org.springframework.context.annotation.ComponentScan;
 3import org.springframework.context.annotation.Configuration;
 4import org.springframework.context.annotation.Description;
 5
 6import com.github.mygreen.supercsv.builder.SpringBeanFactory;
 7
 8// Javaによるコンテナの定義
 9@Configuration
10@ComponentScan(basePackages="sample.spring")
11public class SuperCsvConfig {
12
13    @Bean
14    @Description("Springのコンテナを経由するCSV用のBeanFactoryの定義")
15    public SpringBeanFactory springBeanFactory() {
16        return new SpringBeanFactory();
17    }
18
19}

12.2.3. SpringBeanとしての定義

ステレオタイプのアノテーション @Component/@Service/@Reposition/@Controller をサポートしているため、 これらを使いSpringBeanを定義します。

12.2.3.1. 独自の書式の作成

独自の書式の TextFormatter を定義する際には、スコープを proptotype にします。 読み込み時のエラーメッセージが、カラムごとに異なる場合があるため、インスタンスを別にします。

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.context.annotation.Scope;
 3import org.springframework.stereotype.Component;
 4
 5import com.github.mygreen.supercsv.cellprocessor.format.AbstractTextFormatter;
 6import com.github.mygreen.supercsv.cellprocessor.format.TextParseException;
 7
 8
 9/**
10 * {@link SampleType}に対するTextFromatterの実装
11 */
12@Scope("prototype")
13@Component
14public class SampleTypeFormatter extends AbstractTextFormatter<SampleType> {
15
16    // SpringBeanのインジェクション
17    @Autowired
18    private SampleService sampleService;
19
20    @Override
21    public SampleType parse(final String text) {
22
23        try {
24            // 業務ロジックなので省略
25        } catch(Exception e) {
26            throw new TextParseException(text, SampleType.class, e);
27        }
28
29    }
30
31    @Override
32    public String print(final SampleType object) {
33        // 業務ロジックなので省略
34    }
35
36}

12.2.3.2. 独自の変換/検証の作成

検証用の ConstraintProcessorFactory の例として、DBにユーザが存在するかチェックするCellProcessorで説明します。

変換用の ConversionProcessorFactory も同様の実装方法になります。

  • ConstraintProcessorFactory はシングルトンで定義します。

  • CellProcessorは、カラムごとに固有なインスタンスにするため、Springのコンテナ管理外とします。

  • CellProcessor内で、SpringBeanを利用したい場合は、 ConstraintProcessorFactory クラスでインジェクションしておき、 それをコンストラクタやsetterメソッドで渡すようにします。

 1import java.util.Optional;
 2
 3import org.springframework.beans.factory.annotation.Autowired;
 4import org.springframework.stereotype.Component;
 5import org.supercsv.cellprocessor.ift.CellProcessor;
 6
 7import com.github.mygreen.supercsv.builder.BuildType;
 8import com.github.mygreen.supercsv.builder.Configuration;
 9import com.github.mygreen.supercsv.builder.FieldAccessor;
10import com.github.mygreen.supercsv.cellprocessor.ConstraintProcessorFactory;
11import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
12
13/**
14 * ユーザ名がDBに存在するか検証するCellProcessorを作成するクラス
15 */
16@Component
17public class UserNameExistFactory implements ConstraintProcessorFactory<CsvUserNameExist> {
18
19    // SpringBeanのインジェクション
20    @Autowired
21    private UserService userService;
22
23    @Override
24    public Optional<CellProcessor> create(final CsvUserNameExist anno, final Optional<CellProcessor> next,
25            final FieldAccessor field, final TextFormatter<?> formatter, final Configuration config) {
26
27        // UserService はCellProcessorに渡す
28        final UserNameExist processor = next.map(n -> new UserNameExist(userService, n))
29                .orElseGet(() -> new UserNameExist(userService));
30        processor.setValidationMessage(anno.message());
31
32        return Optional.of(processor);
33    }
34
35}

CellProcessor は、コンストラクタで渡されたSpringBean(UserService)のメソッドを呼び出します。

 1import org.supercsv.cellprocessor.ift.CellProcessor;
 2import org.supercsv.cellprocessor.ift.StringCellProcessor;
 3import org.supercsv.util.CsvContext;
 4
 5import com.github.mygreen.supercsv.cellprocessor.ValidationCellProcessor;
 6
 7/**
 8 * ユーザ名の存在チェックを行う制約のCellProcessor
 9 */
10public class UserNameExist extends ValidationCellProcessor implements StringCellProcessor {
11
12    private final UserService userService;
13
14    public UserNameExist(final UserService userService) {
15        checkPreconditions(userService);
16        this.userService = userService;
17    }
18
19    public UserNameExist(final UserService userService, final CellProcessor next) {
20        super(next);
21        checkPreconditions(userService);
22        this.userService = userService;
23    }
24
25    private static void checkPreconditions(final UserService userService) {
26        if(userService == null) {
27            throw new NullPointerException("userService should not be null");
28        }
29
30    }
31
32    @Override
33    public <T> T execute(final Object value, final CsvContext context) {
34
35        if(value == null) {
36            return next.execute(value, context);
37        }
38
39        final String result = value.toString();
40
41        // サービスのメソッドの呼び出し
42        if(!userService.existByUserName(result)) {
43            throw createValidationException(context)
44                .messageFormat("%s dose not found user name.", result)
45                .rejectedValue(result)
46                .build();
47        }
48
49        return next.execute(value, context);
50    }
51
52}

アノテーションの作成法補は、通常と変わりません。

メタアノテーション @CsvConstraint で ConstraintProcessorFactoryを実装したクラスUserNameExistFactoryを指定します。

 1import java.lang.annotation.Documented;
 2import java.lang.annotation.ElementType;
 3import java.lang.annotation.Repeatable;
 4import java.lang.annotation.Retention;
 5import java.lang.annotation.RetentionPolicy;
 6import java.lang.annotation.Target;
 7
 8/**
 9 * ユーザが存在するかチェックするためのアノテーション
10 */
11@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
12@Retention(RetentionPolicy.RUNTIME)
13@Documented
14@Repeatable(CsvUserNameExist.List.class)
15@CsvConstraint(value=UserNameExistFactory.class)
16public @interface CsvUserNameExist {
17
18    String message() default "{sample.CsvUserNameExist.message}";
19
20    Class<?>[] groups() default {};
21
22    int order() default 0;
23
24    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
25    @Retention(RetentionPolicy.RUNTIME)
26    @Documented
27    @interface List {
28
29        CsvUserNameExist[] value();
30    }
31
32}

12.2.3.3. 独自のProcessorBuilderクラスの作成

独自のクラスタイプのProcessorBuilderは、自身をSpringBeanとして登録します。 通常は、シングルトンでかまいません。

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.stereotype.Component;
 3
 4import com.github.mygreen.supercsv.builder.AbstractProcessorBuilder;
 5import com.github.mygreen.supercsv.builder.Configuration;
 6import com.github.mygreen.supercsv.builder.FieldAccessor;
 7import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
 8
 9@Component
10public class SampleTypeProcessorBuilder extends AbstractProcessorBuilder<SampleType> {
11
12    @Autowired
13    private SampleTypeFormatter formatter;
14
15    @Autowired
16    private SampleConstraintFactory sampleConstaintFactory;
17
18    @Override
19    protected void init() {
20        super.init();
21
22        // 制約や変換用のアノテーションの登録
23        // @CsvConstaint/@CsvCoversionの関連付けを省略し手いる場合に登録する
24        registerForConstraint(CsvSampleConstraint.class, sampleConstaintFactory);
25    }
26
27    @Override
28    protected TextFormatter<SampleType> getDefaultFormatter(final FieldAccessor field, final Configuration config) {
29        return formatter;
30    }
31
32}

12.2.3.4. CsvValidator の作成

CsvValidator クラスは、シングルトンで管理します。

SpringBeanをインジェクションしたいものがあれば行います。

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.stereotype.Component;
 3
 4import com.github.mygreen.supercsv.validation.CsvBindingErrors;
 5import com.github.mygreen.supercsv.validation.CsvValidator;
 6import com.github.mygreen.supercsv.validation.ValidationContext;
 7
 8/**
 9 * {@link UserCsv}に対するValidator
10 *
11 */
12@Component
13public class UserValidator implements CsvValidator<UserCsv> {
14
15    // SpringBeanのインジェクション
16    @Autowired
17    private UserService userService;
18
19    @Override
20    public void validate(final UserCsv record, final CsvBindingErrors bindingErrors,
21            final ValidationContext<UserCsv> validationContext) {
22
23        // 業務ロジックなので省略
24
25    }
26
27}

12.2.3.5. リスナクラスの作成

リスナクラスは、POJOであるため、SpringBeanをインジェクションしたいものがあれば行います。

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.stereotype.Component;
 3
 4import com.github.mygreen.supercsv.annotation.CsvPostRead;
 5import com.github.mygreen.supercsv.annotation.CsvPreWrite;
 6
 7/**
 8 * {@link UserCsv}に対するリスナクラス
 9 *
10 */
11@Component
12public class UserListener {
13
14    @Autowired
15    private UserService userSerivce;
16
17    @CsvPreWrite
18    @CsvPostRead
19    public void validate(final UserCsv record) {
20
21        // 業務ロジックなので省略
22
23    }
24
25}

12.2.4. CsvBeanの定義

CSVのBeanの定義では、SpringBeanとして定義したクラスを指定します。

 1import com.github.mygreen.supercsv.annotation.CsvBean;
 2import com.github.mygreen.supercsv.annotation.CsvColumn;
 3import com.github.mygreen.supercsv.annotation.constraint.CsvRequire;
 4import com.github.mygreen.supercsv.annotation.format.CsvFormat;
 5import com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator;
 6
 7@CsvBean(header=true,
 8    validator=UserValidator.class,   // Spring管理のValidatorの指定
 9    listener=UserListener.class      // Spring管理のリスナクラスの指定
10)
11public class UserCsv {
12
13    // Srping管理のFormatterを指定する場合。
14    @CsvColumn(number=1, label="タイプ")
15    @CsvFormat(formatter=SampleTypeFormatter.class)
16    private SampleType sampleType1;
17
18    // Spring管理のConstraintProcessorFactory を利用している検証用アノテーション
19    @CsvColumn(number=2, label="名前")
20    @CsvUserNameExist
21    private String name;
22
23    // Spring管理のProcessorBuilderを指定する場合
24    @CsvColumn(number=3, label="ホームページ", builder=SampleTypeProcessorBuilder.class)
25    private SampleType sampleType2;
26
27    // setter/getterは省略
28
29}

注釈

SpringBeanの管理外のクラスを指定した場合は、通常の方法としてインスタンスが作成されます。

ただし、管理外のクラスでもインジェクション用のアノテーション(@Resource/@Autowired)があれば、 インジェクションされます。

12.2.5. SpringBeanFactoryの使用方法

BeanMappingFactory#getConfiguration() 取得できる、システム設定に、SpringBeanFactoryを設定します。

 1import com.github.mygreen.supercsv.builder.BeanMapping;
 2import com.github.mygreen.supercsv.builder.BeanMappingFactory;
 3import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
 4
 5import java.nio.charset.Charset;
 6import java.nio.file.Files;
 7import java.io.File;
 8
 9import org.supercsv.prefs.CsvPreference;
10
11@Service
12public class CsvService {
13
14    @Autowired
15    private SpringBeanFactory beanFactory;
16
17    public void sampleSpring() {
18
19        // BeanMappingの作成 - SpringBeanFactoryを設定する
20        BeanMappingFactory beanMappingFactory = new BeanMappingFactory();
21        beanMappingFactory.getConfiguration().setBeanFactory(beanFactory);
22
23        // BeanMappingの作成
24        BeanMapping<UserCsv> beanMapping = mappingFactory.create(UserCsv.class);
25
26        CsvAnnotationBeanReader<UserCsv> csvReader = new CsvAnnotationBeanReader<>(
27                beanMapping,
28                Files.newBufferedReader(new File("user.csv").toPath(), Charset.forName("Windows-31j")),
29                CsvPreference.STANDARD_PREFERENCE);
30
31        //... 以下省略
32    }
33
34}

12.3. エラーメッセージの設定方法

SpringFrameworkの MessageSource を利用する方法を説明します。

12.3.1. XMLによるコンテナの設定

XMLによる設定方法を説明します。

コンテナの定義の基本は次のようになります。

  • MessageSource として、本ライブラリのエラーメッセージ com.github.mygreen.supercsv.localization.SuperCsvMessages を読み込んでおきます。

    • 独自のエラーメッセージがあれば、追加で定義します。

  • com.github.mygreen.supercsv.localization.SpringMessageResolver に、MessageSource を渡します。

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!-- XMLによるコンテナの定義 -->
 3<beans xmlns="http://www.springframework.org/schema/beans"
 4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5    xmlns:context="http://www.springframework.org/schema/context"
 6    xsi:schemaLocation="http://www.springframework.org/schema/beans
 7        http://www.springframework.org/schema/beans/spring-beans.xsd
 8        http://www.springframework.org/schema/context
 9        http://www.springframework.org/schema/context/spring-context.xsd
10    ">
11
12    <!-- Spring標準のメッセージソースの定義 -->
13    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
14        <property name="basenames">
15            <list>
16                <value>com.github.mygreen.supercsv.localization.SuperCsvMessages</value>
17                <value>MyMessages</value>
18            </list>
19        </property>
20    </bean>
21
22    <!-- 本ライブラリのSpring用のMessgeResolverの定義 -->
23    <bean id="springMessageResolver" class="com.github.mygreen.supercsv.localization.SpringMessageResolver">
24        <property name="messageSource" ref="messageSource" />
25    </bean>
26
27</beans>

12.3.2. JavaConfigによるコンテナの設定

Spring Framework3.0から追加された、JavaソースによるSpringBean定義の方法を説明します。

JavaConfigによる設定を使用する場合は、Spring Frameworkのバージョンをできるだけ最新のものを使用してください。 特に、機能が豊富なバージョン4.0以上の使用を推奨します。

 1import org.springframework.context.MessageSource;
 2import org.springframework.context.annotation.Bean;
 3import org.springframework.context.annotation.ComponentScan;
 4import org.springframework.context.annotation.Configuration;
 5import org.springframework.context.annotation.Description;
 6import org.springframework.context.support.ResourceBundleMessageSource;
 7
 8import com.github.mygreen.supercsv.localization.SpringMessageResolver;
 9
10
11// Javaによるコンテナの定義
12@Configuration
13@ComponentScan(basePackages="sample.spring")
14public class SuperCsvConfig {
15
16    @Bean
17    @Description("Spring標準のメッセージソースの定義")
18    public MessageSource messageSource() {
19        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
20        messageSource.addBasenames(
21                "com.github.mygreen.supercsv.localization.SuperCsvMessages",
22                "MyMessages");
23        return messageSource;
24    }
25
26    @Bean
27    @Description("本ライブラリのSpring用のMessgeResolverの定義")
28    public SpringMessageResolver springMessageResolver() {
29        return new SpringMessageResolver(messageSource());
30    }
31
32}

12.3.3. SpringMessageResolverの使用方法

CsvExceptionConverter#setMessageResolver(...) に、SpringBeanとして定義した SpringMessageResolver を設定します。

さらに、 CsvAnnotationBeanReader#setExceptionConverter(...) に、作成した CsvExceptionConverter を渡します。

 1import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
 2import com.github.mygreen.supercsv.localization.SpringMessageResolver;
 3import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
 4
 5import java.nio.charset.Charset;
 6import java.nio.file.Files;
 7import java.io.File;
 8
 9import org.supercsv.prefs.CsvPreference;
10
11@Service
12public class CsvService {
13
14    @Autowired
15    private SpringMessageResolver messageResolver;
16
17    public void sampleSpring() {
18
19        CsvAnnotationBeanReader<UserCsv> csvReader = new CsvAnnotationBeanReader<>(
20                UserCsv.class,
21                Files.newBufferedReader(new File("user.csv").toPath(), Charset.forName("Windows-31j")),
22                CsvPreference.STANDARD_PREFERENCE);
23
24        // CsvExceptionConverterの作成 - SpringMessageResolverを設定する
25        CsvExceptionConverter exceptionConverter = new CsvExceptionConverter();
26        exceptionConverter.setMessageResolver(messageResolver);
27
28        // CsvExceptionConverterを設定する
29        svReader.setExceptionConverter(exceptionConverter);
30
31        //... 以下省略
32    }
33
34}