Spring Framework Tips: DIコンテナとJavaConfig周り

イントロダクション

Spring FrameworkのDI(Dependency Injection)コンテナは、依存性の管理を自動化し、コードの結合度を下げる強力なツールです。本記事では、DIコンテナやJavaConfig、Beanの定義方法、環境ごとの設定を切り替えるプロファイル活用法までをまとめました。

DIコンテナ

DIコンテナの役割

DIのためのBeanオブジェクトの生成と管理を行う。依存性の注入を自動化し、結合度を下げる。

DIコンテナの役割

DIコンテナの生成方法

以下の方法でDIコンテナ生成できる。

public static void main(String[] args) {
    // FooConfigはJavaConfigクラス。
    ApplicationContext context = new AnnotationConfigApplicationContext(FooConfig.class);
}

SpringBootを使う場合は、runメソッドで生成される。

public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(FooApplication.class, args);
}

DIコンテナからBeanオブジェクトを取得

ApplicationContext context = SpringApplication.run(FooApplication.class, args);
FooService fooService = context.getBean(FooService.class);
...

JavaConfig

JavaConfigの役割

DIコンテナに読み込ませるコンフィグレーションファイル。

JavaConfigの定義方法

@Configアノテーションを付与したJavaクラスで定義する。@Beanをメソッドに付与してBeanを定義可能。

@Configuration
public class FooConfig {

    // FooServiceのオブジェクトを生成し、Beanとして管理する
    @Bean
    public FooService fooService(){
        ...
    }
}

複数のJavaConfigクラスを読み込む方法

以下の方法で複数のJavaConfigクラスを読み込み可能。

  • AnnotationConfigApplicationContextクラスのコンストラクタで複数指定
  • @Importアノテーションを用いる
  • コンポーネントスキャンを用いる

AnnotationConfigApplicationContextクラスのコンストラクタで複数指定

ApplicationContext context = new AnnotationConfigApplicationContext(FooConfig.class, BarConfig.class);

@Importアノテーションを用いて複数指定

@Configuration
@Import(BarConfig.class)
public class FooConfig{
    ...
}

@Configuration
public class BarConfig{
    ...
}

ApplicationContext context = new AnnotationConfigApplicationContext(FooConfig.class);

コンポーネントスキャンを用いて複数指定

@Configuration
@ComponentScan
public class FooConfig{
    ...
}

@Configuration
public class BarConfig{
    ...
}

ApplicationContext context = new AnnotationConfigApplicationContext(FooConfig.class);

Bean

Bean定義方法

以下の方法で定義可能。

  • ステレオタイプアノテーション(@Component、@Serviceなど)
  • JavaConfigにおける@Beanアノテーション
  • XMLファイル(レガシーシステム向け)

ステレオタイプアノテーションによるBean定義

Beanとして管理したい具象クラス付与するアノテーションをステレオタイプアノテーションという。決められたいずれかのアノテーションを付与すると、Springが具象クラスのコンストラクタを呼び出してオブジェクトを生成し、Beanとして管理する。

@Service
public class FooServiceImpl implements FooService{
    ...
}

主なステレオタイプアノテーションの種類

@Service

Serviceの具象クラスに付与。付加機能なし。

@Repository

Repositoryの具象クラスに付与。付加機能として、データベースアクセス周りの例外がSpringが提供する例外に変換されるようになる。

@Controller

Controllerの具象クラスに付与。付加機能として、Spring MVCの機能を利用可能になる。

@Component

汎用的なステレオタイプアノテーション。上記に当てはまらないクラスに用いることが多い。付加機能なし。

JavaConfigの@BeanアノテーションによるBean定義

JavaConfigクラス上で@Beanアノテーションを用いてBeanを定義することもできる。

@Configuration
public class FooConfig {
    @Bean
    public FooService fooService(FooRepository repository){
        return new FooServiceImpl(repository);
    }

    @Bean
    public FooRepository fooRepository(){
        return new JdbcFooRepository();
    }
}

Bean定義方法の使い分け

定義方法 利点 用途
ステレオタイプアノテーション 簡単に定義可能。コード内で明示的に宣言。 自作クラスでの使用が適している
JavaConfig 柔軟性が高い。外部ライブラリのクラスも定義可能。 外部ライブラリのBeanを登録する場合
XML 古いシステムで使用されている可能性あり。 レガシーシステム対応

外部ライブラリをBean管理したい場合はJavaConfigでの@Bean定義を利用するのが良い。

@Bean
public DataSource dataSource() {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
    config.setUsername("username");
    config.setPassword("password");
    config.setMaximumPoolSize(10);
    return new HikariDataSource(config);
}

Beanのインジェクション方法

Beanオブジェクトをインジェクションするには以下の方法がある。

  • コンストラクタでのインジェクション
  • フィールドでのインジェクション
  • JavaConfigでの@Beanメソッド内での記述

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

コンストラクタの引数として依存オブジェクトを受け取る方法。DIコンテナがコンストラクタを呼び出す際に、引数の型に合うBeanのオブジェクトを渡してくれる。

@Service
public class FooService {
    private final FooRepository fooRepository;

    // コンストラクタが一つしかない場合は、@Autowiredアノテーション省略可
    @Autowired
    public FooService(FooRepository fooRepository) {
        this.fooRepository = fooRepository;
    }
}

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

フィールドに直接@Autowiredアノテーションを付ける方法。

@Service
public class FooService {
    @Autowired
    private FooRepository fooRepository;

    ...
}

テスト容易性や、フィールドをfinal修飾できることなどいくつかの利点から、コンストラクタでのインジェクションが推奨。

JavaConfigでの@Beanメソッド内での記述

fooServiceメソッドの引数でFooRepository型の引数を定義しておくと、FooServiceの@Beanメソッドを呼び出す際に、引数のFooRepositoryのBeanオブジェクトをDIコンテナから探してきて引数に渡してくれる。これによりServiceImplオブジェクトにJdbcFooRepositoryオブジェクトがインジェクションされる。

@Configuration
public class FooConfig {
    @Bean
    public FooService fooService(FooRepository repository){
        return new FooServiceImpl(repository);
    }

    @Bean
    public FooRepository fooRepository(){
        return new JdbcFooRepository();
    }
}

プロファイルによるコンフィグレーションの切り替え

環境(開発、テスト、本番など)に応じて異なる設定を適用するための機能。@Profileアノテーションを利用して、DIコンテナ生成時に有効にするBeanやコンフィグレーションを切り替えられる。

プロファイル指定方法

@Profileアノテーションを用いて、環境ごとにグルーピングできる

@Repository
@Profile("prd")
public class ExternalFooRepository() implements FooRepository{
    ...
}

@Repository
@Profile("stg")
public class JdbcFooRepository() implements FooRepository{
    ...
}

@Service
public class FooService() implements FooService{
    ...
}

JavaConfigクラスに@Profileを付与

@Profile("prd")
@Configuration
@ComponentScan
public class FooConfig{
    @Bean
    public FooService fooService(){
    ...
}

Beanごとに指定

@Configuration
@ComponentScan
public class FooConfig{
    @Bean
    @Profile("prd")
    public FooService fooService(){
    ...
}

プロファイルの指定方法

  • 起動時の引数で指定:
    java -Dspring.profiles.active=prd -jar myapp.jar
  • 環境変数で指定:
    export SPRING_PROFILES_ACTIVE=stg
  • Spring Bootではapplication.propertiesで指定可能。

この情報は役に立ちましたか?


フィードバックをいただき、ありがとうございました!

関連記事

カテゴリー:

ナレッジブログ

情シス求人

  1. チームメンバーで作字やってみた#1

ページ上部へ戻る