SpringBeanFactoryの設定方法

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>

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}

SpringBeanとしての定義

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

独自の書式の作成

独自の書式の 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}

独自の変換/検証の作成

検証用の 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}

独自の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}

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}

リスナクラスの作成

リスナクラスは、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}

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)があれば、 インジェクションされます。

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}