読者です 読者をやめる 読者になる 読者になる

山崎屋の技術メモ

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

【Spring Framework】簡単な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

<?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して返してくれるというわけだ。

何がうれしいのか

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

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

・ClassBがまだできていない。

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

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

まとめ

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


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