エンジニアのはしがき

プログラミングの日々の知見を書き連ねているブログです

SpringでInterfaceを@Autowiredするだけで動作するのはなぜ?

JavaC#は似て非なるものだと日に日に感じる今日この頃です。

悩んだこと

Springでは@Autowiredを使うことで、インスタンス管理をDIコンテナ側に任せることができ、クラス同士を疎結合に保つことができるようになっています。

ある時、私が見たコードでは@Autowiredがクラスではなくインターフェースに付けられていました。 例として挙げると以下のようなコードです。

public interface FugaInterface {
    public void hello();
}
@Service
public class FugaClass implements FugaInterface {
    @Override
    public void hello() {
        System.out.println("hello!");
    }
}
public class HogeClass {
    @Autowired
    private FugaInterface fuga;

    public void hello() {
        fuga.hello();
    }
}

悩んだのは、何故fuga.hello()が実装クラスであるFugaClassクラスのメソッドと紐づけられるのか、ということでした。 あくまで@AutowiredされているのはFugaInterfaceインターフェースであって、実装クラスではないじゃないか!と思っていました。

結論

  • Springではインターフェースを@Autowiredした場合、そのインターフェースを実装したクラスを自動的に探して@Autowiredしてくれる

  • もし同じインターフェースを実装したクラスが複数ある場合は、実行時にエラーが発生する

    • 明示的に実装クラスの指定が必要になる

なるほど、そんな仕様があったとは…🤔

実装するクラスを指定する場合

同じインターフェースを実装するクラスが複数ある場合は、@ServicevalueにIDを指定し、@Autowiredする際に一緒に@Qualifierを付与します。@Qualifierには参照したい実装クラスのIDを指定します。

public interface FugaInterface {
    public void hello();
}
@Service(value="fuga")
public class FugaClass implements FugaInterface {
    @Override
    public void hello() {
        System.out.println("hello!");
    }
}

@Service(value="piyo")
public class PiyoClass implements FugaInterface {
    @Override
    public void hello() {
        System.out.println("fuck you!");
    }
}
public class HogeClass {
    @Autowired
    @Qualifier("fuga")
    private FugaInterface fuga;

    public void hello() {
        fuga.hello();
    }
}

上記のコードでは、fuga.hello();を呼ぶとSystem.out.println("hello!");が実行されます。