@Configuration アノテーションを使用する

@Configuration は、オブジェクトが Bean 定義のソースであることを示すクラスレベルのアノテーションです。@Configuration クラスは、@Bean アノテーション付きメソッドを介して Bean を宣言します。@Configuration クラスでの @Bean メソッドの呼び出しを使用して、Bean 間の依存関係を定義することもできます。一般的な導入については、基本概念: @Bean および @Configuration を参照してください。

Bean 間の依存関係の注入

Bean が相互に依存関係を持っている場合、その依存関係の表現は、次の例に示すように、ある Bean メソッドが別の Bean メソッドを呼び出すのと同じくらい簡単です。

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public BeanOne beanOne() {
		return new BeanOne(beanTwo());
	}

	@Bean
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun beanOne() = BeanOne(beanTwo())

	@Bean
	fun beanTwo() = BeanTwo()
}

上記の例では、beanOne はコンストラクターインジェクションを通じて beanTwo への参照を受け取ります。

Bean 間依存関係を宣言するこの方法は、@Bean メソッドが @Configuration クラス内で宣言されている場合にのみ機能します。プレーンな @Component クラスを使用して Bean 間の依存関係を宣言することはできません。

ルックアップメソッドインジェクション

前述したように、ルックアップメソッドインジェクションは高度な機能であり、めったに使用しないでください。これは、シングルトンスコープの Bean がプロトタイプスコープの Bean に依存している場合に役立ちます。この型の構成に Java を使用すると、このパターンを実装するための自然な手段が提供されます。次の例は、ルックアップメソッドインジェクションの使用方法を示しています。

  • Java

  • Kotlin

public abstract class CommandManager {
	public Object process(Object commandState) {
		// grab a new instance of the appropriate Command interface
		Command command = createCommand();
		// set the state on the (hopefully brand new) Command instance
		command.setState(commandState);
		return command.execute();
	}

	// okay... but where is the implementation of this method?
	protected abstract Command createCommand();
}
abstract class CommandManager {
	fun process(commandState: Any): Any {
		// grab a new instance of the appropriate Command interface
		val command = createCommand()
		// set the state on the (hopefully brand new) Command instance
		command.setState(commandState)
		return command.execute()
	}

	// okay... but where is the implementation of this method?
	protected abstract fun createCommand(): Command
}

Java 構成を使用することにより、CommandManager のサブクラスを作成できます。ここで、抽象 createCommand() メソッドは、新しい(プロトタイプ)コマンドオブジェクトを検索するようにオーバーライドされます。次の例は、その方法を示しています。

  • Java

  • Kotlin

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
	AsyncCommand command = new AsyncCommand();
	// inject dependencies here as required
	return command;
}

@Bean
public CommandManager commandManager() {
	// return new anonymous implementation of CommandManager with createCommand()
	// overridden to return a new prototype Command object
	return new CommandManager() {
		protected Command createCommand() {
			return asyncCommand();
		}
	}
}
@Bean
@Scope("prototype")
fun asyncCommand(): AsyncCommand {
	val command = AsyncCommand()
	// inject dependencies here as required
	return command
}

@Bean
fun commandManager(): CommandManager {
	// return new anonymous implementation of CommandManager with createCommand()
	// overridden to return a new prototype Command object
	return object : CommandManager() {
		override fun createCommand(): Command {
			return asyncCommand()
		}
	}
}

Java ベースの構成が内部的に機能する方法に関する詳細情報

@Bean アノテーション付きメソッドが 2 回呼び出されることを示す次の例を検討してください。

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public ClientService clientService1() {
		ClientServiceImpl clientService = new ClientServiceImpl();
		clientService.setClientDao(clientDao());
		return clientService;
	}

	@Bean
	public ClientService clientService2() {
		ClientServiceImpl clientService = new ClientServiceImpl();
		clientService.setClientDao(clientDao());
		return clientService;
	}

	@Bean
	public ClientDao clientDao() {
		return new ClientDaoImpl();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun clientService1(): ClientService {
		return ClientServiceImpl().apply {
			clientDao = clientDao()
		}
	}

	@Bean
	fun clientService2(): ClientService {
		return ClientServiceImpl().apply {
			clientDao = clientDao()
		}
	}

	@Bean
	fun clientDao(): ClientDao {
		return ClientDaoImpl()
	}
}

clientDao() は clientService1() で 1 回、clientService2() で 1 回呼び出されています。このメソッドは ClientDaoImpl の新しいインスタンスを作成して返すため、通常は 2 つのインスタンス(各サービスに 1 つ)が必要です。それは間違いなく問題になるでしょう: Spring では、インスタンス化された Bean はデフォルトで singleton スコープを持ちます。これが魔法の出番です。すべての @Configuration クラスは、起動時に CGLIB でサブクラス化されます。サブクラスでは、子メソッドは、親メソッドを呼び出して新しいインスタンスを作成する前に、キャッシュされた(スコープされた)Bean のコンテナーを最初にチェックします。

動作は、Bean のスコープに応じて異なる場合があります。ここでシングルトンについて話しています。

CGLIB クラスは org.springframework.cglib パッケージに再パッケージ化され、spring-core JAR 内に直接含まれるため、クラスパスに CGLIB を追加する必要はありません。

CGLIB は起動時に機能を動的に追加するため、いくつかの制限があります。特に、構成クラスは final であってはなりません。ただし、構成クラスでは、@Autowired の使用やデフォルトインジェクション用の単一の非デフォルトコンストラクター宣言など、任意のコンストラクターを使用できます。

CGLIB によって課される制限を回避したい場合は、非 @Configuration クラス (たとえば、プレーンな @Component クラス) で @Bean メソッドを宣言するか、構成クラスに @Configuration(proxyBeanMethods = false) のアノテーションを付けることを検討してください。@Bean メソッド間のクロスメソッド呼び出しはインターセプトされないため、コンストラクターまたはメソッドレベルでの依存性注入のみに依存する必要があります。