3.4. Dependencies

3.4 Dependencies
bean間の依存関係の定義について。なんか書いてあったけど忘れたので省略。この節は長いな…。

3.4.1 Dependency injection

SpringのDIには、コンストラクタインジェクションと、セッターインジェクションがある。

3.4.1.1 Constructor-based dependency injection

コンストラクタによるDIについて。基本形。






基本型など型が不明瞭な場合のシンタックスその1






基本型など型が不明瞭な場合のシンタックスその2。引数順を指定する。



 
 

基本型など型が不明瞭な場合のシンタックスその3
(debugオプションつきか、@ConstructorPropertiesをつける必要あり。引数名から特定。)



 
 

3.4.1.2 Setter-based dependency injection

setterで指定。例はあとで出てくる。なお完全に余談だが、普通はDIの指定はXMLで定義するが、直接BeanDefinitionというクラス?を使ってカスタマイズできる。普通はやらないらしい。


3.4.1.3 Dependency resolution process

Springのコンテナは、コンテナ生成時にBean定義ファイルのバリデーションをする。バリデーションの時点ではBeanそのものは生成されない。またシングルトンスコープのBeanはコンテナ生成時に一緒に作られる。(この挙動はカスタマイズ可能。)

XML定義ファイルのバリデーションは、Beanの参照が正しいこと、循環参照がないことなどをチェックする。

3.4.1.4 Examples of dependency injection

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;
}
}

3.4.2 Dependencies and configuration in detail
3.4.2.1 Straight values (primitives, Strings, and so on)

文字列など基本型をインジェクションする例。以下は基本的な定義の仕方。




p-namespaceによる簡略化。タグをネストして書かなくてよい。



ただしp-namespaceはXSDがないのでXMLスキーマによるバリデートができない。よって、以下にあるようなIDEの支援を受けるべき。
http://www.jetbrains.com/idea/
http://www.springsource.com/products/sts

また属性の設定についてはjava.util.Propertiesの設定例をつかうといいよ




jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb



#TODO:上記はbeanの属性設定にも使えるかな?

The idref element







とかくと、XMLを跨って同じIDを参照していなければXMLのバリデーションで設定エラーにできる。





と書くと、同じXMLファイルに指定したIDのBeanがなければエラーにできる。

3.4.2.2 References to other beans (collaborators)

コンテナに定義したbeanへの参照を定義する書き方について。に対して定義する。

これが一番普通の書き方。



同一コンテナ、もしくは親コンテナに対する参照を書く。同一のXMLファイル中かどうかは意識しない。beanの値は参照するbeanのidかnameと一致。



同一XMLファイル中のidに同じものがないとバリデーションエラーになる。



親コンテナのbeanを指定する。

3.4.2.3 Inner beans








常に無名で、かつスコープはプロトタイプになる。

3.4.2.4 Collections

コレクションの定義。まぁサンプルみれ

コレクションのマージ。abstractで親を定義し、parentで継承する感じ。サンプルみれ

なおJava5のようなジェネリクス使ったコレクションの場合は、実行時にリフレクションで型解決されます。

3.4.2.5 Null and empty string values

みれ。





3.4.2.6 XML shortcut with the p-namespace

pネームスペース使うと定義が楽。






でもpネームスペースはXSDとして定義されてるわけじゃなくて動的に解決するだけ。
しかもrefとかは



とかなっちゃう。*1いろいろ危なそうなのでpネームスペース使うときは気をつけたほうがいいよ。

3.4.2.7 Compound property names





要するに属性を.でつなげるパターンですね。

3.4.3 Using depends-on

depends-on属性を使うと、beanの初期化順を指定できる。bean間に直接refがなくても暗黙依存関係がある場合は、
これつかって初期化の依存関係を張ることができる。




→beanOne初期化前に、managerを初期化する。depends-on属性には複数カンマ区切りで指定可能。
なおsingletonの場合はシャットダウンプロセスの依存関係にも反映されるらしい。

3.4.4 Lazy-initialized beans

シングルトンのスコープでもBeanが要求されるまで初期化が遅延される。



これはbean毎に指定する例。





とするとデフォルトの挙動を変更できる。

でもお勧め的にはlazyしないほうがいい。

3.4.5 Autowiring collaborators

以下のいづれかの設定に従いオートワイアリングする。かかなくていい。これをやるためには タグのautowire属性に以下のモードを指定する。

・no
 デフォルト。オートワイアリングしない。
・byName
 属性名でオートワイアリングする。private Foo fooValには、bean名がfooValのオブジェクトがインジェクトされる
・byType
 型でオートワイアリングする。同一の型のオブジェクトが複数あったら例外。対応する型のオブジェクトがなければ何もしない
・constructor
 byTypeのコンストラクタ版?ちょっとわからん

3.4.5.1 Limitations and disadvantages of autowiring

XMLを使ったオートワイアリングは必ずしもおすすめできない。

  • オートワイアリングは、の定義より弱い。があればその定義で上書きされる。
  • オートワイアリングは設定ファイルによるワイアリングよりも不正確。
  • 複数のbeanがオートワイアリング対象になるコンフリクト問題
  • ほかにもあるけど割愛(読むのめんどかった)

実際に使うにあたっては、いくつかの選択肢がある。

  1. オートワイアリングを一切使わない。
  2. beanに対してautowire-candidate属性を指定して、bean毎にコンフリクト起こさないようにようにする(autowire-candidate=falseにしたら、そのbeanはオートワイアリング対象外)
  3. beanに対してprimary属性をtrueにする(たぶんそのbeanが優先される?未調査)
  4. アノテーションでオートワイアリング

3.4.5.2 Excluding a bean from autowiring

・autowire-candidate
 に定義。bean毎にautowireing対象からはずす
・default-autowire-candidates 
 に定義。コンテナ毎に、autowire対象からはずすbean名の正規表現を複数指定(カンマ区切り)

3.4.6 Method injection

SingletonのBean「A」にSingletonではないBean「B」をインジェクトしても毎回生成されないよ。
この問題を救う普通のやり方は、ApplicationContextAwareを実装する。


public class CommandManager implements ApplicationContextAware {

private ApplicationContext applicationContext;

public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}

public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}

で、このやり方ではSpringに依存してしまう。これがやな人のためにgetterをCGLIB使って動的に置き換える方法(getterインジェクション)がある。

具体的にはまずこう定義する。


package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

// 生成対象のbeanを取得するメソッド
//  [abstract] theMethodName(no-arguments); の形式でないとだめ
protected abstract Command createCommand(); 
}

次にbean定義のXMLに以下のように書く



//createCommandの戻り値として参照するbean名を指定する


これ以外のメソッド置換も可能だけど、まぁ必要になったら調べてみてね。あんまり使わないと思うから。

*1: (属性「spouse」に対するref。普通に書くと