Skip to content
Published on

Spring Boot Service・Repository Bean登録方法

Authors

一般的なWebアプリケーション(バックエンド)は5つの構成要素に分けられます。

  • コントローラ:Web MVCのコントローラの役割
  • サービス:コアビジネスロジックの実装
  • リポジトリ:データベースへのアクセス、ドメインオブジェクトをDBに保存・管理
  • ドメイン:ビジネスドメインオブジェクト
  • DB:実際のデータを保存する別プロセス(Springの範囲外)

Springでは、サービスやリポジトリといった要素をBeanというオブジェクトとして管理し、必要な場所で必要なタイミングに自動的に注入します。(これをDependency Injectionと呼びます。) これらの要素をSpringで管理させる理由については、次の投稿で詳しく取り上げます。SpringでBeanとして管理されるために必要な最低限のアノテーションは@Componentですが、一般的にサービスの場合は以下のように@Serviceを付けます。(@Serviceアノテーションは内部的に@Componentを含んでいます。)これはComponent Scan方式を利用したBean登録方式であり、アノテーションではなくJavaコードで明示的にSpring Beanを登録する方法もあります。

Component Scan方式による自動依存関係設定

MemberServiceの登録

例えばMemberServiceというサービスを登録する場合、以下のように@Serviceアノテーションを付けます。コンストラクタに@Autowiredを付けた理由は、Springが管理するBeanの中からmemberRepositoryを自動的に注入するためです。

@Service
public class MemberService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    /*
            会員登録
         */
    public Long join(Member member){
        // 同じ名前の重複会員は不可
        validateDuplicateMember(member);

        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
                .ifPresent(m ->{
                    throw new IllegalStateException("既に存在する会員です");
                });
    }
    /*
        全会員照会
     */
    public List<Member> findMembers(){
        return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}

MemoryMemberRepositoryの登録

Repositoryの場合は@Repositoryアノテーションを付けます。

MemoryMemberRepository.java
@Repository
public class MemoryMemberRepository implements MemberRepository{
    private static Map<Long, Member> store  = new HashMap<>();
    private static long sequence = 0L;
    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter((member) -> member.getName().equals(name))
                .findAny();
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }

    public void clearStore(){
        store.clear();
    }
}

@Repositoryアノテーションも@Componentを持っています。

Repository.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

Note これらのアノテーションはすべてのパッケージで動作するわけではなく、@SpringBootApplicationアノテーションが付いているパッケージとそのサブパッケージにのみ適用されます。Spring Beanはシングルトン(SingleTon)として生成され、1つだけ作成されて管理されます。

JavaコードによるSpring Bean登録

@Service@Repository@Autowiredアノテーションを使用せずにSpring Beanを登録する方法を学びましょう。 既存のBean注入のために付けていた上記3つのアノテーションを削除し、SpringConfig.javaというファイルを作成して以下のように記述します。 @ConfigurationアノテーションでSpring関連のファイルであることを登録し、@Beanアノテーションを付けてBeanに登録するオブジェクトを生成してReturnする関数を記述します。

SpringConfig.java
@Configuration
public class SpringConfig {
    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

DIにはコンストラクタ注入、フィールド注入、セッター注入の3つの方式があります。 フィールド注入の場合、生成時を除いてフィールドを変更できないという欠点があり、セッター注入の場合はsetterがpublicで公開されている必要があるため望ましくありません。最も推奨される方式はコンストラクタを通じた注入方式です。実務ではコンポーネントスキャン方式で開発が進められますが、状況に応じて実装クラスを別のBeanに変更する必要がある場合、既存のコードを変更せずに済むようJavaコードでBeanを設定する方式も使用されます。

References