山崎屋の技術メモ

IT業界で働く中でテクノロジーを愛するSIerのシステムエンジニア👨‍💻 | AndroidとWebアプリの二刀流🧙‍♂️ | コードの裏にあるストーリーを綴るブログ執筆者✍️ | 日々進化するデジタル世界で学び続ける探究者🚀 | #TechLover #CodeArtisan、気になること、メモしておきたいことを書いていきます。

【Spring】@Autowired と @Component を使用した DI の基本

@Component と @Autowired を使用して、 Spring Framework の中心とも言える DI ( Dependency injection : 依存性の注入 ) の基本について学習する。まずは最小限の簡単なサンプル。

@Component
public class ClassA {
	
	@Autowired
	private ClassA classA;

}

@Component は インスタンスを Spring 管理下におくため、クラスに付けるアノテーション。

@Autowired は Spring 管理下のオブジェクトの中から、適切なものを変数に自動でセットしてもらうため、インスタンス変数に付けるアノテーションである。

SpringMVC や Spring Boot では、慣習的に @Component の変わりに @Controller、@Service、@Repository を使用する。

慣習的にと言ったのは、これら3つのアノテーションは、すべて @Component を継承しており、ソースコードレベルでの差異はないからである。

ソースのリンクを貼っておく。

Component

Controller

Service

Repository

では早速 DI のサンプルを作っていこう。
 
 

フォルダ構成

フォルダ構成は以下のとおり。

[org.yyama.bean]パッケージ配下の2つのクラスを Spring のコンテナに管理させる。Main クラスは Spring の管理外にする。
 
 

ソース

まず、applicationContext.xml。org.yyama.bean 配下を Spring がスキャンして、管理下とする bean を探す設定にしている。「<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.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="org.yyama.bean" />
</beans>

クラスA。

package org.yyama.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassA {

	@Autowired
	private ClassB classB;

	public void proc() {
		classB.print();
	}

}

@ComponentでSpringコンテナに管理させるクラスとして宣言している。@Autowired を使用してクラスBのインスタンスを Spring で自動的にセットしてもらう。


クラスB。

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class ClassB {
	public void print() {
		System.out.println("ClassBのprint()メソッドです。");
	}
}

@Component で Springコンテナに管理させるクラスとして宣言している。


最後に Main クラス。

package org.yyama;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.yyama.bean.ClassA;

public class Main {
	public static void main(String... args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		ClassA classA = ctx.getBean(ClassA.class);
		classA.proc();
		ctx.close();
	}
}

コンテナから、ClassA のインスタンスを取り出して proc() メソッドを実行する。このとき ClassA のプロパティ classB には Spring が自動で ClassB のインスタンスをセットしてくれるはずだ。
 
 

実行結果

ClassBのprint()メソッドです。

期待通り、クラスBの print() メソッドが呼び出されて"ClassBのprint()メソッドです。"というメッセージが表示されている。

@Autowired アノテーションをプロパティに記述しておくと、Spring がコンテナの中からその「プロパティの型に合うクラス」のインスタンスを裏で new して返してくれるというわけだ。

ん?、同じ型が2つ以上あった場合、Spring はどのインスタンスを DI すればいいか迷ってしまうのではないか?

その場合の対処方法は以下の記事を参考にして欲しい。

yyama1556.hateblo.jp

何がうれしいのか

「ClassA の中で ClassB を普通に new すればいいじゃないか」という NEW おじさんの声が聞こえてきた。確かに小さいプログラムであれば Spring など使用しないで new したほうが早い。

ある程度のアプリケーションになると、以下のような状況が考えられる。

・ClassB がまだできていないので ClassA のテストができない。

・ClassB が DB アクセスを行うので、ClassA を単体テストしたいだけなのに DB 構築が必要。

・ClassB を改良した NewClassB が発明された。ClassA を修正することなく NewClassB を使用したい。

このような場合、設定ファイルを修正するだけで、自動的にプロパティ classB には Mock が設定されるなど直接プログラムの修正を行わずに、依存するクラスを変更できる。

上記含め DI のメリットとして以下のようにまとめられる。

  • 疎結合 (Loose Coupling): コンポーネント間の依存関係が最小限に抑えられるため、システムの各部分を独立して開発、テスト、保守することが可能になる。
  • 再利用性の向上: 同じコンポーネントやサービスを異なるアプリケーションで再利用しやすくなる。
  • コードのテスト容易性: 疎結合により、ユニットテストが容易になり、モックオブジェクトなどを用いたテストがしやすくなる。
  • コードの管理性向上: DIを利用すると、依存関係が中央で管理されるため、コードが整理され、管理しやすくなる。
  • 構成の柔軟性: コンポーネント間の依存関係がコード内に硬直化されず、設定ファイルなどを通じて柔軟に変更できる。
  • スコープ管理の自動化: Springはシングルトン、プロトタイプなどの異なるスコープのビーンを自動的に管理し、ライフサイクルを適切にハンドリングできる。

 

まとめ

Springは当初 DI コンテナとして生まれてきた。その後、周辺のいろいろな機能が追加された。
 
その各機能でも DI の機能を利用することになるので、DI については、自分でサンプルを組んでしっかり習熟しておきたい。


Spring 関連記事へのリンク集つくりました。