読者です 読者をやめる 読者になる 読者になる

新人SEの学習記録

14年度入社SEの学習記録用に始めたブログです。ようやく社会人2年目に突入。

学習記録:Spring

[学習記録] Spring

内容:2章 SpringのDI

DIとは何か
DIの使い所
  • 値をRDBから持ってきてインスタンス化する作業には向かない
    • プロパティファイルから固定値を持ってきてインスタンス生成するのは得意
    • ドメインオブジェクト間の依存関係をDIで構築してから値をRDBから読み込んで設定、のようなことはしてはダメ
    • コントローラとサービス、サービスとDAOの依存関係には向くが、サービス/DAOとドメインの依存関係には向かない
    • あらゆるクラスにインタフェースを作るのも間違い(全てのクラスが単体でコンポーネントになるわけではない)
アノテーションを使ったDI
  • @Autowiredと@Component
    • インスタンス変数の前にAutowiredを付けると、DIコンテナがその変数の型に代入できるクラスを@Componentの付いているクラスから探し出し、インジェクションしてくれる
    • インスタンス変数がprivateでもインジェクションできる(setterが必要ない)
    • @Componentの付くクラスが複数あっても、型が違っていればインジェクションされない(byType)
    • アノテーションを使ってもBean定義ファイルは必要
@Component
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductDao productDao;

    ...
}
  • Bean定義ファイル
    • 定義ファイルの名前は慣習的にapplicationContext.xmlとすることが多い
    • beansタグを記述、下記の例では属性にはXMLスキーマとしてbeanとcontextの2つを宣言
    • 続くタグは@Autowiredと@Componentを実現するためのタグ
    • context:annotation-config
      • @Autowired、@Resourceを利用する場合の宣言
      • context:conponent-scanやmvc:annotation-drivenが記述されている場合は省略も可能
    • context:component-scan
<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

  <context:annotation-config />
  <context:component-scan base-package="sample.*"/>
</beans>
名称 説明
bean Bean(コンポーネント)の設定
context Beanの検索やアノテーションの設定
util 定数定義やプロパティファイルの読み込みなどのutility機能の設定
jee JNDIおよびEJBのlookupの設定
lang スクリプト言語を利用する場合の設定
aop AOPの設定
tx トランザクションの設定
mvc SpringMVCの設定
@Autowiredと@Component
  • @Autowired
    • インジェクション(してもらうため)の設定
    • インスタンス変数の前に付ける以外に、適当なメソッド宣言の前にも付けることができる
    • 引数に設定されたFooとBarの2つをインジェクションすることも可能
    • 基本的にインジェクション必須(対象オブジェクトが無いとエラーになる)だが、required=falseにすることで必須で無くなる
    • インタフェースの実現クラスが2つ以上ある場合エラーになるが、@Autowiredと@Qualifierを併用することで回避可能
    • @Component("hoge")で名前を付け、@Qualifier("hoge")でコンポーネントを指定(byName)
@Autowired
public void setFoo(Foo foo) {
    this.foo = fop;
}

@Autowired
public void setFooBar(Foo foo, Bar bar) {
    this.foo = foo;
    this.bar = bar;
}
<context:component-scan base-package="sample.di.business.*" />
<context:component-scan base-package="sample.di.dao.*" />
アノテーション 解説
@Controller プレゼンテーション層Spring MVCアノテーション
@Service ビジネスロジック層Service用アノテーショントランザクション管理できるという噂もあったが・・・
@Repository データアクセス層DAO用アノテーション。例外を全てDataAccessExceptionに変換
  • @PostConstructと@PreDestroy
    • それぞれ初期処理/終了処理を行うメソッドを宣言するアノテーション
    • @PostConstructはインジェクション後に呼ばれるので、インジェクションされた値を使った初期化処理に使う
    • (でなければコンストラクタで済むので)
    • Javaにはデストラクタがないので、終了処理には@PreDestroyを使うしかない
Bean定義ファイルでDI
  • アノテーションを使ったDIは非常に便利で、小規模開発ではよく使われる
    • 大規模開発になってくるとアノテーションを管理するのは容易ではない
    • アーキテクトチームがBean定義ファイルを利用してDIを管理
  • BeanFactory
    • DIコンテナの実態の核(正確にはBeanFactoryインタフェースとその具象クラス)
    • BeanFactoryは、その実行時に渡されるBean定義ファイルに基づいてインスタンスを生成、インジェクションを行う
    • 開発者が直接利用することは少ないが、DIコンテナからインスタンスを取得するということは、実際にはBeanFactoryから取得していることになる
  • Bean定義ファイルの書き方は以下。
<beans>

<!-- クラスYにXをAutowiredでインジェクションする -->
  <bean id="Xのオブジェクト名" class="Xのパッケージ名.Xのクラス名" />
  <bean id="Yのオブジェクト名" class="Yのパッケージ名.Yのクラス名" autowire="byType" />
<!-- クラスYにXを明示的にインジェクションする -->
  <bean id="Xのオブジェクト名" class="Xのパッケージ名.Xのクラス名" />
  <bean id="Yのオブジェクト名" class="Yのパッケージ名.Yのクラス名" >
    <property name="インスタンス変数名" ref="Xのオブジェクト名" />
<!-- 例 -->
  <bean id="productService"
      class="sample.di.business.service.ProductServiceImpl" autowire="byType" />
  <bean id="productDao" class="sample.di.dataaccess.ProductDaoImpl" />

</beans>
  • beanタグの属性
属性 意味
id オブジェクトを一意に示すID
name オブジェクト名を定義(オブジェクトに複数の名前をつけたりする場合に使用)
class idの実態。パッケージ名とクラス名から成る
parent 設定を引き継ぐBeanのクラス名を指定
abstract true/false、trueでインスタンスを生成せずに共通の設定を定義したい場合。省略時false
singleton true/false、trueでgetBeanメソッドで取得するインスタンスはシングルトンに。省略時true
lazy-init true/false。trueでインスタンス化を遅らせる。省略時false
autowire no/byName/byType/constructor/autodetect。省略時no
dependency-check none/simple/object/all。インスタンス変数へのインジェクションのチェックをするか否か。省略時none
depend-on 依存関係の対象となるオブジェクトの存在をチェックする。
init-method メソッド名を記述することで、インスタンス変数の設定後に呼ばれる。メソッドは引数がないことが条件。
destroy-method システムh数量じに呼ばれるメソッド
  • プロパティファイルの利用
    • メッセージなどのプロパティファイルを読み込みたい場合
    • util:propertiesを使用
<util:properties id="msgProperties" location="classpath:sample/config/message.properties" />

<bean id="message" class="sample.MessageServiceImpl">
  <property name="message" value="#{msgProperties.message}" />
</bean>