Spring学習メモ(Springとは~IoCコンテナ)
仕事でSpringを触る羽目になったことになったので、勉強した内容をメモしておく。
参考にしたのは公式リファレンスとPluralsightのSpringコース。
Spring Framework Reference Documentation
http://www.pluralsight.com/courses/spring-fundamentals
- Springとは
Java開発フレームワーク。Java開発者は、さまざまなデザインパターン(Singleton、Factory、Abstract Factory、Template Method、Decorator、Service Locator…)を駆使してアプリを構築していくが、Springを使えばこれらを自前で実装することなく実現できる。
- 主な特徴
- POJO(Plain Old Java Object)ベースなので再利用が容易
- J2EEに比べとても軽量
- コード横断的な機能を実現するため、AOP(Aspect Oriented Programming)やプロキシパターンを採用
- ベストプラクティス、デザインパターンを導入
- 全体像
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/overview.html
約20のモジュールで構成される。コアコンテナ、データアクセス・統合、Web、AOP、Instrumentation、メッセージング、テストに分類される。一部のみを採用したり、別フレームワークのコンポーネントを導入したりすることも容易に可能で、これは、IoCコンテナで実現される。
- IoCコンテナ ※SpringではDIコンテナではなくIoCコンテナと呼ぶようだ
インターフェースorg.springframework.context.ApplicationContextがSpringのIoCコンテナを表現しており、これがBeanのインスタンス化、コンフィグレーション、構築の役割を担う。IoCコンテナは、XML・Javaアノテーション・またはJavaで書かれた「コンフィグレーションメタデータ」を参照して動く。
コード例(http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.htmlより)
コンフィグレーションメタデータ(services.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --> </beans>
IoCコンテナとインスタンスの生成
// create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // retrieve configured instance PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance List<String> userList = service.getUsernameList();
- services.xmlでPetStoreServiceImplクラスを、petStoreという名前で登録し、これをJavaコード側で呼び出している
- BeanはデフォルトでSingleton扱い
- xmlのpropertyにある「ref="xx"」は他Beanへの依存性。(コンテナが解決する)
- Javaコード側では、services.xmlからコンテナを生成
- getBean()でPetStoreServiceを取得
Beanのインスタンス化
デフォルトでは、コンテナはコンストラクタを使ってBeanを生成するが、Static Factory MethodやInstance Factory Methodを使うこともできる。
依存性注入の方法
コンストラクタベース、またはSetterベースの注入ができる。(例はいずれも
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より)
コンストラクタベースの例
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
<beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
Setterベースの例
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i){ this.i = i; } }
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
なお後述するように、@Autowiredアノテーションを使って、Setterなしのメンバーに直接注入することも可能。
PropertyPlaceholderConfigurer
XMLに設定する実際の値は、別ファイルに定義することができる。(例はいずれも
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より)
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/> <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
上記のように、使うためにはPropertyPlaceholderConfigurerの定義が必要。
Annotation-based Configuration
メタデータの一部をアノテーションを使ってJavaクラスに移すことにより、XML(とトータルの記載量)を削ることができる。(コード例はhttp://dev.classmethod.jp/server-side/java/spring-firstcontact/ より。一部改変)
@Autowired
<bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker"> <property name="sqs" ref="amazonSQSClient" /> <property name="mailSender" ref="javaMailSenderImpl" /> <property name="mailTemplate" ref="templateMessage" /> </bean>
public class QueueWorker { private AmazonSQS sqs; private MailSender mailSender; private SimpleMailMessage mailTemplate; public void setSqs(AmazonSQS sqs) { this.sqs = sqs; } public void setMailSender(MailSender mailSender) { this.mailSender = mailSender; } public void setMailTemplate(SimpleMailMessage mailTemplate) { this.mailTemplate = mailTemplate; } }
上記は、@Autowiredアノテーションで次のように書き換えできる。
<context:annotation-config /> <bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker“ />
public class QueueWorker { private AmazonSQS sqs; private MailSender mailSender; private SimpleMailMessage mailTemplate; @Autowired public void setSqs(AmazonSQS sqs) { this.sqs = sqs; } @Autowired public void setMailSender(MailSender mailSender) { this.mailSender = mailSender; } @Autowired public void setMailTemplate(SimpleMailMessage mailTemplate) { this.mailTemplate = mailTemplate; } }
コンテナは、自動的にターゲットのBeanをTypeから判別して取得・注入する。
public class QueueWorker { @Autowired private AmazonSQS sqs; @Autowired private MailSender mailSender; @Autowired private SimpleMailMessage mailTemplate; }
フィールドに直接Autowireすることもできる。このとき、コンテナはリフレクションで値をセットする。
public class QueueWorker { private AmazonSQS sqs; private MailSender mailSender; private SimpleMailMessage mailTemplate; @Autowired public QueueWorker(AmazonSQS sqs, MailSender mailSender, SimpleMailMessage mailTemplate) { this.sqs = sqs; this.mailSender = mailSender; this.mailTemplate = mailTemplate; } }
コンストラクタベースの例。
@Component
コンポーネントスキャンを使うと、Beanの定義自体をXMLから削れる。
<context:annotation-config /> <context:component-scan base-package="jp.classmethod.sample.spring.start" /> <!-- <bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker" /> -->
@Component public class QueueWorker { @Autowired private AmazonSQS sqs; @Autowired private MailSender mailSender; @Autowired private SimpleMailMessage mailTemplate; }
Springはターゲットになっているパッケージをスキャンし、@Componentアノテーションつきのクラスを自動でBeanとして登録する。
@Component以外にも、@Repository(データアクセスレイヤ用)、@Service(ビジネスサービスレイヤ用)、@Controller(MVCのコントローラ)が、コンポーネントスキャンの対象。(ステレオタイプアノテーションと呼ぶ)
Bean名は、小文字のクラス名になる(例:QueueWorker→queueWorker)が、指定することもできる。
@Component(“MyQueueWorker”) public class QueueWorker { @Autowired private AmazonSQS sqs; @Autowired private MailSender mailSender; @Autowired private SimpleMailMessage mailTemplate; }
IoCコンテナ関連のその他のトピック
- XMLのProperty値(文字列)は、ConversionServiceで変換されて使用される
- XMLのProperty部の記載量を減らすため、p-namespaceという方法がある
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="masterkaoli"/>
(http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より) - Beanのスコープは、①Singleton(デフォルト)、②Prototype(リクエストのたびに新インスタンスを生成)、③Request(Web)、④Session(Web)、⑤Global Session(Web)、⑥Application(Web)が設定できる
- SingletonのBeanは、アプリケーションの初期化時にすべて生成されるが、遅延させるオプションもある
- コンフィグレーションメタデータは、XMLを使わず、すべてJavaで書くこともできる