見出し画像

Spring入門(DI編)


はじめに

仕事でSpringを用いたバックエンド開発を行うことになったため、Springについての学習をゼロから開始した。
学んだ内容を概念ごとに分割して記事を作成する。
本記事の対象読者はプログラミングやアプリケーション構築の基礎知識はあるが、Springは未経験の方。

DIとは

DIはDependency Injection(依存性注入)の略で、クラス間の依存関係を小さくするために、依存関係があるクラスのオブジェクトをクラス外の別の場所(DI
コンテナ)で作成して、オブジェクト使用時に事前作成していたオブジェクトを使用(注入)するという考え方。
クラス間の依存関係をなくすために、事前作成するオブジェクトの型は具象化クラスではなくインタフェースを使用する。

以下にSpringでDIを行っていく上で必須となる概念を説明する。

Bean

DIコンテナで管理されるオブジェクトの実体。
SpringではSpringBoot起動時にBeanのオブジェクトが作成(new)され、DIコンテナ内に格納される。
依存性が注入され、オブジェクトを使用する際は、このDIコンテナに格納されたオブジェクトが引き渡されて使用される。

Bean定義

DIコンテナに格納されるBeanの設計書。
インタフェースにどの具象オブジェクトを紐づけるかなどを管理する。

Bean定義には主に以下の2種類が存在する。

ステレオタイプアノテーション

@Controller
@Service
@Repository
@Conmponent
など役割毎に異なるアノテーションをクラスに設定することでBean定義を作成できる。
主に自作クラスに設定してDIで管理するために使用する。

例)
インタフェース

public interface OrderService {
    Order placeOrder(OrderInput orderInput, CartInput cartInput);
}

ステレオタイプアノテーション

@Service
public class OrderServiceImpl implements OrderService {
    private final OrderRepository orderRepository;
...


Beanメソッド

ステレオタイプアノテーションではライブラリクラスのBean定義を作成できないため、ライブラリクラスのオブジェクトをDIコンテナで使用するために使用する。
JavaConfigクラス内で@Beanアノテーションを付与したメソッドを作成することで実装する。
DIコンテナにより自動で実行される。

@Bean
public DataSource dataSource() {
    EmbeddedDatabase dataSource = new EmbeddedDatabaseBuilder()
            .addScripts("schema.sql", "data.sql")
            .setType(EmbeddedDatabaseType.H2).build();
    return dataSource;
}

SpringBootのオートコンフィグレーションを使用する場合は大部分が省略可能。

Bean定義内でのインジェクション

Bean定義内で他クラスのインジェクションを行うには以下の3つの方法がある。
いずれの場合も@Autowiredが必要(コンストラクタが一つの場合は省略可能)。

コンストラクタインジェクション

コンストラクタ内で依存性の注入を行う。
変数定義時にfinalを指定してオブジェクトを不変にできるため、基本的にはこの方法を使用する。

public class OrderService{

    private final OrderRepository orderRepository;

    @Autowired
    privete OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
    // 以下省略
}

Setterインジェクション

インジェクション用のSetter関数を作成する。
不変にできないため非推奨。

フィールドインジェクション

フィールド内で直接変数定義する。
不変にできない+メンテナンス性が下がるため非推奨。

@Autowired

DIコンテナからオブジェクトを取り出して依存性注入を行うためのアノテーション。(newを省略してオブジェクトを取得できる)
あくまでDIコンテナからオブジェクトを取得するという処理のため、DIコンテナで管理されていないクラスのオブジェクトは取得することができない。

プロファイル

使用するBean定義を環境毎に切り替えるための設定。
Bean定義やコンフィグレーションに@Profile("プロファイル名")を追加することで設定できる。
例)

@Repository
@Profile("JDBC")
public class JdbcTrainingRepository implements TrainingRepository {
...

使用するプロファイルの指定はJava実行時のオプションで指定するか、以下の記述をDIコンテナ作成前に追記する。
(Javaコマンドでの指定推奨)

System.setProperty("spring.profiles.active", "プロファイル名");

適用される定義は、プロファイルで指定されている定義+プロファイルが指定されていないすべての定義。

コンフィグレーション

Bean定義やDIコンテナの各種設定を管理する、DIコンテナに読み込ませる設計書のようなもの。
実体はJavaConfigクラス。

JavaConfig

コンフィグレーションを定義するためのJavaクラス。
通常のクラスに@Configurationアノテーションをつけることで作成できる。

コンポーネントスキャン

DIコンテナからBean定義の具象化クラスを探す処理。
パッケージ単位で指定可能。
コンポーネントスキャンはJavaConfigクラスに@ComponentScanアノテーションをつけることで可能。
コンポーネントスキャンはパッケージを指定して実行可能。

複数コンフィグレーションのDIコンテナへの適用

JavaConfigクラス自体もBeanとして管理されるため、コンポーネントスキャンで読み取られる。
コンフィグレーションを適用する際は、代表となるコンフィグレーションにコンポーネントスキャンを適用して、追加で必要となるコンフィグレーションをコンポーネントスキャンで読み取られる対象に含めることで、複数のコンフィグレーションをDIコンテナに適用できる。

DIコンテナ(ApplicationContext)

DIで注入されるオブジェクトの実体を管理するもの。


この記事が気に入ったらサポートをしてみませんか?