依存性注入
依存性注入(DI)は、コンストラクターの引数、ファクトリメソッドへの引数、オブジェクトインスタンスの構築後に設定されるプロパティを通じてのみ、オブジェクトが依存関係(つまり、動作する他のオブジェクト)を定義するプロセスです。ファクトリメソッドから返されます。コンテナーは、Bean を作成するときにそれらの依存関係を注入します。このプロセスは、基本的に、クラスの直接構築または Service Locator パターンを使用して、Bean 自体のインスタンス化または依存関係の位置を制御する Bean 自体の逆(つまり、Inversion of Control)です。
DI の原則によりコードは簡潔になり、オブジェクトに依存関係が提供されると、デカップリングがより効果的になります。オブジェクトは依存関係を検索せず、依存関係の場所またはクラスを知りません。その結果、特に依存関係がインターフェースまたは抽象基本クラスにある場合、クラスのテストが容易になり、ユニットテストでスタブまたはモックの実装を使用できるようになります。
DI は、コンストラクターベースの依存性注入と setter ベースの依存性注入の 2 つの主要なバリアントに存在します。
コンストラクターベースの依存性注入
コンストラクターベースの DI は、それぞれが依存関係を表すいくつかの引数を使用してコンストラクターを呼び出すコンテナーによって実現されます。特定の引数を使用して static
ファクトリメソッドを呼び出して Bean を構築することはほぼ同等であり、この説明ではコンストラクターと static
ファクトリメソッドの引数を同様に扱います。次の例は、コンストラクターの注入でのみ依存関係を注入できるクラスを示しています。
Java
Kotlin
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private final MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
// a constructor so that the Spring container can inject a MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
// business logic that actually uses the injected MovieFinder is omitted...
}
このクラスには特別なことは何もないことに注意してください。これは、コンテナー固有のインターフェース、基本クラス、アノテーションに依存しない POJO です。
コンストラクター引数解決
コンストラクターの引数解決の一致は、引数の型を使用して発生します。Bean 定義のコンストラクター引数に潜在的なあいまいさが存在しない場合、コンストラクター引数が Bean 定義で定義される順序は、Bean がインスタンス化されるときにそれらの引数が適切なコンストラクターに提供される順序です。次のクラスを検討してください。
Java
Kotlin
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)
ThingTwo
クラスと ThingThree
クラスが継承によって関連付けられていないと仮定すると、潜在的なあいまいさは存在しません。次の構成は正常に機能し、<constructor-arg/>
要素でコンストラクター引数のインデックスまたは型を明示的に指定する必要はありません。
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
別の Bean が参照される場合、型は既知であり、一致が発生する可能性があります(前の例の場合のように)。<value>true</value>
などの単純な型が使用される場合、Spring は値の型を判別できないため、ヘルプなしでは型ごとに一致できません。次のクラスを検討してください。
Java
Kotlin
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private final int years;
// The Answer to Life, the Universe, and Everything
private final String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
package examples
class ExampleBean(
private val years: Int, // Number of years to calculate the Ultimate Answer
private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything
)
コンストラクター引数型の一致
上記のシナリオでは、次の例に示すように、type
属性を使用してコンストラクター引数の型を明示的に指定すると、コンテナーは単純型との型マッチングを使用できます。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
コンストラクター引数インデックス
次の例に示すように、index
属性を使用して、コンストラクター引数のインデックスを明示的に指定できます。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
複数の単純な値のあいまいさを解決することに加えて、インデックスを指定すると、コンストラクターに同じ型の 2 つの引数がある場合のあいまいさを解決できます。
インデックスは 0 ベースです。 |
コンストラクター引数名
次の例に示すように、コンストラクターパラメーター名を使用して値を明確にすることもできます。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
これをすぐに動作させるには、Spring がコンストラクターからパラメーター名を検索できるように、-parameters
フラグを有効にしてコードをコンパイルする必要があることに注意してください。-parameters
フラグを使用してコードをコンパイルできない場合、またはコンパイルしたくない場合は、@ConstructorProperties (標準 Javadoc) JDK アノテーションを使用して、コンストラクター引数に明示的に名前を付けることができます。サンプルクラスは次のようになります。
Java
Kotlin
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
package examples
class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)
setter ベースの依存性注入
setter ベースの DI は、引数なしのコンストラクターまたは引数なしの static
ファクトリメソッドを呼び出して Bean をインスタンス化した後に、コンテナーが setter メソッドを Bean で呼び出すことによって実現されます。
次の例は、純粋な setter インジェクションを使用することによってのみ依存関係をインジェクトできるクラスを示しています。このクラスは従来の Java です。これは、コンテナー固有のインターフェース、基本クラス、アノテーションに依存しない POJO です。
Java
Kotlin
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
class SimpleMovieLister {
// a late-initialized property so that the Spring container can inject a MovieFinder
lateinit var movieFinder: MovieFinder
// business logic that actually uses the injected MovieFinder is omitted...
}
ApplicationContext
は、管理する Bean のコンストラクターベースおよび setter ベースの DI をサポートします。また、コンストラクターアプローチによっていくつかの依存関係がすでに注入された後の setter ベースの DI もサポートします。依存関係は BeanDefinition
の形式で構成します。これを PropertyEditor
インスタンスと組み合わせて使用して、プロパティをある形式から別の形式に変換します。ただし、ほとんどの Spring ユーザーは、これらのクラスを直接(つまり、プログラムで)操作するのではなく、XML bean
定義、アノテーション付きコンポーネント(つまり、@Component
、@Controller
などでアノテーションが付けられたクラス)、Java ベースの @Bean
メソッドを操作します。@Configuration
クラス。次に、これらのソースは内部で BeanDefinition
のインスタンスに変換され、Spring IoC コンテナーインスタンス全体をロードするために使用されます。
依存関係解決プロセス
コンテナーは、次のように Bean 依存関係の解決を実行します。
ApplicationContext
は、すべての Bean を記述する構成メタデータで作成および初期化されます。構成メタデータは、XML、Java コード、アノテーションによって指定できます。各 Bean の依存関係は、プロパティ、コンストラクター引数、静的ファクトリメソッドの引数の形式で表されます(通常のコンストラクターの代わりにそれを使用する場合)。これらの依存関係は、Bean が実際に作成されるときに Bean に提供されます。
各プロパティまたはコンストラクターの引数は、設定する値の実際の定義、またはコンテナー内の別の Bean への参照です。
値である各プロパティまたはコンストラクター引数は、指定された形式からそのプロパティまたはコンストラクター引数の実際の型に変換されます。デフォルトでは、Spring は、ストリング形式で提供された値を、
int
、long
、String
、boolean
などのすべての組み込み型に変換できます。
Spring コンテナーは、コンテナーの作成時に各 Bean の構成を検証します。ただし、Bean が実際に作成されるまで、Bean プロパティ自体は設定されません。シングルトンスコープで事前インスタンス化(デフォルト)に設定された Bean は、コンテナーの作成時に作成されます。スコープは Bean スコープで定義されています。それ以外の場合、Bean はリクエストされたときにのみ作成されます。Bean を作成すると、Bean の依存関係とその依存関係の依存関係(など)が作成および割り当てられるため、Bean のグラフが作成される可能性があります。これらの依存関係間の解決の不一致は、遅れて、つまり、影響を受ける Bean を最初に作成したときに表示されることに注意してください。
一般的に Spring が正しいことをすることを信頼することができます。コンテナーのロード時に、存在しない Bean への参照や循環依存関係などの構成の課題を検出します。Spring は、Bean が実際に作成されたときに、プロパティを設定し、依存関係をできるだけ遅く解決します。つまり、正しくロードされた Spring コンテナーは、オブジェクトまたはその依存関係の 1 つを作成する際に問題が発生した場合に、オブジェクトをリクエストしたときに後で例外を生成できます。たとえば、Bean は、欠落または無効の結果として例外をスローします。プロパティ。いくつかの構成の課題のこの潜在的に遅延した可視性が、ApplicationContext
実装がデフォルトでシングルトン Bean を事前インスタンス化する理由です。これらの Bean を実際に必要になる前に作成するための事前の時間とメモリを犠牲にして、ApplicationContext
の作成時に、後でではなく、構成の課題を発見します。シングルトン Bean が先行して事前インスタンス化されるのではなく、遅延して初期化されるように、このデフォルトの動作をオーバーライドすることもできます。
循環依存関係が存在しない場合、1 つ以上の連携 Bean が依存 Bean に注入されるときに、各連携 Bean は依存 Bean に注入される前に完全に構成されます。これは、Bean A が Bean B に依存している場合、Spring IoC コンテナーは、Bean A で setter メソッドを呼び出す前に Bean B を完全に構成することを意味します。つまり、Bean はインスタンス化されます (事前にインスタンス化されたシングルトンでない場合)。)、その依存関係が設定され、関連するライフサイクルメソッド ( 構成された init メソッドや InitializingBean コールバックメソッドなど) が呼び出されます。
依存性注入の例
次の例では、setter ベースの DI に XML ベースの構成メタデータを使用しています。Spring XML 構成ファイルのごく一部は、次のようにいくつかの Bean 定義を指定しています。
<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"/>
次の例は、対応する ExampleBean
クラスを示しています。
Java
Kotlin
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;
}
}
class ExampleBean {
lateinit var beanOne: AnotherBean
lateinit var beanTwo: YetAnotherBean
var i: Int = 0
}
上記の例では、setter は XML ファイルで指定されたプロパティと一致するように宣言されています。次の例では、コンストラクターベースの DI を使用しています。
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
次の例は、対応する ExampleBean
クラスを示しています。
Java
Kotlin
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
class ExampleBean(
private val beanOne: AnotherBean,
private val beanTwo: YetAnotherBean,
private val i: Int)
Bean 定義で指定されたコンストラクター引数は、ExampleBean
のコンストラクターへの引数として使用されます。
ここで、コンストラクターを使用する代わりに、Spring が static
ファクトリメソッドを呼び出してオブジェクトのインスタンスを返すように指示されている、この例のバリアントを考えます。
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
次の例は、対応する ExampleBean
クラスを示しています。
Java
Kotlin
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
class ExampleBean private constructor() {
companion object {
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
@JvmStatic
fun createInstance(anotherBean: AnotherBean, yetAnotherBean: YetAnotherBean, i: Int): ExampleBean {
val eb = ExampleBean (...)
// some other operations...
return eb
}
}
}
static
ファクトリメソッドへの引数は、コンストラクターが実際に使用された場合とまったく同じように、<constructor-arg/>
要素によって提供されます。ファクトリメソッドによって返されるクラスの型は、static
ファクトリメソッドを含むクラスと同じ型である必要はありません(ただし、この例ではそうです)。インスタンス(非静的)ファクトリメソッドは(class
属性の代わりに factory-bean
属性を使用することを除いて)基本的に同じ方法で使用できるため、ここではそれらの詳細については説明しません。