23
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Bootのステレオタイプアノテーションを整理する

23
Posted at

はじめに

Spring Bootでは、クラスを Bean としてDIコンテナに登録することで、インスタンスの生成や依存関係の管理をフレームワークに任せることができます。
Bean とはSpringが管理するオブジェクトのことで、DIコンテナがそのライフサイクルを管理します。@Autowired や コンストラクターインジェクションでBeanを受け取れるのは、DIコンテナがインスタンスを保持しているからです。

Beanとして登録するための主なアノテーションが @Component@Service@Repository@Controller の4つです。
この記事ではそれぞれの用途について整理します。


全員 @Component の派生アノテーション

まず大前提として、4つはすべて @Component を元にした派生アノテーション(メタアノテーション) です。

// @Service の定義(イメージ)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component  // ← @Component が付いている
public @interface Service { ... }

// @Repository の定義(イメージ)
@Component
public @interface Repository { ... }

// @Controller の定義(イメージ)
@Component
public @interface Controller { ... }

つまり機能的には @Component だけでDIコンテナへの登録はできます。では4つに分かれている理由は何でしょうか。


役割と使う層が違う

Spring Bootはレイヤードアーキテクチャを前提としており、アノテーションがその層を表します。

┌─────────────────────────────────┐
│  プレゼンテーション層             │ → @Controller / @RestController
│  (HTTPリクエスト・レスポンス)    │
├─────────────────────────────────┤
│  ビジネスロジック層               │ → @Service
│  (業務処理・判断)               │
├─────────────────────────────────┤
│  データアクセス層                 │ → @Repository
│  (DBとのやりとり)               │
├─────────────────────────────────┤
│  その他の汎用コンポーネント        │ → @Component
└─────────────────────────────────┘

各アノテーションの詳細

@Component

どの層にも属さない、汎用的なBeanに使います。ユーティリティクラスや共通処理など、明確に層が決まらないクラスに使うイメージです。

@Component
public class DateUtils {
    public String formatToJapanese(LocalDate date) {
        return date.getYear() + "年" + date.getMonthValue() + "月" + date.getDayOfMonth() + "日";
    }
}

@Service

ビジネスロジックを担うクラスに使います。「何をするか」という業務処理の判断が含まれる層です。

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + id));
    }

    public User save(User user) {
        // バリデーション・重複チェック等のビジネスロジック
        return userRepository.save(user);
    }
}

@Repository

DBとのやりとりを担うクラスに使います。Spring Data JPAを使っている場合は JpaRepository を継承したインターフェースが自動的にこの役割を担うため、自分で @Repository を書く機会は少ないです。

// Spring Data JPAの場合、@Repository は不要(自動認識される)
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

JDBCを直接使うなど、自分でRepositoryクラスを実装する場合は明示的に付けます。

@Repository
public class UserJdbcRepository {

    private final JdbcTemplate jdbcTemplate;

    public List<User> findAll() {
        return jdbcTemplate.query("SELECT * FROM users", userRowMapper());
    }
}

@Repository には @Component にはない例外変換機能があります。DB固有の例外(SQLException 等)をSpringの DataAccessException に変換してくれるため、DB種別を意識せずに例外処理を書けます。


@Controller / @RestController

HTTPリクエストを受け取り、レスポンスを返す層に使います。

// @Controller:ビューを返す場合(Thymeleaf等)
@Controller
public class UserController {

    @GetMapping("/users")
    public String list(Model model) {
        model.addAttribute("users", userService.findAll());
        return "users/list"; // テンプレート名を返す
    }
}
// @RestController:JSONを返す場合(REST API)
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserRestController {

    private final UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.findById(id));
    }
}

@RestController@Controller + @ResponseBody の組み合わせです。REST APIを作る場合は @RestController を使います。


4つの比較まとめ

アノテーション 使う層 主な用途 追加機能
@Component 汎用 ユーティリティ・共通処理 なし
@Service ビジネスロジック層 業務処理・判断 なし
@Repository データアクセス層 DBとのやりとり 例外変換
@Controller プレゼンテーション層 ビュー返却 なし
@RestController プレゼンテーション層 JSON返却(REST API) @ResponseBody 付与

なぜ役割に合ったアノテーションを使うべきか

技術的には @Component だけでも動きます。それでも役割に合ったアノテーションを使う理由は2つあります。

① コードの意図が伝わる
@Service が付いているクラスを見れば、「ここにビジネスロジックがある」とすぐに読み取れます。

② Springの追加機能を受けられる
@Repository の例外変換のように、アノテーションに応じたSpringの機能が有効になります。


まとめ

  • 4つはすべて @Component の派生アノテーションで、DIコンテナへの登録という点では同じ
  • 違いは「どの層で使うか」と「追加機能の有無」
  • REST APIを作るなら @RestController、ビジネスロジックは @Service、DBアクセスは @Repository を使う
  • どの層にも属さない汎用クラスには @Component を使う
23
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?