Java Springの逆引きメモ

Java Springの逆引きメモ

JavaのSpring frameworkのメモを書いていきます!
初心者の勉強ノートなので間違いがあるかもしれませんが、何かヒントになることがあれば幸いです。

javaのSpringのメモを書いていきます! コメントなどいただけると大変うれしいです。

Amebaでブログを始めよう!

このブログでは、JavaのSpringの記事をメインに書いていきます。

その目次です。



【一般的準備(Springとは無関係です)】

・TomcatのGETの文字化け対策

・windowsでTomcatを使用する場合の注意点


【Spring準備編】

・Springを使用するには?

・SpringでWEB作成するには?(基本的な作成例)

・Springとは? (機能一覧と概要)

・Springにおけるファイル構成



【Springのコア機能】

・DIの機能について

・DIの設定ファイルを書くには?

・DIの設定ファイルで外部ファイル(プロパティファイル)を参照するには?

 (↑環境変数やシステムプロパティを参照する方法も記載しています)

・DIの設定ファイル内から外部DIファイルを参照するには?

・プロパティファイルをPropertiesクラスのbeanにするには?

・MessageSourceを使いやすくするには?



【SpringMVC機能】
・SpringMVCの機能について

・Controller内でApplicationContextを取得するには?

・リクエストの文字コードを設定ファイルで制御するには?

・SpringMVCを使用してWEBを作るには? (基礎編)

・Validatorでパラメタの妥当性チェックをするには?

・SpringMVCを使用してWEBを作るには? (実践編) : (パラメタとオブジェクトのバインドの方法)

・パラメタをオブジェクトの日付型データと結びつけるには? (型のバインドの追加方法)

・tilesについて




【SpringModules機能】

・SpringModulesの機能について

・Strutsのように設定ファイルでパラメタの妥当性チェックをするには?

・日本語の妥当性チェックを追加するには? (独自のチェックの追加の仕方)



【SpringSecurity機能】
・SpringSecurityの機能について

・実際に認証と認可をWEBにつけるには? (基礎編:設定方法)

・DBのユーザ情報を使用して認証するには? (実践編:DBを使用する方法)

・ログイン後の画面にログインしたユーザを表示するには? (ログイン情報の表示方法)

・ロール(権限)によって画面のリンクの表示/非表示を制御するには?

・閲覧許可がある画面のリンクのみ表示するには?

・拡張して、独自の機能をつけるには?




【SpringJDBC機能】

・SpringJDBCの機能について

・SpringJDBCで作成したDaoとビジネスロジックを連携するには?

・DBからデータを取得するには?(SimpleJdbcTemplate のサンプル)

・DBからデータを取得するには?(NamedParameterJdbcTemplateのサンプル)

・NamedParameterJdbcTemplateをうまく使うには? (HashMap版)

・NamedParameterJdbcTemplateをうまく使うには?(POJO版)

・結果をMapの配列で受け取るには?

・DBにデータを登録するには?

・DBへのデータ登録を簡単にするには?

・実行したSQL文のログを出力するには?



【DIxAOP機能】

・DIxAOPの機能について

・Springに付属のAOPサンプルのログトレースを動かすには?

・ログを出力するには?(独自のAOP処理の追加の仕方)

・オブジェクトを読み取り専用にするには? (オブジェクトへの適用の仕方)



【Springトランザクション機能】

・Springのトランザクション機能について
・トランザクションのコードを書かないようにするには?(宣言的トランザクション)

・アノテーションによるトランザクション管理
・プログラムによるトランザクション管理(TransactionTemplate)

・プログラムによるトランザクション管理(PlatformTransactionManager)



【SpringBatch機能】
・SpringBatch機能について

・WEB上で定期的に何か処理をするには?

・実際に作成するものとは(Stepの概念について)

・Spring Batchを使えるようにするには? (準備編)

・Spring Batchを起動するには? (基本編)

・Stepを使って処理を書くには?

・複数のStepをひとまとまりで扱うには?(flow)

・条件分岐をさせるには?

・既に用意されている機能(クラス)を探すには?

・DBにデータを読み書きするには?

・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)

・バッチ処理を非同期で起動するには?

・ItemWriterの出力先をメールにするには?

・自作クラスをrestartできるようにするには?

・ItemReaderで読み込んだデータを妥当性チェックするには? (valang方式)



【Strutsとの連携】
Strutsと連携するには?(理論編)

Strutsと連携するには?(実践編)

連携方法を拡張して、より自由度が高い連携をするには?

検索画面を簡単に実現するには?




【語句の説明】

・AOPとは?

・Anthenticationオブジェクト(SpringSecurity)とは

・CommandLineJobRunnerとは?

・Executionとは? (SpringBatch用語)

・Springとは?

・tilesとは?

・XMLとは?

・システムプロパティとは?

・フィルタとは?(tomcat)


Springは世界中で使用されているフレームワークで、GoogleAppEngineや、VMForceなどでも使用可能になっていて、使い方が分からないと乗り遅れている感があります。

しかし、「じゃあ、Springって何!?」って調べ始めると、DI(依存性の注入)とか、IoC(制御の反転)とか出てきて、「?」が広がっていきます


ここでは、難しい説明は他の書籍やホームページに任せて、もっと簡単な説明をしてみたいと思います

説明は自分の解釈で書きますのでそこは注意をお願いしたいところですが、

導入の解釈としてはこれで十分だと思っていますグッド!(おそらく・・・)

使っていくうちになんとなくDIとか分かっていくものじゃないでしょうか。(たぶん)



【SpringのDIって何?】


ひと言で言うなら、


 「設定情報を外部定義化できる機能」


です。

外部定義というと、Windowsでいうとiniファイル、Javaなら.propertiesファイルなどを思い浮かべますが、

それらは固定値をコンパイルなしに変更できる機能です。

Springがすごいところは、


 「使用するクラスも外部定義化でき、変更できる」


ところです。

どういうことかは、この後、みていきます。




【Springの外部定義化】

通常、外部定義をする場合、次のような実装をする必要があります。

 ①外部定義ファイルから値を取り出す機能の実装

 ②取り出した値を、使用するクラスもしくはメソッドに渡す機能の実装


この程度ならたいしたこと無いでしょ!って思うかもしれませんが、設定が増えてくると

これだけでもかなりのコード量になり、おそろしく大変になります


Springでは上記のコードを実装する必要がなくなります。

その具体的な例を見ていきましょう!


まず、Springでは外部定義にXMLを使用します。

以下のような感じです。

例では、データをファイル出力することを考えます。



 <設定XMLの記述例>

 <bean id="fileWriter" class="com.file.MyFileWriter">
   <property name="extractor" ref="csvExtractor"/>
 </bean>


 <bean id="csvExtractor" class="com.file.MyCsvExtrator">

   <property name="delimiter" value=","/>
 </bean>



 beanは1つのクラスを表し、そのクラスをnewした実体を内部で持っています。

 id属性は、その実体に名前をつけたものと思ってもらえば大丈夫です。

 上記の場合、2つのクラスが登場していて、以下のようにしたのと同じことになります。


 MyFileWriter fileWriter = MyFileWriter();

 MyCsvExtrator csvExtractor = new MyCsvExtrator();


 //propertyタグでの設定は、setterを表す(文字列を設定)

 csvExtractor.setDelimiter(",");

 

 //propertyタグでの設定、setterを表す(クラス自体も設定できる)

 fileWriter.setExtractor(csvExtractor);


  ※CsvExtractorは、与えられた1行分のデータをカンマ区切り文字列に変換するクラスとしています。


 なんとなく分かるでしょうか?

 newして、setterで値を設定しているだけです。

 propertyタグはsetterを表していて、値を設定する場合はvalue属性を使用し、クラス(bean)を設定する場合は

 ref属性を使用します。


 これで、最初の課題であった①②の、値をファイルから取得すること、値をクラスに設定すること、の

 2つを実現できているのが分かるでしょうか?

 区切り文字も外部定義化できています。

  

  再掲:

  <bean id="csvExtractor" class="com.file.MyCsvExtrator">

    <property name="delimiter" value=","/> ←ここを" "に変えれば空白区切りになる!!
  </bean>


 XMLファイルを変更すれば、コンパイルなしに任意の区切り文字にできます!

 もちろん、MyFileWriter、CsvExtractorの実装は必要ですが、それはSpringを使用しなくても同じです。

 



【動作の変更】
お話は、まだまだ続きます!


上記の例では、値の設定だけでなく、クラスも設定しています。


 再掲: fileWriter.setExtractor(csvExtractor);

これを利用すると、動作の変更もできます。


 

 <設定XMLの記述例>

 <bean id="fileWriter" class="com.file.MyFileWriter">
   <property name="extractor" ref="fixedExtractor"/>
 </bean>


 <bean id="csvExtractor" class="com.file.MyCsvExtrator">

   <property name="delimiter" value=","/>
 </bean>


 <!-- 追記 -->

 <bean id="fixedExtractor" class="com.file.MyFixedExtrator">

 </bean>


 ※MyFixedExtratorは、与えられた1行分のデータを固定長で文字列に変換するクラスとしています


 
fixedExtractorを追記しました。

そして、fileWriterも書き変えています。


 再掲:

 <bean id="fileWriter" class="com.file.MyFileWriter">
   <property name="extractor" ref="fixedExtractor"/> ←設定するクラスを変更しました
 </bean>


 これだけで、カンマ区切りファイル出力から、固定長によるファイル出力に変わりました


 通常の設定ファイルでも動作を変えることはできますが、相当コードを記述しなければなりません。

 しかしSpringでは設定ファイルのためのコード記述は必要なく、ref属性を変更するだけです!


 簡単ですよね!


この例でなんとなく分かったかと思いますが、Springを使うと、

 

   「必要な機能の実装のみに専念できる!」


ということになります。

今まで煩わされた外部定義化の実装に工数をかける必要がなくなります!




【設定したクラス(bean)を実際に使用する】

ちょっとあとまわしになりましたが、上記で設定した内容をプログラム上で使用する方法に軽く触れます。


 <使用方法例>

 //XMLファイルのロード(他にもロードするクラスはあります。状況に合わせて使用するクラスを決めます)

 ApplicationContext ac = new FileSystemXmlApplicationContext("c:/app/context.xml");

 

 //クラスの取得

 MyFileWriter fileWriter = (MyFileWriter)ac.getBean("fileWriter");

 

 //ファイル出力

 fileWriter.output("c:/app/out.txt");


 

簡単ですよね!

ファイルをロードして、getBean()で指定のbeanを取得するだけです!


さて、さきほどの内容とあわせると、とってもいいことがあります。

それがここでのポイントですビックリマーク


 さきほど、MyFixedExtratorを使用するように変更しました。

 この影響はあるでしょうか?

 上記のプログラムをみると、関係ありそうなところは、ac.getBean("fileWriter");だけです。

 しかも、bean名「fileWriter」は変更しないので影響が一切ありません!!


 

これがポイントです。

Springを適切に使用すれば、拡張も変更も既存のコードを変更することなく容易にできるようになります。

また、変更が容易なため、テスト用のモックに切り替えることも簡単になり、テストの効率が上がります!


Spring導入部の説明は以上です。

ここでの説明は話を簡単にするため、省略している便利な機能がいくつもあります。

あとはどんどん使って実践あるのみです!




【補足】

Springの設定ファイルは、jarファイルの中に含めることがほとんどです。

その場合、jarの中のファイルを変更することは大変です。

でも大丈夫!

当然、Springはそのような場合でも対応できます。

以下を参照してみてください!

 

 

 ・DIの設定ファイルで外部ファイル(プロパティファイル)を参照するには?

 ・DIの設定ファイル内から外部DIファイルを参照するには?




参考:

・トップ

・DIの設定ファイルを書くには?

・DIの設定ファイルで外部ファイル(プロパティファイル)を参照するには?

・DIの設定ファイル内から外部DIファイルを参照するには?

・プロパティファイルをPropertiesクラスのbeanにするには?

・MessageSourceを使いやすくするには?

・SpringJDBCの機能について

・DIxAOPの機能について

Strutsと連携するには?(理論編)

Spring IDEを使ってみよう

Spring Framework について








SpringではMessageSourceも用意されており、設定ファイルに設定すれば使用できます。

しかし、そのままでは少し使いにくいので使いやすくする方法を見てみましょう!



【Springの設定ファイルのサンプル(applicationContext.xml)】


<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>


<bean id="sample" class="sample.Sample">
<property name="messageSource" ref="messageSource"/>
</bean>



【Sampleクラス(sample.Sample)】


public class Sample{



public void setMessageSource(MessageSource messages) {
this.messages = new MessageSourceAccessor(messages);
}


public String obtainMsg(){

return this.messages.getMessage("sample.msg", "メッセージのデフォルトです");
}


}



【メッセージファイル(messages_jp.properties)】


sample.msg=サンプルメッセージ




【説明】


特に説明の必要は無いかもしれません。


this.messages = new MessageSourceAccessor(messages);

単純にMessageSourceAccessorでラップしているだけです。

この便利さは、もとのMessageSourceの使い方を見ないと分からないかと思います。


 <MessageSourceの使い方>

 getMessage(sample.msg", null, "メッセージのデフォルトです", Locale.getDefault());


 <MessageSourceAccessorの使い方(再掲)>

 getMessage("sample.msg", "メッセージのデフォルトです");


いくつか引数を入力しなくて良くなっていますよね!

ほんの少しの違いですが、何回も書くと意外と疲れます。

このクラスもうまく使ってみてくださいニコニコ




【おまけ】

SpringにはVMを再起動しなくてもリロードできるメッセージリソースもあります。

 ReloadableResourceBundleMessageSource


また、テスト用にプログラム上で書き換えられるメッセージリソースもあります。

 StaticMessageSource


わざわざ自作するようなことはやめて、まずはこれらが使えないか検討してみましょう!




参考:

・トップ

・DIの設定ファイルで外部ファイル(プロパティファイル)を参照するには?

・DIの設定ファイル内から外部DIファイルを参照するには?

・プロパティファイルをPropertiesクラスのbeanにするには?




以前の記事でおおよそのバッチ処理のステップの設定を見てきました。

ここではItemReaderで読み込んだデータについて、妥当性チェックをする方法を見ていきます。



【予備知識】

最初にいくつか触れておきたいことがあります。

SpringBatchには妥当性チェックをするためのValidatorの準備がされています。

しかし、用意されているのはinterfaceだけで、チェックの実装はされていません。

チェックの実装については、以下のような解決方法があります。


 ①自分でValidatorをimplementsして作成する。

 ②SpringModulesを利用する。


①はあまりやりたくないですよね?汗

そんなわけ②をみていきます。


ちなみにSpringModulesは、他の記事で紹介していますようにSpringの妥当性チェックのためのフレームワークです。

 参考: ・SpringModulesの機能について


Strutsの妥当性チェックと同じApacheのライブラリを裏で使用しています。

Bean(setter/getterがあるクラス)をチェックの対象にしているので、Mapをチェック対象にはできないようです。


それと、Spring自体にもValidatorという同じ名前のinterfaceが用意されていますが、

SpringBatchのValidatorとは違う物であることに注意してください


 ただし、ラッパーが用意されていますのでSpringのValidator由来のクラスもSpringBatchでも使用できます。



 <SpringModulesで用意されている妥当性チェックの機能>

  主に以下の機能が用意されています。

機能 内容
valang

SpringModulesが独自で作成したチェック用言語。

Springの設定ファイルにEL式のような感じでチェック内容を記述する。

common validator Strutsと同じように、validator-rules.xmlとvalidator.xmlファイルを用意して、validator.xmlにチェック内容を記述していきます。

アノテーション

アノテーションを利用する方法も用意されているようです。

まだ、調べていませんので分かりましたら追記するかもしれません。



  以下では、valangを使用してみようと思います。

  valangで使用できる記号等は以下を参照してみてください。

    http://www.springbyexample.org/examples/spring-modules-validation-module.html
 


 valangを使用する上での注意点:

  tomcatのライブラリである、servlet-api.jar をビルド・パスに追加しておいてください。

  何故かvalangのモジュールがServletContextをimportしているため、クラスが見つからないエラーが出ます。

  (将来は直るかもしれませんが)

  tomcatのインストールディレクトリ内を探せばすぐ見つかります!



【サンプルの動作内容】

これから見るサンプルでは、以下の動作をするものを作ります。

 1.ファイルからデータの読み込み (ファイルはパッケージ配下に置きます)

 2.Accountクラス(Bean)に読み込んだ値をマップします。

 3.Accountクラスを妥当性チェックします。

 4.Accountクラスの内容をファイルに書き込みます。



それでは早速いってみましょービックリマーク




【/sample/test.txt ファイル (データファイル。sampleパッケージ内に置く)】

2010/08/21,太郎,18
2007/11/01,次郎,29
2009/03/12,さんま,53
2010/07/07,タモリ,54
2010/12/24,鶴野,28




【/sample/back-context.xml ファイル(Batchバックグラウンドの設定)】

以下の記事の同じタイトルの箇所をコピーしてください。


 ・Stepを使って処理を書くには?


  ↑DBの設定などもお忘れなく。

【/sample/runValid-context.xml ファイル(Batch処理)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans " xmlns:p="http://www.springframework.org/schema/p " xmlns:aop ="http://www.springframework.org/schema/aop " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
<import resource="classpath:/sample/back-context.xml"/> <!-- ジョブの処理 --> <job id="jobValid" xmlns="http://www.springframework.org/schema/batch " incrementer="jobParametersIncrementer"> <step id="step1" parent="simpleStep" > <tasklet > <chunk reader="fileItemReader" processor="validatingItemProcessor" writer="fileItemWriter" commit-interval="2" /> </tasklet> </step> </job> <!-- enables the functionality of JobOperator.startNextInstance(jobName) --> <bean id="jobParametersIncrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer" /> <bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true"> <property name="jobRepository" ref="jobRepository" /> <property name="commitInterval" value="1" /> </bean> <!-- ファイルを読み込みBeanに入れる --> <bean id="fileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step"> <property name="resource" value="classpath:/sample/sample-db.txt" /> <property name="encoding" value="shift_jis" /> <property name="lineMapper"> <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"> <property name="lineTokenizer"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"> <property name="names" value="id,name,date" /> </bean> </property> <property name="fieldSetMapper"> <bean class="sample.AccountFieldSetMapper" /> </property> </bean> </property> </bean> <!-- 読み込んだ結果をファイルに出力していく --> <bean id="fileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"> <property name="resource" value="file:c:/temp/spring_batch-test.txt" /> <property name="lineAggregator"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator"> <property name="fieldExtractor"> <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor" > <property name="names" value="id,name,date" /> </bean> </property> </bean> </property> </bean> <!-- 妥当性チェックプロセッサ --> <bean id="validatingItemProcessor" class="org.springframework.batch.item.validator.ValidatingItemProcessor"> <property name="validator" ref="validator" /> </bean> <!-- SpringModules用からSpringBatch用へ変換するためのラッパー。 --> <bean id="validator" class="org.springframework.batch.item.validator.SpringValidator"> <property name="validator" ref="valangValidator" /> </bean> <!-- 妥当性チェックするクラス --> <bean id="valangValidator" class="org.springmodules.validation.valang.ValangValidator"> <property name="valang"> <value> <![CDATA[ { id : ? >= 0 AND ? <= 5 : 'IDは0~5です。' : 'errors.range': 'ID',0,5 } { name : size(?) <= 5 : '名前は5文字以下です。' : 'errors.maxlength': '名前',5 } { date : ? IS NOT NULL AND ? > [2010-05-05] : '日付は5/5以降を入れてください。' : 'errors.date': '日付','5/5'} ]]> </value> </property> </bean> </beans> 【Accountクラス(Beanクラス。sampleパッケージ内に置く)】 package sample; import java.util.Date; public class Account { private int id; private String name; private Date date; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } } 【AccountFieldSetMapper クラス(Accountに読み込みデータを設定するクラス)】 package sample; import org.springframework.batch.item.file.mapping.FieldSetMapper; import org.springframework.batch.item.file.transform.FieldSet; import org.springframework.validation.BindException; public class AccountFieldSetMapper implements FieldSetMapper<Account> { @Override public Account mapFieldSet(FieldSet fs) throws BindException { Account a = new Account(); a.setId(fs.readInt("id", -1)); a.setName(fs.readString("name")); a.setDate(fs.readDate("date", "yyyy/MM/dd", null)); return a; } }





【起動方法】

起動方法は、以下のとおりです。

 メインクラス    : org.springframework.batch.core.launch.support.CommandLineJobRunner

 プログラムの引数: classpath:/sample/runValid-context.xml jobValid -next


 ※詳しいやり方は、・Spring Batchを起動するには? (基本編) の真ん中当たりの【実行】を参照。


 ※-nextは何回でもサンプルを実行できるようにつけたオプションです。

  詳しくは他の記事を読んでみてください。



【わざとエラーを出す】

サンプルのテキストファイルを以下のようにして実行してみてください。

エラーが出るはずです!


<修正後のファイル>

1,太郎,2010/05/03
2,次郎,2010/07/07
3,さんま,2010/08/08
4,タモリ,2010/09/09
5,鶴野,2010/10/10



<実行結果(エラーログの内容)>

org.springframework.batch.item.validator.ValidationException: Validation failed for sample.Account@15d4de6 :
Field error in object 'item' on field 'date': rejected value [Mon May 03 00:00:00 JST 2010];

codes [errors.date.item.date,errors.date.date,errors.date.java.util.Date,errors.date];

arguments [日付,5/5];

default message [日付は5/5以降を入れてください。]




【説明】

妥当性チェック自体は、バッチフレームワークのプロセッサで行います。

validatingItemProcessorですね。

ItemReaderでは、データを読み込み、sample.AccountFieldSetMapperがAccountオブジェクトに値を設定しています。


AccountFieldSetMapperクラス>

  マッパークラスです。

  FieldSetから値を受け取り、設定するだけです。


   再掲(一部抜粋):

     Account a = new Account();
     a.setId(fs.readInt("id", -1));


  readXxxx()の1番目の引数はフィールド名で、2番目の引数は値が存在しないときに返却される値です。

  つまり、デフォルト値です。

  もし、idの値が数値でない場合は例外が発生します。



validatingItemProcessorクラス>

  プロセッサのクラスですので、ItemReaderでAccountが作られた後に呼ばれます。

  このクラスはSpringの設定ファイルでDIしたvalidatorを使用して妥当性チェックを実行するだけです。


  では、validatorを見ていきましょう。


  validatorには、SpringValidatorクラスが設定されています。

  このクラスが冒頭で述べたラッパークラスで、valangValidatorをSpringBatchで

  使用できるようにしているクラスです。

  は、SpringModulesのクラスですので、そのままではvalidatingItemProcessorに設定できません。



valangValidator

  次に見ていくのは以下のBeanです。

  validatingItemProcessor内部で実際にチェックをしているのは以下の設定です。


 再掲:

 <bean id="valangValidator"
  class="org.springmodules.validation.valang.ValangValidator">
  <property name="valang">
    <value>
  <![CDATA[
 { id : ? >= 0 AND ? <= 5 : 'IDは0~5です。' : 'errors.range': 'ID',0,5 }
 { name : size(?) <= 5 : '名前は5文字以下です。' : 'errors.maxlength': '名前',5 }
 { date : ? IS NOT NULL AND ? > [2010-05-05] : '日付は5/5以降を入れてください。' : 'errors.date': '日付','5/5'}

  ]]>
   </value>
  </property>
 </bean>

  赤字の部分がvalang式になります。

  コロン(:)で区切られています。

  形式は、以下のような感じです。


  {フィールド名 : valag式 : デフォルトメッセージ : エラーコード : エラー引数}

     

     ※フィールド名について

     idのような指定だけでなくネストしたaaa.bbbのような指定も可能です。

     また、aaaの結果がMapなら、aaa[id]のような指定も可能です。


     ※エラーコードは、メッセージリソースからエラーメッセージを探すときのキーです。

     ここではメッセージリソースを設定していませんのでデフォルトメッセージが表示されます。


  しかし、メッセージリソースを設定してもそのままではエラーメッセージを検索してくれないようです。

  もしどうしてもメッセージリソースを利用したい場合、SpringValidatorを拡張して、メッセージリソースから

  エラーメッセージを検索するようにすればよいでしょう。



  valang式はここでは特に説明しませんが、参考のURLを見ればすぐ分かるかと思います。


  

【他の妥当性チェックの方法への変更】

 他の方法、例えばStrutsに慣れた方ならCommon Validateを使用するほうが使いやすいかもしれません。

 そうすると、変更をかけたくなります。

 変更は簡単です!

 

 SpringのラッパークラスにDIするbeanを変更するだけです。

 再掲:

  <!-- SpringModules用からSpringBatch用へ変換するためのラッパー。 -->
  <bean id="validator" class="org.springframework.batch.item.validator.SpringValidator">
    <property name="validator" ref="valangValidator" /> ←refの値を変更します。
  </bean>

 もちろん、必要なvalidate-rule.xml, validate.xmlなどのファイルの用意と

 validatorFactoryなどのbeanの設定もお忘れなく!

 以下を参考にしてもらえればいいかと思います。

   ・SpringModulesの機能について



さて、おおよそ使い方は分かりましたでしょうか?

valang式の記法は覚えないといけませんが全然難しくないですよねにひひ


うまく使って、コード量を減らしていきましょう!




参照:

・トップ

・SpringBatch機能について

・実際に作成するものとは(Stepの概念について)

・CommandLineJobRunnerとは?

・Executionとは? (SpringBatch用語)

・WEB上で定期的に何か処理をするには?

・Spring Batchを起動するには? (基本編)

・Stepを使って処理を書くには?

・複数のStepをひとまとまりで扱うには?(flow)

・条件分岐をさせるには?

・DBにデータを読み書きするには?

・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)

・バッチ処理を非同期で起動するには?

・ItemWriterの出力先をメールにするには?

・自作クラスをrestartできるようにするには?

SpringModulesの外部記事

SpringBatchで用意しているItemReader、ItemWriterについては、既にrestartできるように設計されています。

自作で作った場合は自分で設計しなければなりません。

restartできるように設計するときに、どのように考えればよいか?

また、実際にどのようにプログラムすればよいかを簡単に見ていきます。


 ※注意: Springが用意しているクラスの中にはテストや検証を簡単にするものがあり、

  これらはrestart設計されてません。


【restart可能にするための基本的考え方】

restartするということは、前回どこか途中で終了しています。

基本的にはどこまで読み込んだか?どこまで書き込んだか?を保存しておく必要があります。

そして、Stepを開始したときに保存した値を読み込んで、途中で終わっていればそこまでの処理を実行する必要があります。


例えば、前回ItemWriterが5回目で失敗した場合、ItemReaderで4回目までデータを読み飛ばしてStep処理を始めれば、後はJobに任せれば5個目のデータを読み込んでItemProcessor、ItemWriterにデータを渡すのでOKということにりますね!


そうすると知っておくべきポイントが以下のようになるかと思います。

 ・最後のデータの位置を保存する方法

 ・Stepが開始したときにデータを読み飛ばす方法


では上記をこれから見ていくことにしましょう!



【最後のデータの位置を保存する方法】

まず、ExcecutionContextについて知っておきましょう!

簡単に言うと、データを保存しておくMapです。

StepExecutionとJobExecutionそれぞれに別々のExcecutionContextが保持されています。

Stepについては、Stepごとに別々のインスタンスで保持されています。

詳しい説明は、・Executionとは? (SpringBatch用語) を参照ください。


まあ、ほぼこれが答えです(笑)


 例: executionContext.putInt("sendMailCnt", this.sendedCount);


こんな風にすれば値が保持されます。

そして、設定した値がDBに保存されることになります。


保存についてはもうひとつ重要なポイントがあります。

ItemWriterがコミットしたときに保存しなければなりません。

なぜならもし、ItemReaderがデータを読み込んだときに保存すれば、その後ロールバックしたら数がずれちゃいますよね?

例えば、コミット間隔が5個で、10個目のデータまで読み込み、ItemWriterで8個目のデータ書き込みでロールバックして終了した場合。

コミットされているのは5個目までなので、restart時は6個目からItemReaderが読み込み始めないといけません。

もし10個目のデータを読み込んだ段階で10という値を保存してしまえば11個目から読み込み始めてしまいますよね。


そこで、コミットがあったときに読み込んだ個数を保存するようにします


方法: 

まず、ItemReaderにItemStreamをimplementsします。 

ItemStreamはStepをopen/close/commitするときに呼び出されるメソッドを用意しています。

implementsしたItemReader、ItemWriterなどは自動でそれらのメソッドが呼ばれます。


例: commitしたときにread数を保存する


public void update(ExecutionContext executionContext)
throws ItemStreamException

{

  executionContext.putInt("sendMailCnt", this.sendedCount);

}


上記のような感じになります。




【Stepが開始したときにデータを読み飛ばす方法】

さてどこまで読み込んだかを保存できたところで次に、restartしたときにデータを読み飛ばす方法を見てみます。

実はこの答えの半分は、ItemStreamにあります。

ItemStreamのopenメソッドに読み飛ばしの処理を実装すればOKです。

例えば以下のような具合です。


例:

public void open(ExecutionContext executionContext) throws ItemStreamException

{

  int itemCount = executionContext.getInt("sendMailCnt", 0);

  this.sendedCount = itemCount;

  for (int i = 0; i < itemCount; ++i)

   readData(); //データ1個を読むだけのメソッドを作っておく

}




これでなんとなくやり方わかりましたでしょうか?

open/updateはSpringBatchの方で自動で呼んでくれるので、呼び出すための処理は記述する必要はありませんビックリマーク

ItemWriterにもそのときそのときの状態を持たなければrestartできない場合は、ItemReaderと同様にItemStreamをimplementsして同様のことをしてください。


それ程難しくないですよね。


せっかくなので自作のクラスについてもrestart可能にしましょう!


もしそれがかなわない場合は、設定ファイルのjobタグの属性にrestartable="false" を追加しておきましょう!





参照:

・トップ
・SpringBatch機能について

・WEB上で定期的に何か処理をするには?

・実際に作成するものとは(Stepの概念について)

・Spring Batchを使えるようにするには? (準備編)

・Spring Batchを起動するには? (基本編)

・Stepを使って処理を書くには?

・複数のStepをひとまとまりで扱うには?(flow)

・条件分岐をさせるには?

・既に用意されている機能(クラス)を探すには?

・DBにデータを読み書きするには?

・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)

・バッチ処理を非同期で起動するには?

・ItemWriterの出力先をメールにするには?

・CommandLineJobRunnerとは?

・Executionとは? (SpringBatch用語)