LaravelとSpringBootでDIコンテナを利用してみる


はじめに

これはユアマイスターAdventCalendar2019の13日目の記事です。
(社会人になってから学んだことをアウトプットする記事になります。)

今回の経緯

社会人になってからDI(依存性の注入)という概念を知りました。
WEBサービスの開発を行う際に、フレームワークを利用する場面は多々ありましたが、主に利用していたCake PHPではDIという概念は出ていなかったと記憶しています。
(記憶違いだったらすみません。)

DIは、インスタンスをnewで作成して利用するのではなく、DIコンテナやサービスコンテナ呼ばれるもの(SpringBootではDIコンテナ、Laravelではサービスコンテナと呼ばれます)を利用して、あらかじめ登録されたインスタンスを利用します。

今回は、業務で利用しているSpringBootでのDIコンテナの利用、最近独学で学んでいるLaravelでのサービスコンテナを利用してみるというテーマで記事を書いてみたいと思います。

SpringBootの場合

コンストラクターインジェクションを利用する

ユアマイスターアドベントカレンダー2019 の7日目の記事で書いたコードを用いて書いていきます。
SpringBootで動的にDBを切り替えてみる
https://github.com/Masaki-Ogawa/datasourceDemo

  1. PersonRepository.java
PersonRepository.java
package com.example.dataSourceDemo.domain.repositories;

import com.example.dataSourceDemo.domain.models.Person;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {

}

JpaRepositoryを継承したRepositoryクラス

  1. PersonServiceImpl.java
PersonServiceImpl.java
package com.example.dataSourceDemo.domain.services;

import com.example.dataSourceDemo.annotations.DataSource;
import com.example.dataSourceDemo.annotations.DataSource.DataSourceType;
import com.example.dataSourceDemo.domain.models.Person;
import com.example.dataSourceDemo.domain.repositories.PersonRepository;
import java.util.List;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceImpl implements PersonService {

  private final PersonRepository personRepository;

  public PersonServiceImpl(
      PersonRepository personRepository) {
    this.personRepository = personRepository;
  }

  /**
   * stgのDBからPersonテーブルのレコードを取得するメソッド
   * @return Personテーブルのレコード
   */
  @Override
  public List<Person> findAllPersonInStg() {
    return personRepository.findAll();
  }

  /**
   * stgのDBからPersonテーブルのレコードを取得するメソッド
   * @return Personテーブルのレコード
   */
  @DataSource(value = DataSourceType.PROD)
  @Override
  public List<Person> findAllPersonInProd() {
    return personRepository.findAll();
  }
}

ここでDIを行っています。

具体的には、


private final PersonRepository personRepository;

  public PersonServiceImpl(
      PersonRepository personRepository) {
    this.personRepository = personRepository;
  }

の部分でコンストラクターインジェクションによるDIを行っています。
@Service@Repositoryというアノテーションを利用することにより、DIコンテナに登録されます。
利用するには上記のように、コンストラクターインジェクション等を利用して、インスタンスを作成します。

参考
Spring Framework 要点まとめ ~ DIについて

LaravelでのDI

サービスプロバイダーを利用する

作成したサービスをサービスコンテナ登録するためのサービスプロバイダーを作成します。
今回はDemoServiceというサービスクラスを作成しました。

AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('App\Services\DemoService');
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

上記のようにサービスを登録します。

DemoService.php
class DemoService {
    public function show() {
        echo "何かを表示します";
    }
}

サービスクラスは今回は適当ですが、何か文字を出力するメソッドのみ実装します。

DemoController.php
class MessageController extends Controller
{
    protected $demoService;

    public function __construct(DemoService $demoService)
    {
        $this->demoService = $demoService;
    }

    public function index(Request $request) {

        return $this->demoService->show();
    }
}

このようにこちらもコンストラクターインジェクションを利用して、こちらもDIを行います。

終わりに

個人的には、@Service@Controller等のアノテーションで、DIコンテナに登録できるSpringBootの方が利用しやすいと感じています。
Java、SpringBootを利用してやはり、アノテーションの強力さに気づかされる場面が多々あります。

また、7日目のアドベントカレンダーで書きましたが、それぞれのフレームワークに良さや悪さがあり、多数のフレームワークに触るという経験は、今後何かものを作るときに、「どんなものを採用すれば、そのプロダクトにとって一番良いのか?」という判断材料になると思います。

今後も、業務、業務外を含めて触れていきたいと思います。