山崎屋の技術メモ

IT業界で働く中で、気になること、メモしておきたいことを書いていきます。

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

@Component と @Autowired を使用して、 Spring Framework の中心とも言える DI の基本について簡単なサンプルを作成して学習する。

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

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

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

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

Component

Controller

Service

Repository

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

フォルダ構成

フォルダ構成は以下のとおり。
f:id:yyama1556:20160809172015p:plain

[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 のインスタンスをセットしてくれるはずだ。

実行結果

8 09, 2016 5:33:05 午後 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Tue Aug 09 17:33:05 JST 2016]; root of context hierarchy
8 09, 2016 5:33:06 午後 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
ClassBのprint()メソッドです。
8 09, 2016 5:33:06 午後 org.springframework.context.support.ClassPathXmlApplicationContext doClose
情報: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Tue Aug 09 17:33:05 JST 2016]; root of context hierarchy

期待通り、クラス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 には Mock が設定されるなどプログラムの修正を行わずに、依存するクラスを変更できる。

まとめ

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


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