Spring @Configuration と @Component アノテーションの違いを見てみよう

Spring @Configuration と @Component アノテーションの違いを見てみよう

2024年7月9日

Springで依存性(Beanクラスとも呼ばれる)を登録する方法は大きく2つある。すぐに説明する@Configuration@Componentである。

Beanは以前の投稿で説明したように、Spring IoC Containerで管理されるオブジェクトを指す。

@Configuration@ComponentはそのBeanを登録する方法である。この2つの違いについて調べてみよう。

@Componentを使う

@ComponentはSpring IoC ContainerにBeanを登録する方法の1つである。

以下に3つのコンポーネントを登録する方法を見てみよう。

Computer.java
@Component
public class Computer {
	MainBoard mainBoard;
	CPU cpu;

	public Computer(MainBoard mainboard, CPU cpu) {
		this.cpu = cpu;
		this.mainBoard = mainBoard;
	}

	void printComputerInfo() {
		System.out.println("CPU Name:" + cpu.name);
		System.out.println("CPU Manufacturer:" + cpu.manufacturer);
		System.out.println("Main board Name:" + mainBoard.name);
		System.out.println("Main board Manufacturer:" + mainBoard.manufacturer);
	}
}
MainBoard.java
@Component
public class MainBoard {
	String name; // 製品名
	String manufacturer; // 製造業者

	// コンストラクタ関数
	public MainBoard() {
		this.name = "B760M Pro RS D5";
		this.manufacturer = "ASRock";
	}
}
CPU.java
@Component
class CPU {
	String name; // 製品名
	String manufacturer; // 製造業者

	// コンストラクタ関数
	public CPU() {
		this.name = "Ryzen7 PRO 8700GE";
		this.manufacturer = "AMD";
	}
}

このようにすると、IoCコンテナにそれぞれComputerMainBoardCPUクラスが個別のBeanとして登録される。

これらのコンポーネントはアプリケーション実行時点で依存性が登録され、3つのクラスがそれぞれ依存性を注入されることを図にすると以下の通りである。

image

@Configuration@Beanを使う

@ConfigurationもSpring IoC ContainerにBeanを登録する方法の1つである。

@Componentで宣言した場合、メンバ変数として宣言されたname、manufacturerなどを事前に初期化する必要がある。もしString型の変数を初期化しなかった場合、以下のようなエラーが発生する。

Field name in com.example.demo.product.domain.MainBoard required a bean of type 'java.lang.String' that could not be found.

もちろんString型のBeanを登録すればエラーは発生しないが、String自体は非常に広範であるため、Beanとして登録して使用するには適していない。

では、メンバ変数を必ず事前に初期化しなければならないのか?

そうではない。

@Configurationを使用すると、コンストラクタメソッドで引数を保持しながらBeanを登録することができる。

以下の3つの説明したクラスは以前に定義した@Componentを外し、コンストラクタに各クラスの引数を受け取るように初期化する。

Computer.java
public class Computer {
	MainBoard mainBoard;
	CPU cpu;

	public Computer(MainBoard mainboard, CPU cpu) {
		this.cpu = cpu;
		this.mainBoard = mainBoard;
	}

	void printComputerInfo() {
		System.out.println("CPU Name:" + cpu.name);
		System.out.println("CPU Manufacturer:" + cpu.manufacturer);
		System.out.println("Main board Name:" + mainBoard.name);
		System.out.println("Main board Manufacturer:" + mainBoard.manufacturer);
	}
}
MainBoard.java
public class MainBoard {
	String name; // 製品名
	String manufacturer; // 製造業者

	// コンストラクタ関数
	public MainBoard(String name, String manufacturer) {
		this.name = name;
		this.manufacturer = manufacturer;
	}
}
CPU.java
class CPU {
	String name; // 製品名
	String manufacturer; // 製造業者

	// コンストラクタ関数
	public CPU(String name, String manufacturer) {
		this.name = name;
		this.manufacturer = manufacturer;
	}
}

そして、ComputerConfigというクラスを作成して@Configurationを宣言し、@Beanを通じて各クラスのBeanを登録する。

ComputerConfig.java
@Configuration
public class ComputerConfig {
    @Bean
    public Computer computer(MainBoard mainBoard, CPU cpu) {
        return new Computer(mainBoard, cpu);
    }

    @Bean
    public MainBoard mainBoard() {
        return new MainBoard("B760M Pro RS D5", "ASRock");
    }

    @Bean
    public CPU cpu() {
        return new CPU("Ryzen7 PRO 8700GE", "AMD");
    }
}

このように@Configurationを使うと、コンストラクタメソッドで引数を保持しつつBeanを登録することができる。

このようにBeanを登録する方法の違いであって、実際には技術的に大きな違いはない。

それぞれはいつ使われるのか?

@Componentは独自実装したコンポーネントについて多く使用される傾向がある。たとえば、@Controller@Repository@Serivceなどがユーザーが実装する代表的なコンポーネントの例である。

@Configurationは名前の通り、外部クラスの設定をBeanとして登録したいときに使用される。

たとえば、@Configurationを使用してDataSourceをBeanとして登録すると、application.yamlを使用しなくてもDataSourceをBeanとして登録することができる。(実際にはapplication.yamlもyamlを読み込んでBeanとして登録する方法を取っており、これをSpringではAutoConfigurationと呼んでいる。)

DataSourceConfig.java
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/test")
                .username("root")
                .password("password")
                .build();
    }
}