- Authors

- Name
- Youngju Kim
- @fjvbn20031
一般的な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アノテーションを付けます。
@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を持っています。
@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する関数を記述します。
@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
- キム・ヨンハンさんのInflearn講座:スプリング入門 - スプリングブート