コンストラクタインジェクションとは
前回のプロパティ(Setter)インジェクションに続き、今回はコンストラクタを用いた依存性の注入方法について説明します。Spring Frameworkでは、Beanのインスタンス生成時に特定のコンストラクタを呼び出し、引数を通じて値を注入する仕組みが提供されています。これを「コンストラクタインジェクション」と呼びます。
1)対象クラスの定義
まず、依存性を受け取る側のクラスとしてStudentを作成します。この例では、Setterメソッドを意図的に省略し、初期化を完全にコンストラクタに限定することで、Setter注入との違いを明確にします。
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2)XMLによるBean定義
Springの設定ファイル(applicationContext.xmlなど)で、上記クラスのBeanを宣言します。<constructor-arg>要素を使用して、コンストラクタの各引数に対応する値を指定します。
<bean id="student" class="com.example.model.Student">
<constructor-arg>
<value>山田太郎</value>
</constructor-arg>
<constructor-arg>
<value>20</value>
</constructor-arg>
</bean>
3)実行と確認
ApplicationContextからBeanを取得し、正しく初期化されているかをテストします。
@Test
public void testConstructorInjection() {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student); // 出力: Student{name='山田太郎', age=20}
}
コンストラクタのオーバーロードと型の曖昧さ
複数のコンストラクタが存在する場合、特に引数の数が同じだとSpringはどのコンストラクタを使うべきか判断できなくなることがあります。以下のクラスを見てみましょう。
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name) {
this.name = name;
}
public Student(int age) {
this.age = age;
}
// toStringメソッドは省略
}
ここで、年齢(int型)のみを設定したい場合、次のように設定ファイルを書くのは危険です。
<bean id="student" class="com.example.model.Student">
<constructor-arg>
<value>21</value>
</constructor-arg>
</bean>
このままでは、Springは引数が一つのコンストラクタを呼び出そうとしますが、「String型のコンストラクタ」と「int型のコンストラクタ」のどちらを選ぶべきか判断できません。特にvalueに数値を書いても、Springはそれを自動的にintと見なすわけではなく、文字列として扱う可能性があります。
解決策:type属性の使用
このような曖昧さを解消するには、constructor-argにtype属性を追加して、期待する引数のデータ型を明示します。
<bean id="student" class="com.example.model.Student">
<constructor-arg type="int" value="21"/>
</bean>
これにより、Springは明確にpublic Student(int age)のコンストラクタを選択し、ageフィールドに21が設定されます。同様に、名前だけを設定したい場合はtype="java.lang.String"や単にtype="String"を指定すれば安全です。
複数引数の明示的指定
引数が複数ある場合でも、それぞれのconstructor-argにtypeを付けることで、正確なコンストラクタの選択が保証されます。
<bean id="student" class="com.example.model.Student">
<constructor-arg type="java.lang.String" value="佐藤花子"/>
<constructor-arg type="int" value="19"/>
</bean>