DIを使う場合、基本的にインスタンスをシングルトンで使う事が前提になる。

普通のオブジェクト指向的にデータと処理をまとめたクラスからDIを前提としたクラスを使う場合、インスタンス生成をどうするのか、DIした場合とnewした場合とで同じインスタンスが取得できるのか調べた。

結論から言うと、newするとDIで生成したクラスとは別のインスタンスになる模様。つまりシングルトン前提のクラスを通常のクラスから正しく利用することはできないらしい。

ファクトリクラス/メソッドを使う方法

よく考えたら別にnewを使う必要は無く、DIしたクラスを利用するクラスをファクトリクラスで生成すればいい。ただファクトリクラス自身はDIで取得したインスタンスを使う必要がある。

コントローラ

package com.example.singleton;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	private final MyLogicFactory myLogicFactory;

	public TestController(MyLogicFactory myLogicFactory) {
		this.myLogicFactory = myLogicFactory;
	}

	@RequestMapping("/")
	public String index() {
		MyLogic myLogic = myLogicFactory.of();
		// myLogicを使った処理

		return "ok";
	}

}

利用したいBean

package com.example.singleton;

public class TestBean {
	// 固有の処理
}

Beanのコンフィグレーションクラス

package com.example.singleton;

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

@Configuration
public class TestConfig {

	@Bean
	public TestBean testBean() {
		return new TestBean();
	}

}

TestBean利用クラス

package com.example.singleton;

public class MyLogic {

	private final TestBean testBean;

	public MyLogic(TestBean testBean) {
		this.testBean = testBean;
	}

	// TestBeanを使った処理

}

TestBean利用クラスのファクトリクラス

package com.example.singleton;

import org.springframework.stereotype.Component;

@Component
public class MyLogicFactory {

	private final TestBean testBean;

	public MyLogicFactory(TestBean testBean) {
		this.testBean = testBean;
	}

	public MyLogic of() {
		return new MyLogic(testBean);
	}

}

フレームワーク内でインスタンスが生成されるなどこちらが手を出せない場合は駄目だが、通常の利用方法であれば利用できる。

ちなみにSpring Bootを使う前はPlayframeworkをよく使っていたが、バージョン2.5でDIが導入されたときにフォームクラスの生成方法が変わった理由が今更分かった気がする。

今回使ったサンプル

https://github.com/orimajp/springboot-study/tree/master/singleton