25
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【初心者向け】今度こそわかるDI〜DIの基礎とSpringにおけるDI〜

Posted at

はじめに

DIとはDependency Injectionの略であり、日本語では「依存性の注入」と訳されることが多いです。

新卒だったころにSpringに触れた私は、初めてこの概念を聞いた時に頭に浮かんだのは、はてなマークでした。DIの概念やDIコンテナ、Springにおけるアノテーションとの結びつきがピンとこなかったのです。

今回は初心者だったころの自分にあてて、DIの概念およびSpringにおけるDIについて自分なりに整理しつつ、簡単にまとめていきたいと思います。

##依存性(Dependency)とは
依存(性)とは何かということはソースコードで示したほうがわかりやすいと思います。

Main.java
public class Main {
    public int hoge() {
        var sub = new Sub();
        return sub.calculate();
    }
}

Mainクラスのhogeメソッド内で、Subクラスのインスタンスを生成し、利用しています。
このようなMainクラスがあったとき、「MainクラスはSubクラスに依存している」と表現します。

依存に関しては比較的わかりやすいと思います。

注入(Injection)とは

先程の例では、Subクラスのインスタンスを内部で生成していましたが、インスタンスを外部で生成し、それを用いるクラスに渡してあげることを注入と呼びます。

ここでは、コンストラクタを用いた例(コンストラクタインジェクション)を示します。

Main.java
public class Main {
    
    private Sub sub;
    
    public int hoge() {
        return sub.calculate();
    }
    
    // コンストラクタインジェクション
    public Main(Sub sub) {
        this.sub = sub;
    }
}

先程はhogeメソッド内でSubクラスのインスタンスを生成していましたが、今回の例では、インスタンスをコンストラクタから受け取るようになっています。

このようにすることで、Mainクラスを実際に用いる際は、

var sub = new Sub();
var main = new Main(sub);

といった形で、SubクラスのインスタンスをMainクラスの外から渡す形になります。

これによるメリットは後述します。

DIコンテナとは

コンテナと聞くと現代ではDockerを連想するかもしれませんが、DIコンテナにおけるコンテナはDockerなどの文脈で用いられるコンテナと異なります。

誤解を恐れず一言で書くとすると、DIコンテナは「注入するインスタンスを管理してくれる入れ物」の役割をしてくれます。

先程のようにDIコンテナを用いずに、Mainクラスを用いようとすると、用いるたびにSubクラスのインスタンスが必要になります。

DIコンテナを用いると、このインスタンスを管理してくれるので、いちいちインスタンスを生成する必要がなくなります。

SpringにおけるDIコンテナ

Springではアノテーションでクラスを指定することで、そのクラスのインスタンスをDIコンテナで管理する対象として指定することができます。

具体的には以下のようなアノテーションです。

  • @Controller
  • @Service
  • @Repository
  • @Bean

ちなみに、DIコンテナで管理されているインスタンスはApplicationContextクラスのgetBeanメソッドを用いることで、取得することができます。

また、SpringではDIコンテナがインスタンスをいつまで管理するかという、スコープを設定することができます。

設定できるスコープは下記のとおりで、設定する際はScopeアノテーションを用います。

  • singleton
  • prototype
  • request
  • session
  • global session

Springのデフォルトはsingletonになっていますが、設定を変更することにより、例えばsession単位で、コンテナに管理されているインスタンスを破棄し、再設定するといったことが可能になります。

初心者がやりがちな注意点としては、singletonにもかかわらず、クラスに変化しうるフィールドを持たせてしまうことです。以下に例を示します。

hogeService
@Service
public class hogeService {
    
    private String result;
    
    public int fuga(String suffix) {
        result = "test" + suffix;        
        return result;
    }
}

hogeServiceにはServiceアノテーションが付与されており、特にスコープを設定していないので、スコープがsingletonになります。

そのため、resultフィールドを別のセッションで変更してしまう危険性があり、スレッドセーフでなくなってしまいます。

下記のようにクラスフィールドではなく、変数にしてあげることで、スレッドセーフな実装になります。

hogeService
@Service
public class hogeService {
    
    public int fuga(String suffix) {
        String result = "test" + suffix;        
        return result;
    }
}

SpringにおけるDI

Springでは、Autowiredアノテーションを用いることで、DIすることができます。

インジェクションの方法には先程のコンストラクタインジェクションも含め、下記の3つがあります。

  • フィールドインジェクション
  • セッターインジェクション
  • コンストラクタインジェクション

それぞれ具体的には、このようになります。

Main.java
public class Main {
    
    // フィールドインジェクション
    @Autowired
    private Sub sub;
    
    public int hoge() {
        return sub.calculate();
    }
}
Main.java
public class Main {
    
    private Sub sub;
    
    public int hoge() {
        return sub.calculate();
    }
    
    // セッターインジェクション
    @Autowired
    public setSub(Sub sub) {
        this.sub = sub;
    }
}
Main.java
public class Main {
    
    private Sub sub;
    
    public int hoge() {
        return sub.calculate();
    }
    
    // コンストラクタインジェクション
    @Autowired
    public Main(Sub sub) {
        this.sub = sub;
    }
}

Autowiredアノテーションを付与することで、フィールド、メソッドおよびコンストラクタの引数にDIコンテナからインスタンスを注入してくれます。

ちなみに自分がインジェクションする際は、こちらの記事にあるように、ライブラリのLombokのRequiredArgsConstructorアノテーションを用いて書いています。

DIのメリット

よく言われるメリットとして、単体テストが書きやすくなることが挙げられますが、
これはそのとおりだと私も思います。

冒頭の例をもう一度示します。

Main.java
public class Main {
    public int hoge() {
        var sub = new Sub();
        return sub.calculate();
    }
}

仮にこのSubクラスのcalculateメソッドが、DBにアクセスする必要があったとすると、Mainクラスの単体テストは非常に難しくなります。

しかし、DIによって注入するインスタンスをモックにすることで、テストすることができます。
ちなみに私はモックを用いる際はMockitoを用いています。

まとめ

最後に、改めてDIおよびDIコンテナについて一言でまとめると下記のようになります。

DI:あるクラスに対して依存しているクラスのインスタンスを外部から渡してあげること
DIコンテナ:注入するインスタンスを管理してくれる入れ物

これらを用いることで、テストが書きやすくなったり、スコープの管理がしやすくなったりというメリットがあります。

内容は以上です。本記事が少しでもお役に立てば幸いです。
最後までお読み頂き、ありがとうございました。

参考文献

公式ドキュメント
Core Technologies

25
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?