アノテーションインジェクションによるDI
どんなときに使うの?
リソースの作成、後処理をクライアントコードに任せずに基盤コード側で実行、制御したいとき。
他のインジェクション方式との比較
コンストラクタインジェクションやセッターインジェクションではリソースの作成、後処理がクライアントコード側の責任になり、処理が様々なクラスに分散してしまう。
アノテーションインジェクションであれば基盤側で集中制御できる。
クライアント側のコード
こんな感じ。
@Resource(type=SocketMAAPI) public Socket sock;
アノテーションを宣言する
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Resource { public ResourceType type(); }
注入する
クライアントクラスのインスタンスから全フィールドを取得する。
Field[] fields = clientObject.getClass().getDeclaredFields();
フィールドのアノテーションをすべて取得する。
Annotation[] annotations : field.getDeclaredAnnotations()
取得したアノテーションの中からResourceアノテーションを探す。
if(annotation instanceof Resource) {
あればそのフィールドにリソースを設定する。
filed.set(clientObject, sock);
クライアントインスタンスの管理には弱参照を使う
注入する側はクライアントインスタンスを管理しておく必要がある。
- 再度リソース割当て要求がきたときに重複する複数のリソースを作成・割り当てないため
- 再度割当て要求がきたときに重複する複数の弱参照を作成しないため
しかし基盤側がMapなどを作成してクライアントインスタンスの強参照を持ち続けると不都合がが発生する。
クライアントインスタンスの管理には弱参照を使うことが大事。
リソース解放はファントム参照を利用する
クライアントインスタンスに割り当てたリソースをクライアントインスタンス終了後に解放するにはなんらかの方法でクライアントインスタンスが終了したことを検知する必要がある。
ファントム参照を利用することができる。
まずファントム参照のコンストラクタにReferenceQueueを渡す。
private ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantom = new PhantomReference<>(clientObj, queue);
clientObjインスタンスが終了するとファントム参照はqueueキューに入る。キューに入ったことをpollメソッドやremoveメソッドで検知してリソース解放処理を実施する。
PhantomReference<Object > zombie = (PhantomReference<Object>) queue.remove(1000);
最後にファントム参照をクリアしてGCの回収対象にする。
zombie.clear()