LombokでJavaの冗長なコードを排除する

Javaで開発しているとリクエストやレスポンスをマッピングするクラス(Value Object)とか
レイヤを跨ぐ場合に使うクラス(Data Transfer Object)など多用することがあるので、
アクセサー(getter/setter)を書くのが大変で、辛い。
Lombokを使うとかなり楽になると思うので、それについて、書きます。

環境に関して

今回の環境は、下記の環境で、検証しました。

Lombokのインストール

利用する為には、Eclipseにインストールする必要があります。
http://projectlombok.org/からlombok.jarをダウンロードしてきます。
ダウンロードしたJARファイルを起動します。

java -jar lombok.jar 

GUIが立ち上がってくると思います。
通常は、IDEsに利用しているEclipseのパスが表示されていると思うので、
そのままInstall/Updateで、インストールは完了します。

Apache Mavenの設定

POMファイルの依存関係を定義する部分にlombokを追加します。

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>${lombok.version}</version>
	<scope>provided</scope>
</dependency>

普通にValue Objectを書いた場合

まず、普通にValue Objectを書いた例です。
EclipseIDEのコード補完とか駆使すれば、ある程度、楽に書けるのですが、
冗長なコードになってしまうのと、Value ObjectとかDTOをいっぱい作るようなある程度の規模のアプリの場合、
大変だったりします(辛い)。

public class Message implements java.io.Serializable {
	private Integer id;
	private String message;
	public Message() {
		super();
	}
	public Message(Integer id, String message) {
		super();
		this.id = id;
		this.message = message;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((message == null) ? 0 : message.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Message other = (Message) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (message == null) {
			if (other.message != null)
				return false;
		} else if (!message.equals(other.message))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "Message [id=" + id + ", message=" + message + "]";
	}
}

Lombokを利用して書いた場合

Lombokは、その冗長なコードの部分をAOPで書くことができます。
これは、コンパイル時に、そのその冗長なコード部分を付加して、コンパイルされる仕組みになっています。
上のクラスと同じようなクラスを作る場合は、3つのAOPを使います。

@Data

下記の部分をコンパイル時に補完してくれます。

  • アクセサー(getter/setter)
  • hashCode()
  • equals()
  • toString()
@NoArgsConstructor

デフォルトコンストラクタコンパイル時に補完してくれます。

@AllArgsConstructor

メンバーを引数にとるコンストラクタコンパイル時に補完してくれます。|

上の3つのAOPを使って書いたクラスは、下の様なクラス定義になります。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message  implements java.io.Serializable {
	private Integer id;
	private String message;
}

また、setterを付けたくない場合は、@Dataでなく、個別にAOPを指定する必要があります。
その場合は、@EqualsAndHashCode、@ToStringをクラスに付け、メンバーに対して、@Getterを付与します。

@EqualsAndHashCode
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Message  implements java.io.Serializable {
	@Getter
	private Integer id;
	@Getter
	private String message;
}

Eclipseの補完機能より、かなり楽になるかも(個人的には、メソッドにコメントとか書かなくてもいいからwww)。
それ以外のAOPもまだあるので、いろいろ楽になるかもしれない。

SpringFrameworkのHandlerInterceptorで共通処理

Spring MVCでのリクエストに対してのアクセスコントロールや認証などの共通処理を
実装する方法は、HandlerInterceptorを利用すると実現可能です。
Servlet Filterでも可能なんですが...。
それで、このHandlerInterceptorは、インターフェイスで、それを実装する必要があります。
この方法を使うと、Controllerの前後で、処理を呼び出すことが可能になります。
HandlerInterceptorには、下記の3つのメソッドが定義されています。

preHandle

リクエスト情報をControllerに受け渡す前に呼ばれるメソッドです。
前処理を記述する場合は、このメソッド内に処理を書きます。

postHandle

ビューのレンダリング完了前に呼ばれるメソッドです。
サーバーサイドMVCで利用可能ですがSPAでは、
ビューのレンダリング等がないので、RestAPIだと呼ばれないようです。
ビューのレンダリング前に何らかの処理を入れたい場合は、 このメソッド内に処理を書きます。

afterCompletion

ビューのレンダリング完了後に呼ばれるメソッドです。
後処理を記述する場合は、このメソッド内に処理を書きます。
サーバーサイドMVC及び、SPAともに呼ばれます。
サーバーサイドMVCは、postHandleに後処理を書きますが、
SPAの場合は、このメソッド内に後処理を書きます。

実際のインターフェイスを実装したクラスを作成して、
必要なメソッド内に共通処理を記述すれば完了です。

public class SampleHandlerInterceptor implements HandlerInterceptor {
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 共通処理を記述する。
        return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
        // 共通処理を記述する。
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view) throws Exception {
        // 共通処理を記述する。
    }
}

HandlerInterceptorを実装したクラスを作成しただけでは、利用できません。
Webアプリケーションの設定クラスで、作成したHandlerInterceptorを登録します。
Webアプリケーションの設定クラスは、WebMvcConfigurationSupportを継承して、
@ConfigurationのAOPを付加する必要があります。

@Configuration
public class SampleWebApplicationConfiguration extends WebMvcConfigurationSupport {
    @Bean
    public SampleHandlerInterceptor sampleHandlerInterceptor() {
	return new SampleHandlerInterceptor();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(sampleHandlerInterceptor()).addPathPatterns("/**")
    }

最後に、ApplicationContext.xmlにWebアプリケーション設定クラスを設定すれば完了です。

<bean class="com.chronos.configuration.SampleWebApplicationConfiguration"/>

システムテスト自動化カファレンスへ参戦してきた

1時間で分かるSTA

Software Test Automationとは?

テスト自動化において考慮すべき事項が網羅的に扱っている。
テスト自動化の実例など。
ソフトウェアテストの自動化について一般的な知識を網羅的に扱う書籍がない?
翻訳された書籍がシステムテスト自動化標準ガイド

テスト自動化のコンテキスト

自動化以前にテストはまともなのか?

  • 効果的
  • 経済的
  • 発展的
  • 典型的
キャプチャーリプレイ
  • 準備がほとんどいらない。
  • 操作ログを残せる。
  • しかし、テスト自動化ではない。
スクリプティング技法
自動比較
  • 何を?どれだけ?比較するか?
  • センシティブな比較。
  • できるだけ多くの情報を比較。
  • ロバストな比較。
  • 費用最低限の情報に絞り比較。
  • 動的比較。
  • 実行後比較。
テストウェアアーキテクチャ
  • テスト自動化によって増殖するファイル(どんどんファイルが増殖)。
  • 共有スクリプトなど、効率的に再利用できる必要がある。
  • 管理なしだと破綻する。
  • 前処理と後処理の自動化。
  • 保守性の高いテストを構築する。
  • テストウェアのメンテナンス。
  • 最初は、短めで、徐々に長くする。
メトリックス

テスト自動化のパターンと実践

テスト自動化パターンについて

そもそも始めるのに勇気と力が必要。
最初は、テンションが高いが、徐々に低下してしまう。
テンションを維持するのが大変。

パタンランゲージ
  • コンテキスト(問題の背景)
  • 問題(実際の問題)
  • フォース(問題を発生させる要因となる外部からの力)
  • 解決(解決策)
  • 結果(レポートなど)

テスト結果を適切に分析し、活用することができていない。
多くの情報を解釈するには担当者のリソースが必要。
目的をもった結果レポートを出力するようにする。
メトリックスを測定する。
全員が理解できる共通言語(ユビキタス言語)で話す。
自分の経験を皆に伝えるツールとして。

パターン解説①
  • システムテスト自動化のボルトネックは、GUI操作。
  • システムテスト自動化って、つまりは、プロダクトプロセスとテストプロセスを協調動作させる。
  • マルチプロセスプログラミング。
  • ホワイトボックスでのテストをする。
  • 一番不安定で、自動化コストがかかる部分(GUI)を抜いて行く。
  • ユーザーロジックのテストだけで十分だったりする(バグのほとんどは、ここにあることが多い)。
  • 単体テストとは違い、結合状態でテストする。
  • 抜いた部分も管理しておく。
  • 自動化でテストしなくとも手動で、確認しておく。
パターン解説②
  • オートーメター
  • 自動家の道具(テストツール、CI&CDツールなど)
  • 自動化の価値
  • 合理性・効率性の向上。
  • 本来作業に近づける(別の仕事(ノイズ)が多い)。
  • 楽しい(問題を技術課題へと転化出来る。

GUI自動テストの保守性を高めるには

Seleniumとその保守性
  • キャプチャーリプレイ。
  • キャプチャーリプレイはテスト自動化ではない。
  • 手動画面操作をSelenium IDE/Selenium Builder(使いづらい)で作る。
  • 画面を操作するだけでスクリプト生成
  • 生成されたものが読みにくいものが出来てしまう。
  • スクリプト修正の作業が大変。
  • スクリプトの共通化ができない。
  • 記録した後、スクリプトの手直しがかなり必要
  • Selenium IDEは、記録機能よりも視覚的に見やすい。
  • 複雑なロジックを書くのは結構大変
  • 処理の共通化がかなり難しいく、だんだんプログラムで書いているのとかわらない。
プログラミング言語Selenium WebDriver)
  • 共通化の技法を駆使して、メンテナンスコストを減らせる。
  • EclipseなどのIDEのコード保管。
  • コードが書けないと厳しい。
  • テストフレームワークがある。
Excelから自動生成
テストはなぜ失敗するのか
  • テスト対象システムのバグ
  • 仕様変更
  • ツールバグ
  • それ以外

ほぼそれ以外の部分で失敗する。

  • 不安定テスト
  • 環境準備失敗
  • DB定義失敗
  • テストデータ不整合
失敗した原因を突き止める
  • テスト結果に誰でも簡単にアクセスできる。
  • メールやブラウザ上で見れると結果確認が簡単。
  • チーム全体で結果を共有し、テスト失敗への関心を高める。
  • 画面キャップチャ・動画・サーバーログなど、エラー調査に必要なデータを残す。
  • エラーになったテストだけを手元で流して確認できる
  • テストスクリプトはバージョン管理し、誰でも手元に取得できるように
  • 原因が分かったら修正(簡単に修正できる)。
スクリプトを共通化

状態遷移テストにおけるテスト設計と実行の自動化

デプロイメントパイプライン
  • 自動化っていっても、効果のあるものをやらないと意味がない。
  • まずは現状をみえるようにする必要がある。
  • システムテストは、コストと効果を考える。
Value Stream Map

テストを自動化することが正しいのではなく、ソフトフェア開発自体をよいサイクルにすること。
リリースにかかる時間を短くするための有用な選択肢。

Deployment pipeline

テスト自動化をする上で、ある程度は俯瞰しなれば、局所化してしまう。
ソフトウェアをバージョン管理から取り出してユーザーに届けるまで。
ビルドパイプラインを実装するというのは、テストの大まかな分割方針、フィードバック順序、
実行頻度を決め、そして実装後には多様なテストを早期から設計、実装、実行することを意味する。

ビルドプロセスとCI

テストウェア?

自動・手動の区別なく、テストに関わる成果物
テスト資料

テスト結果

  • 実際の出力
  • ログ
  • ステータス
  • 比較レポート
なにが問題なのか?
  • 規模(ファイルの数が多い)
  • 再利用性(建て増し旅館)
  • 複数のバージョン(製品のバージョンとの同期)
  • プラットフォームと環境からの独立(動作環境ごとにテストウェアのコピーを作るのか)
ツールで解決できるもの
  • CIツールで解決(CIの結果、レポート機能など)⇒ 規模
  • テスト資料は、バージョン管理システムで管理してしまう ⇒ 複数のバージョン
  • テストコード、テストデータ(SQL)、テーブル定義、比較用の出力データ(期待結果)
  • Git-flowなど、ブランチやタグなどの規制しておく。
前処理と後処理の自動化
  • 前処理(生成(DBへのデータ投入)、チェック、再配置、変換)
  • 後処理(削除、チェック、再配置、変換)
  • 後処理の削除、再配置は、前処理でやった方がいい。
  • ビルドツール(Make Rake Ant Gradleなど)

Gradleをはじめてみた

Apache Mavenに不満があるかと言うとないのだけど、
Gradleを触ってみることにしました。
それで、Gradleで、Springを使ったRestを試しに作ってみました。
まずは、build.gradleの作成から、
GradleもMavneリポジトリから依存関係を定義したものをダウンロードして来てくれます。
ただし、Mavenとは、依存関係の解釈が違うぽいです。
作ったbuild.gradleは、こんな感じです。

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'war'
sourceCompatibility = 1.8
version = '1.0'
repositories {
    mavenCentral()
}
dependencies {
	// Unit Test Library
	testCompile 'org.mockito:mockito-all:1.9.5'
	testCompile 'org.powermock:powermock-module-junit4:1.5.5'
	testCompile 'org.powermock:powermock-api-mockito:1.5.5'
	testCompile 'com.jayway.jsonpath:json-path:0.9.1'
	testCompile 'junit:junit:4.11'
	testCompile 'org.springframework:spring-test:4.1.2.RELEASE'
	// Web Application Library
	compile 'org.aspectj:aspectjweaver:1.7.3'
	compile 'org.springframework:spring-core:4.1.2.RELEASE'
	compile 'org.springframework:spring-context:4.1.2.RELEASE'
	compile 'org.springframework:spring-context-support:4.1.2.RELEASE'
	compile 'org.springframework:spring-jdbc:4.1.2.RELEASE'
	compile 'org.springframework:spring-tx:4.1.2.RELEASE'
	compile 'org.springframework:spring-web:4.1.2.RELEASE'
	compile 'org.springframework:spring-webmvc:4.1.2.RELEASE'
	compile 'commons-logging:commons-logging:1.1.3'
	compile 'commons-lang:commons-lang:20030203.000129'
	compile 'commons-collections:commons-collections:3.2'
	compile 'org.slf4j:slf4j-api:1.7.5'
	compile 'ch.qos.logback:logback-classic:1.1.2'
	compile 'com.fasterxml.jackson.core:jackson-core:2.4.0'
	compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0'
	compile 'com.fasterxml.jackson.core:jackson-annotations:2.4.0'
	// Web Application Provider Library
	providedCompile 'org.eclipse.jetty:jetty-webapp:9.2.5.v20141112'
}

XMLよりこちらの方が抵抗がなく書けるかもしれません。
Groovyで書けるので、楽かもしれませんが....Groovy自体、そんなに詳しくないので、
これを機にまじめに勉強するかもしれません。
次にRestを作ります。
今回は、単純にメッセージを戻すだけのものにします。
作成したものは、下の3つです。

JSONマッピングクラス
public class HelloMessage implements java.io.Serializable {
	private String id;
	private String message;
	public HelloMessage() {
		super();
	}
	public HelloMessage(String id, String message) {
		super();
		this.id = id;
		this.message = message;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
}
Restサービスインターフェイス
public interface HelloMessageService {
	HelloMessage hello(String messageId) throws Exception;
}
Restサービスインターフェイス実装クラス
@RequestMapping(value="/message")
@RestController
@Service("helloMessageService")
@Scope("request")
public class HelloMessageServiceImpl implements HelloMessageService {
	@RequestMapping(value = "/hello/{messageId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	@Override
	public HelloMessage hello(@PathVariable String messageId) throws Exception {
		return new HelloMessage("hello", "Hello World");
	}
}

とりあえず、Eclipseで見る限り、ビルドは、通ってるようです。
Gradleでもビルドしたら正常にビルド出来ました。

gradle clean build
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:war
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 9.581 secs

それで、ここから問題で、Jettyで動かしたいのだが、デフォルトのJetty plugin使ったら
見事に動きませんでした。
なんかJetty関連のライブラリがないとかのエラー。
本家のドキュメントには、パラメーターの設定が少し載っているぐらいで、役にたたない。
これは、時間が掛かりそうなんで、また、今度にすることに...

gradle clean build JettyRun
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:war
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
:jettyRun
Failed startup of context org.gradle.api.plugins.jetty.internal.JettyPluginWebAppContext
java.lang.IllegalArgumentException: Object is not of type class org.eclipse.jetty.webapp.WebAppContext
	at org.mortbay.xml.XmlConfiguration.configure(XmlConfiguration.java:189)
	at org.mortbay.jetty.webapp.JettyWebXmlConfiguration.configureWebApp(JettyWebXmlConfiguration.java:103)
	at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1269)

次にSpring MVCのテストを書いて、テストが正常に動くかやってみました。
作成したテストクラスは、下のクラスです。

public class TestHelloMessageServiceImpl {
	private static HelloMessageService helloMessageService;
	private MockMvc mockMvc;
	@Before
	public void setUp() throws Exception {
		GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
		XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(genericApplicationContext);
		xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
		ConfigurableListableBeanFactory beanFactory = genericApplicationContext.getBeanFactory();
		Scope request = new SimpleThreadScope();
		beanFactory.registerScope("request", request);
		genericApplicationContext.refresh();
		helloMessageService = (HelloMessageService)genericApplicationContext.getBean("helloMessageService");
		mockMvc = MockMvcBuilders.standaloneSetup(helloMessageService).build();
	}
	@Test
	public void テスト() throws Exception {
		mockMvc.perform(
				get("/message/hello/100")
						.characterEncoding("UTF-8")
						.accept("application/json;charset=UTF-8"))
					.andExpect(status().isOk())
					.andExpect(content().contentType("application/json;charset=UTF-8"))
					.andExpect(jsonPath("$.id").value("hello"))
					.andExpect(jsonPath("$.message").value("Hello World"));
	}
}

それでは、テストを実行!!
これは、すんなりうまく行ったwww。
テストが動作しているか?不明だったので、一度、エラーになるテストを書いて確認したので、
恐らくこれで、大丈夫だと思います。

gradle clean test
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources
:testClasses
:test

BUILD SUCCESSFUL

Total time: 6.32 secs

とりあえず、今日は、これで終わり。
簡単に触った感じは、楽そうですが、なれるまで、少し時間がかかるかなぁ。
それと、実際で、システムで導入するには、環境依存とか自動生成系のものとか、
テンプレート化など、いろいろあると思うので、
かなり扱えるレベルにならないと使えない気がするので、
お休みの日にひっそりがんばりたいと思います。

【第48回】すくすくスクラムへ潜入

すくすくスクラムへ潜入してきました。
若干、遅刻してしまいました。
東京駅で、迷子になったwww。
それで、今回の勉強の内容
Scurmな人材育成〜プロダクト開発に必要な人材像や育成方法〜
・組織としてスクラムを導入するメリットについて?
・人材育成の観点で、プロダクト開発の中で、伸ばせるスキル、伸ばせないスキルは?
ディスカッションのやり方は、ワールードカフェスタイル?に近い形式です。
f:id:chronos_snow:20140905200008j:plain
最初の議論の内容は、人に足りないもの?
議論して、出た項目
・集中力
・表現能力
・伝達能力
・感情のコントロール
・体力
・視力
・知識力
・信頼
f:id:chronos_snow:20140905202034j:plain
次の議論の内容は、人が成長に必要なもの?
議論して、出た項目
・モチベーション
 ⇒ 〜なりたい!
 ⇒ あこがれ
 ⇒ お金
 ⇒ 評価
・プレッシャー
・ライバル
・いい仕事
 ⇒ キャリアパス
 ⇒ 仕事を楽しむ
 ⇒ 自分のレベルに合った仕事
 ⇒ 少し高いレベルの仕事
・勉強・学習
・好奇心
・時間
・チャレンジ
・自発的
f:id:chronos_snow:20140905203908j:plain
次の議論の内容は、では?議論した中でスクラムが役立つことは?
人になりないもの?について、役立つもの
知識力、信頼力ぐらい?
人が成長に必要なもの?
・時間
 ⇒ スクラムのタイムイベント
・評価
 ⇒ 価値とか、ユーザーに必要なものとか議論して作っていくから?
・勉強・学習
 ⇒ スクラムの定義としては、ないかも?
 ⇒ プロセス改善的な取組みとしては、あるかも?
  ⇒ それは、スクラムではなく、XPとか、チーム独自の取組みなんじゃ?
f:id:chronos_snow:20140905211636j:plain
最後に人材育成に関しての紹介
・アダプティヴィ・ラーニング
 ⇒ 学習者の学習履歴データを蓄積・解析することで、各自に適した学習コンテンツを提供する。
・マスタリー・ラーニング
 ⇒ ほぼ全ての学習者に一定水準以上の学力を保証することにオモキをおく
・インストラクショナル・デザイン
 ⇒ 主に心理学、学習学、コミニケーション学、情報学、行動分析学などを基礎として、
   各分野で確率された科学的な知見を網羅しながら教育方法として、あるべき姿を提示する。
・教育方法の設計
 ⇒ 5フェーズ(分析、設計、開発、実践、評価)
 ⇒ 4フェーズ(準備、反復設計、反復開発、本格運用)
f:id:chronos_snow:20140905211659j:plain
以上、これで、終了でした。
人材教育の側面でのスクラムとして、なにが出来るかの議論は、
いろいろな人の意見を聞いたり、議論出来たのは良かった。
人材育成の手法を何パターンか紹介してもらえたので、ちょっと調べようかと思います。
スクラムマスタのみなさんお疲れ様でした。
f:id:chronos_snow:20140905220154j:plain

【第4回】スクラムナイトセミナーに潜入

スクラムナイトセミナーに潜入しました。
このセミナーも徐々に参加する人数が多くなってきました。
業界も幅広く、いろいろな人と話せるので、楽しい。
今回も最初に聞きたいことを付箋に書いて、
みんなで、出し合いホワートボードに張り出し、共有します。
・見積もりに関して
・ストーリーに関して
スクラムの導入に関して
などなど、いろいろなことが張り出されてました。
その後は、いつもと同じく、
どれについて、議論するかを投票で上位4つを決定して、
チームに分かれる感じでした。
f:id:chronos_snow:20140728202258j:plain
今回は、プロダクトバックロググルーミングに関して、
議論するチームに入らせてもらいました。
他のスクラムマスターは、どうやってのかなぁと思ったので、
スクラムの中で、プロダクトバックロググルーミングをやると、
結構時間が掛かってしまうものなので、
みんなは、どうやっているのか興味があった。
・やってない...エッセンシャルを見て知った。
・スプリント中、小分けして実施する。
・スプリントの最後で。
などなど(結構あいまいで、覚えてないwwww)。
f:id:chronos_snow:20140728213056j:plain
最後は、各チーム発表です!!
f:id:chronos_snow:20140728213247j:plain

Play FrameworkとSpring Frameworkの連携

最近、Play Frameworkにも手を出して、家で、いろいろやっているので、
今回は、Play Frameworkについてのエントリーです。
それで、Play Frameworkで、DIコンテナSpring Frameworkを使いたい場合の
連携方法に関して書きたいと思います。
やっぱり、DIしたいなぁと思ったので!!
まずは、最初にbuild.sbtにSpringに必要なものを設定します。

libraryDependencies ++= Seq(
  "org.aspectj" % "aspectjweaver" % "1.7.3",
  "org.springframework" % "spring-context" % "4.0.5.RELEASE",
  "org.springframework" % "spring-orm" % "4.0.5.RELEASE",
  "org.springframework" % "spring-jdbc" % "4.0.5.RELEASE",
  "org.springframework" % "spring-tx" % "4.0.5.RELEASE",
  "org.springframework" % "spring-expression" % "4.0.5.RELEASE",
  "org.springframework" % "spring-aop" % "4.0.5.RELEASE",
  "org.springframework" % "spring-test" % "4.0.5.RELEASE" % "test"
) 

設定が完了したら、playのコマンドで、Springをダウンロードさせます。

play dependencies

Springのダウンロードがうまく行ったら、
Playのグローバル設定を行う為、クラスを作成します。
ここで、Springの設定ファイルのapplicationContext.xmlを読ませて、
ApplicationContextを生成します。
PlayのGlobalSettingsクラスを継承した、ApplicationConfigurationクラスを作成します。

public class ApplicationConfiguration extends GlobalSettings {
	private static ApplicationContext context = null;
	private String lock = "lock";
	@Override
	public void onStart(Application application) {
		if (context == null) {
			synchronized(lock) {
				context = new FileSystemXmlApplicationContext(
						new String[] {"conf/applicationContext.xml"});
			}
		}
	}
	@Override
	public <A> A getControllerInstance(Class<A> clazz) {
		return context.getBean(clazz);
	}
}

configディレクトリにSpringのapplicationContext.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:util="http://www.springframework.org/schema/util"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jee
		http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
		http://www.springframework.org/schema/lang
		http://www.springframework.org/schema/lang/spring-lang-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">
	<!-- Autowire -->
	<context:component-scan base-package="logic,controllers" scoped-proxy="targetClass"/>
	<!-- AOP -->
	<aop:aspectj-autoproxy/>
</beans>

次にルーティングの設定をするので、これもconfigにあるroutesファイルを
下記の様に編集します。

GET     /                           @controllers.Application.index()

これで、連携の準備は、完了です。
あとは、コントローラーにインジェクションできるようになるので、
何らかのクラスを@Componentのアノーテションを付けて、
下の様に、コントローラークラスにインジェクションするように書くだけです。

@Service
public class Application extends Controller {
	@Autowired
	@Qualifier("logic")
	private Logic logic;
    public Result index() {
    	System.out.println(logic.getMessage());
        return ok(index.render("Your new application is ready."));
    }

}