Java 広告

【Java】SpringBootでDIコンテナを使ってクラスの注入を行ってみた(アノテーション編その2)

記事内に商品プロモーションを含む場合があります

今回はJavaでSpringBootを使ってDIコンテナを使ったクラスの注入を行います。

今回使うのはアノテーションというのもので、メソッドやクラスの前に@××××などの表記を追記するものです。

今回使うアノテーションは@Autowiredと@Qualifier、@Configuration、@Beanというアノテーションを使って実装してみます。

今回使うソースについて

今回使うソースは以下の6つです。

1.アプリケーションを起動するjavaソース(Article92Application.java)

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//JAVAベースのDIコンテナ利用
@SpringBootApplication
public class Article922Application {

	public static void main(String[] args) {
		SpringApplication.run(Article922Application.class, args);
	}
}

今回もこのソースに関してはDIコンテナに関する記述を行っていません。

2.DIコンテナを定義するためのクラス

package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import dayYoubiBean.Day2Youbi;

@Configuration
public class AppConfig {
	@Bean("day2Youbi")
	Day2Youbi day2Youbi() {
		return new Day2Youbi();
	}
}

このクラスで注目したいのは8行目の@Configurationと10行目の@Beanというアノテーションですね。

@Configurationを付けることで、DIコンテナの対象のクラスであることを宣言しておきます。

そして、@Beanですが、クラス内のDIコンテナの対象のメソッドとすると同時に、どんな名前で参照するかの定義をすることが出来ます。

この名前を、@Qualifierの引数に入れてあげることで、@Beanを付与したメソッドを呼び出すことが出来るようになります。

3.曜日を変換する処理を定義したクラス(ConvertYoubiController.java)

package com.example.demo;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import dayYoubiBean.Day2Youbi;

@Controller
@RequestMapping()
public class ConvertYoubiController {
	//DIコンテナ注入対象となるクラス
	public static Day2Youbi d2y;
	@Autowired
	@Qualifier("day2Youbi")
	public void setDay2Youbi(Day2Youbi day2Youbi) {
		d2y = day2Youbi;
	}
	//入力した西暦と年月日から曜日を取得する
	@PostMapping("/index/getYoubi")
	public String getYoubi(Frm form, Model model) {
		String retYoubi;
		//フォームから送信されたデータを受け取る
		String yyyyMMdd = form.getBirth();
		//受け取ったらLocalDate形式に変換する
		LocalDate ld = LocalDate.parse(yyyyMMdd, DateTimeFormatter.ofPattern("yyyyMMdd"));
		//曜日を取得
		DayOfWeek dow = ld.getDayOfWeek();
		
		// 日付から曜日を取得する
		retYoubi = d2y.Day2YoubiOpe(dow);
		
		//フォームに表示する情報をセットする
		model.addAttribute("youbi", retYoubi);
		model.addAttribute("birth", yyyyMMdd);
		return "convertYoubi";
	}
	@GetMapping("/index")
	public String index(Model model) {
		//フォームに表示する情報をセットする
		model.addAttribute("youbi", "");
		return "convertYoubi";
	}
}

このソースで注目していただきたいのが22行目の@Autowiredと23行目の@Qualifier(“day2Youbi”)です。

22行目の@Autowiredを追加することによりDIコンテナによりインスタンスを作成してくれます。

また、23行目の@Qualifierですが、DIコンテナを生成するクラスの@Beanで名前を付けたメソッドを呼び出すために使用しています(2.のソースで”day2Youbi”という名前でインスタンスを作成するメソッドを作成していますので、@Qualifierの引数にも同じように”day2Youbi”という名前を引数に設定しています)。

4.フォームで表示するためのGet、Setクラスの定義(Frm.java)

package com.example.demo;

public class Frm {
	//private String youbi = "";
	private String birth = "";
	
	public String getBirth() {
		return this.birth;
	}
	
	public void setBirth(String birth) {
		this.birth = birth;
	}
}

このクラスはフォームからの入力値を受け取り、ConvertYoubiControllerクラスのgetYoubiメソッドでの処理に利用し、再表示後もフォームの入力を保持するために定義しています。

5.西暦の年月日から曜日を計算するクラス(Day2Youbi.java)

package dayYoubiBean;

import java.time.DayOfWeek;

//DIコンテナ注入がなされるクラス

public class Day2Youbi{	
	//日付から曜日を判断する
	public String Day2YoubiOpe(DayOfWeek dow) {
		String retYoubi ="";
		
		// 日付から曜日を取得する
		switch (dow) { 
		    case SUNDAY: 
		    	retYoubi="日";
		        break;
		    case MONDAY: 
		    	retYoubi="月";
		        break;
		    case TUESDAY:
		    	retYoubi="火";
		        break;
		    case WEDNESDAY:
		    	retYoubi="水";
		        break;
		    case THURSDAY:
		    	retYoubi="木";
		        break;
		    case FRIDAY:
		    	retYoubi="金";
		        break;
		    case SATURDAY: 
		    	retYoubi="土";
		        break;
		    default:
		    	retYoubi="曜日取得失敗";     	
		}
		return retYoubi;
	}
}

このソースで定義されているクラスがDIコンテナによるインスタンス生成の対象となっているのですが、このソース自体に何もDIコンテナに関する記述がない所が、前回の方法と異なる部分です。

2.のAppConfigクラスでインスタンスを作成するメソッドを作成していて、DIコンテナの対象としているため、このソースには@Componetというアノテーションがついていないことに注目ですね。

6.画面表示用ページ(convertYoubi.html)

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>送信のデモ</title>
 </head>
  <body>
    <form action="#" th:action="@{/index/getYoubi}" th:object="${frm}" method="post">
      <table>
        <tr>
			<td>生年月日(19800101の形式で入力)</td>
			<td>
				<input type="text" maxlength="10" id="birth" name="birth" th:value="${birth}" />
			</td>
		</tr>
      </table>
      <table>
        <tr>
			<td th:text="あなたは + ${youbi} + 曜日に生まれました。">
			</td>
		</tr>
      </table>
          <input type="submit" value="送信">
    </form>

  </body>
</html>

画面の表示のためにHTMLで書かれたページです。

今回もThymeleafによりJava側から受け取った変数の内容を表示するようにしています。

動作の確認

では、上記で定義したプログラムを動かしてみることにします。

今回も前回と同様に西暦と年月日を入力すると曜日を計算してくれるプログラムになっています。

初期表示はこんな感じです。

適当に西暦と年月日を入力して、「送信」ボタンを押してみます。

送信ボタンを押すと、きちんと曜日が表示されました。

このことにより、Day2Youbi.javaで記載したクラスと処理がきちんとインスタンスとして生成され、動作していることが確認できました。

つまり、DIコンテナにより正常にインスタンスが注入され、プログラムが動いたということになります。

今回のプログラムの構成

今回は以下のようなソースの構成としました。

DIコンテナによるインスタンス注入のクラスを別パッケージにしたかったため、このような構成としました。

ただ、DIコンテナによるインスタンス注入の設定を行うクラスのソースは、メイン処理を行うソースと同じパッケージ内に存在しています。

これは、前回の時のようにこのソースを別パッケージに移動させると、
うまく@Autowiredで認識できない懸念があったためです。

今回のDIコンテナのインスタンス注入の書き方のメリット

今回はインスタンス注入を行うメソッドに名前を付けて、それを指定してDIコンテナによるインスタンスの注入を行っています。

@Beanにより複数のクラスのインスタンス注入が行えるようになりますので、かなり使い勝手が良く、XMLで書くよりも分かりやすく、応用が利きやすい書き方ではないかと思いました。

実際の開発現場で、1つのクラスで複数のクラスを参照することは良くあります。

そのため、この書き方は実用性が高く、開発現場でも使えるのではないかと思います。

今回のDIコンテナのインスタンス注入の書き方のデメリット

今回のアノテーションを使った書き方ですが、やはり@Autowiredを書くソースと@Configurationを書くソースは同じパッケージ内に存在しないと正しく動いてくれませんでした。

そのため、@Autowiredを書くクラスと@Configurationと@Beanを使ったDIコンテナの対象とするクラスは同一パッケージにいないといけないという制約があるようです。

この制約を超えてDIコンテナによるインターフェースの注入を行うのであれば、面倒ではありますがXMLを使った方法を使う必要があるのかなと思います。

ソースの書き方によってはこの問題を解消できるかもしれませんが、少し工夫が必要かもしれません。

今回の記事はここまでとなります。
また次の記事でお会いしましょう。