ここでは、SpringやJavaEE等のDIコンテナを使わずに、自作アノテーションを使って 依存性注入(DI) を行う方法を紹介します。
リフレクションを利用して、クラスのフィールドにアノテーションが付いているかをチェックし、自動でインスタンスを注入する仕組みを作ります。
1. 環境
- java:openjdk version "21.0.5"
C:\pleiades\2024-12\java\21\bin>java -version
openjdk version "21.0.5" 2024-10-15 LTS
OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing)
C:\pleiades\2024-12\java\21\bin>
2. 作成するファイル
作成するファイルは以下となります。
No | ファイル名 | 説明 |
---|---|---|
1 | MyAutowired.java | カスタムアノテーション@MyAutowired 、DIを示す自作アノテーション |
2 | HelloService.java | DI対象のクラス |
3 | MyController.java |
@MyAutowired を使ってDIを適用するクラス |
4 | MyDIContainer.java | 自作のDIコンテナ |
5 | Main.java | 動作確認 |
3. 簡単な自作DIを作成
3.1. 自作アノテーションを作成
まず、DIの対象であることを示す@MyAutowired
アノテーションを作成します。
package di;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 実行時にアノテーションを保持
@Target(ElementType.FIELD) // フィールドに適用
public @interface MyAutowired {
}
3.2. DI の対象となるクラス
依存関係としてインジェクトするクラスを作成します。
package di;
public class HelloService {
public void sayHello() {
System.out.println("Hello, Custom DI!");
}
}
このHelloService
クラスのインスタンスを、@MyAutowired
の付いたフィールドに自動注入します。
3.3. 自作の DI コンテナ(MyDIContainer)を作成
次に、@MyAutowired
をスキャンし、対象のクラスにインスタンスを注入するMyDIContainer
クラスを作成します。
package di;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class MyDIContainer {
private Map<Class<?>, Object> beans = new HashMap<>();
public MyDIContainer() {
// 依存オブジェクトを手動で登録(実際のフレームワークでは自動スキャン)
beans.put(HelloService.class, new HelloService());
}
public void injectDependencies(Object object) {
Class<?> clazz = object.getClass(); // 渡されたオブジェクトのクラス情報を取得
for (Field field : clazz.getDeclaredFields()) { // クラスのフィールドを取得
if (field.isAnnotationPresent(MyAutowired.class)) { // フィールドに @MyAutowired があるかチェック
Class<?> fieldType = field.getType(); // フィールドの型を取得
Object dependency = beans.get(fieldType); // 依存オブジェクトを beans から取得
if (dependency != null) {
field.setAccessible(true); // private フィールドでもアクセスできるようにする
try {
field.set(object, dependency); // フィールドにインスタンスをセット
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to inject dependency", e);
}
}
}
}
}
}
injectDependencies()
メソッドは、オブジェクトのフィールドをスキャンし、@MyAutowired
が付いたフィールドに適切なインスタンスを注入します。
3.4. @MyAutowired
を使ってDIを適用するクラス
次に、@MyAutowired
を利用してHelloService
を DIするクラスを作成します。
package di;
public class MyController {
@MyAutowired
private HelloService helloService; // 自動 DI
public void execute() {
helloService.sayHello(); // HelloService のメソッドを実行
}
}
通常の @Autowired
のように、@MyAutowired
でHelloService
を自動DIできます。
3.5. 動作確認のためのMain
クラスを作成
最後に、MyDIContainer
を使ってMyController
のインスタンスを作成し、依存関係を注入します。
package di;
public class Main {
public static void main(String[] args) {
MyDIContainer container = new MyDIContainer(); // DI コンテナを作成
MyController controller = new MyController(); // コントローラを作成
container.injectDependencies(controller); // 依存関係を注入
controller.execute(); // 実行
}
}
4. 実行結果
Main.java
を実行すると、期待通り、MyControllerにHelloServiceが自動的にDIされ、期待通りに動作することを確認できました。
C:\pleiades\2024-12\workspace\git\java\bin>C:\pleiades\2024-12\java\21\bin\java.exe di.Main
Hello, Custom DI!
C:\pleiades\2024-12\workspace\git\java\bin>
以上