SpringのDI(Dependency Injection)のサンプルを作りました

Springフレームワークが提供するDI(Dependency Injection)を理解するためのサンプルプログラムを作りました。
『Spring3入門』の 第2章 SpringのDI を参考にしています。初版の第一刷を買ったせいか、それじゃ動かないだろ、というサンプルソースもあるが許容範囲内。Java分かるけどSpring初めての人には為になる印象です。

Spring3入門 ――Javaフレームワーク・より良い設計とアーキテクチャ

Spring3入門 ――Javaフレームワーク・より良い設計とアーキテクチャ

DIの概念などは書籍や他のサイトの説明に譲るとして、ここでは初めてSpringを動かした感想のみ。

JavaSE1.5以上のバージョンでの実開発経験が無い身にとって衝撃的だったのは、DAOを呼び出す時に実装クラスをnewする必要が無いところ。
しかもDAOは呼び出しの都度Springがnewするのではなく、Singletonのインスタンスとして取得できる(つまりSpringからinjectされる)。
DAOをSingletonにする理由は明確でないが、都度newしないのでパフォーマンス向上は期待できると思われる。
※誤解の無いように言うと、都度newされた(Singletonでない)インスタンスとして取得することも可能。

また、ビジネスロジックの開発者がDAOの実装クラスをnewする必要なくインスタンスを取得できるので、DAOの呼び出し側(ビジネスロジック層)にDAOの実装クラス名は一切出てこない。
これは本当に気持ちが良い。インタフェースによるプログラムが可能になったのだと実感が沸く。

JUnitのテストコードの書き方に若干コツが必要なので、サンプルプログラムのソースコードをダウンロードできるようにしておきます。
20121228_SpringTest.zip 直

Eclipseで実行する場合は、一旦Eclipseのプロジェクトに変換してからインポートするのが楽です。

c:\SpringTest>mvn eclipse:eclipse
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building SpringTest 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-eclipse-plugin:2.8:eclipse (default-cli) @ SpringTest >>>
[INFO]
[INFO] <<< maven-eclipse-plugin:2.8:eclipse (default-cli) @ SpringTest <<<
[INFO]
[INFO] --- maven-eclipse-plugin:2.8:eclipse (default-cli) @ SpringTest ---
[INFO] Using Eclipse Workspace: null
[INFO] Adding default classpath container: org.eclipse.jdt.launching.JRE_CONTAINER
[INFO] Not writing settings - defaults suffice
[INFO] Wrote Eclipse project for "SpringTest" to c:\SpringTest.
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.809s
[INFO] Finished at: Fri Dec 28 23:15:19 JST 2012
[INFO] Final Memory: 9M/109M
[INFO] ------------------------------------------------------------------------
c:\SpringTest>


コマンドラインMavenから起動したJUnitのテスト結果も参考までに載せておきます。

c:\SpringTest>mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building SpringTest 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ SpringTest ---
[WARNING] Using platform encoding (MS932 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ SpringTest ---
[WARNING] File encoding has not been set, using platform encoding MS932, i.e. build is platform dependent!
[INFO] Compiling 6 source files to c:\SpringTest\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ SpringTest ---
[WARNING] Using platform encoding (MS932 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ SpringTest ---
[WARNING] File encoding has not been set, using platform encoding MS932, i.e. build is platform dependent!
[INFO] Compiling 1 source file to c:\SpringTest\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ SpringTest ---
[INFO] Surefire report directory: c:\SpringTest\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running sample.di.business.service.ProductServiceTest
12 28, 2012 12:25:26 午前 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
情報: @TestExecutionListeners is not present for class [class sample.di.business.service.ProductServiceTest]: using defaults.
12 28, 2012 12:25:26 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
12 28, 2012 12:25:27 午前 org.springframework.context.support.AbstractApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.GenericApplicationContext@15b96350: display name [org.springframework.context.support.GenericApplicationContext@15b96350]; startup date [Fri Dec 28 00:25:27 JST 2012]; root of context hierarchy
12 28, 2012 12:25:27 午前 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
情報: Bean factory for application context [org.springframework.context.support.GenericApplicationContext@15b96350]: org.springframework.beans.factory.support.DefaultListableBeanFactory@a7393a2
12 28, 2012 12:25:27 午前 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
情報: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@a7393a2: defining beans  org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,ProductService,productDaoImpl]; root of factory hierarchy
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.619 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.630s
[INFO] Finished at: Fri Dec 28 00:25:27 JST 2012
[INFO] Final Memory: 14M/110M
[INFO] ------------------------------------------------------------------------
c:\SpringTest>