Autowired

Autowiredによる依存性の注入(昭和のプログラマにはこの言葉がピンとこない)は、ぶっちゃけ

「アプリフレームワークの初期化のときやクラス(=Bean)の初期化のときに、jarの中の@Autowiredのラベルのついている変数を探して、存在したら、nullかどうかチェックして、nullだったら対象のシングルトンインスタンスが作られているかどうかを探して、作られていなかったらそれを先に初期化して、対象インスタンスのポインタをjarの実行中のバイトコードの変数に直に上書きしてしまう」

という話。JavaBeansは初期化時の規則が定まっているので、そういう初期化プロセスまでフレームワーク側ががっつりハックできるという話なのだが、実行時のバイトコード内の変数の位置を実行時に探索して、後付けで値を入れてしまうというのは自分らの世代では「OS以外やっちゃいかんこと」という先入観があるのでどうしても「え? なんで」となってしまう。

実行時にスタックポインタに直書きしてプログラムの飛び先を変える古いウイルスのような挙動とか、デバッガで実行中のアプリを一時停止してメモリを書き換えてプログラムの飛び先を変えるとか、アプリではそれやると反則だよね(そんなコード書いたら袋だたきだよね)というイメージが古いプログラマにはある。でもそれをJavaアノテーション仕様とBeanの仕組みでルール範囲でハックして実現して、現実として「便利だよね」という状況を作ったら認めざるを得ない。すべては「ソースコードをビジネスロジックの仕様記述のみにする」という今風の哲学によるものだからだ。

「nullにいつのまにか値が入っていても、便利だからいいじゃないか。業務ロジックを書いているときに初期化なんて考えたくないし、何より目に邪魔だから見たくない(見えないところでやってくれ)」というコンセプト。

あとは、細部まで追った訳ではないが簡単に調べた部分だけ。

Spring Boot サンプルコード

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  @Autowired
  private AdderService autowiredAdderService;

  @GetMapping("/hello")
  public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
    return String.format("Hello %s!", name);
  }
}

デバッグポインタをAdderServiceのコンストラクタに設定してスタックを追う。

DemoApplication 初期化時にAutowiredを探索
autowiredする要素(inject)を登録する
inject要素を作る
存在していなかったらシングルトンインスタンスを作っちゃう

※ @Autowiredによる紐付けは最近は推奨されないそうだ。 http://pppurple.hatenablog.com/entry/2016/12/29/233141 言ってることはわかるので今度Springを使うときには参考にしよう。