30 人が閲覧(直近 30 日) Spring

Spring Bootのアノテーション一覧|仕組みと使い分け・Boot 4での変更点

Spring Bootのアノテーション一覧|仕組みと使い分け・Boot 4での変更点

Spring Bootで日常的に使うアノテーションを、起動設定・DI・Web・データアクセス・入力チェック・AOP・セキュリティ・テストの役割別に一覧表で並べました。Spring Boot 4ではテスト系を中心にimport先が変わったため、表にはパッケージ名も載せています。アノテーションが動く仕組みと、@Autowired・@Bean・@Qualifier のように取り違えやすいものの使い分けも扱います。表とコードは、2026年10月22日に公開されたSpring Boot 4.1.2(Spring Framework 7.0.10)で確かめました。

まとめ:Springアノテーション一覧の要点

  • アノテーションはJava標準の注釈で、それだけでは何もしません。Springなどの処理系が読み取り、Bean登録・リクエスト処理・入力検証などに反映します。Lombokのようにコンパイル時に処理されるものもあります。
  • 最低限の核は @SpringBootApplication、@RestController と @GetMapping 系、@Service・@Repository、@Transactional の4グループです。
  • パッケージ名で提供元が分かります。@Data はLombok、@Entity はJakarta Persistence、@NotBlank はJakarta Validationで、Springのものではありません。
  • 自分で書いたクラスは @Component 系、ライブラリのクラスは @Bean で登録します。同じ型が複数あるときは @Primary か @Qualifier で1つに絞ります。
  • @Transactional は既定のプロキシ方式では自己呼び出しやprivateメソッドに適用されません。また、チェック例外では既定でロールバックしません。
  • Spring Boot 4では @MockBean が削除され、@WebMvcTest などはパッケージが移動しました。javax.annotation の @PostConstruct はエラーも出さずに無視されます。

Springアノテーションとは:起動時にSpringが読み取る仕組み

アノテーションは、クラス・メソッド・フィールド・引数に @ で付ける注釈です。処理を書くものではなく、「このクラスをBeanとして登録する」といった指示を、それを解釈するフレームワークに伝えます。XMLの設定ファイルに書いていた内容を、コードのすぐ横に書けるようにしたものと考えると分かりやすくなります。

アノテーションの正体:@interfaceとRUNTIME保持・メタアノテーション

Springのアノテーションも、Javaの @interface で定義された普通のアノテーションです。@Service のクラスファイルを javap で見ると、@Retention(RetentionPolicy.RUNTIME) と @Component が付いています。RUNTIME保持なので実行時にリフレクションで読め、@Component を持つので @Service のクラスは @Component の一種として登録されます。この「アノテーションに付けたアノテーション」をメタアノテーションと呼びます。@RestController が @Controller と @ResponseBody の合成になっているのも同じ仕組みです。

同じ仕組みで、自分で合成アノテーションを作れます。次のコードは @Service と @Transactional を1つにまとめたものです。

// @Service と @Transactional を1つにまとめた合成アノテーション
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional
public @interface TransactionalService {
}

起動時の処理順序:スキャンからプロキシ生成まで

  1. コンポーネントスキャン:@SpringBootApplication に含まれる @ComponentScan が、mainクラスのパッケージ配下から @Component 系の付いたクラスを集めます。
  2. Bean定義への変換:見つけたクラスを、スコープや遅延初期化の有無を持つBeanDefinition(設計図)に変えて登録します。インスタンスはまだ作りません。
  3. 生成と注入:設計図からインスタンスを作り、コンストラクタ引数などに型が合うBeanを注入します。@PostConstruct のメソッドはこの後に呼ばれます。
  4. プロキシ生成:@Transactional・@Async・@Cacheable などが付いたBeanは、元のオブジェクトを包むプロキシに置き換えて登録します。

スキャンの既定範囲はmainクラスのパッケージとその配下だけです。mainクラスが com.example.app にあると、com.example.common に置いた @Service は見つかりません。「付けたのにBeanが無い」と言われたら、まずパッケージの位置を疑います。範囲を広げるときは @SpringBootApplication(scanBasePackages = "com.example") と書きます。コンテナとBeanの関係そのものはIoCコンテナとは?Spring IoCコンテナ・Beanの仕組みで扱っています。

パッケージ名で見分ける提供元:@DataはLombok、@EntityはJPA

「Spring Bootのアノテーション」として紹介されるものには、Spring以外が定義したものが多く混じっています。付けたのに効かないときは、まずimport文のパッケージを見ると、足りない依存の見当がつきます。ただし定義元と処理する側が同じとは限りません。jakarta.validation の制約はHibernate Validatorが検査し、org.aspectj の記法はSpring AOPが解釈します。

パッケージの先頭 提供元 代表例 必要な依存(Spring Boot 4.1)
org.springframework.boot Spring Boot @SpringBootApplication spring-boot-starter
org.springframework Spring Framework @Service, @GetMapping 各starterに同梱
org.springframework.data Spring Data @Query, @Modifying spring-boot-starter-data-jpa
org.springframework.security Spring Security @PreAuthorize spring-boot-starter-security
jakarta.persistence Jakarta Persistence @Entity, @JoinColumn spring-boot-starter-data-jpa
jakarta.validation Jakarta Validation @NotBlank, @Valid spring-boot-starter-validation
jakarta.annotation Jakarta Annotations @PostConstruct spring-boot-starterに同梱
org.aspectj AspectJ @Aspect, @Around spring-boot-starter-aspectj
lombok Lombok @Data, @RequiredArgsConstructor lombok(版はBootのBOMが管理)
java.lang Java標準 @Override, @Deprecated 不要

Lombokの @Data はgetter・setter・equals・hashCode・toStringと必須引数のコンストラクタをコンパイル時に生成するもので、Springは関与しません。Spring BootのBOMはLombokの版(4.1.2では1.18.48)を管理しているだけで、依存は自分で足します。JPAのエンティティに @Data を付けるのは避けます。生成されるequals・hashCodeは既定でstaticとtransientを除くフィールドを使い、JPAの関連フィールドも含むため、遅延ロードの関連に触れて余計なSQLが走ったり、保存前後でhashCodeが変わったりします。エンティティには @Getter と @Setter を個別に付けます。

同じ名前の別物にも注意します。@Transactional にはJakarta版の jakarta.transaction.Transactional もあり、IDEの補完で取り込むと rollbackFor や readOnly が使えません。

起動と設定のアノテーション一覧

アプリケーションの入口と、設定値・Bean定義・登録条件を扱うアノテーションです。@ConditionalOn で始まる条件系はSpring Boot固有で、自動構成の仕組みそのものです。

アノテーション 付ける場所 役割
@SpringBootApplication mainクラス 下の3つをまとめた合成アノテーション
@SpringBootConfiguration mainクラス 設定クラスであることの宣言
@EnableAutoConfiguration mainクラス 依存に応じた自動構成を有効化
@ComponentScan mainクラス 同じパッケージ以下のBeanを検出
@Configuration クラス @Beanメソッドを持つ設定クラス
@Bean メソッド 戻り値をBeanとして登録
@Value フィールド・引数 設定値を1つずつ注入
@ConfigurationProperties クラス・record 接頭辞単位で設定値を束ねる
@ConfigurationPropertiesScan mainクラス 上記を付けた型を自動登録
@Profile クラス・メソッド プロファイル有効時だけ登録
@ConditionalOnProperty クラス・メソッド 設定値が条件に合うときだけ登録
@ConditionalOnClass クラス・メソッド 指定クラスがあるときだけ登録
@ConditionalOnMissingBean クラス・メソッド 指定条件を満たすBeanが無い場合に登録
@Import 設定クラス 別の設定クラスを取り込む

同じ接頭辞に属する設定値をまとめて扱うなら、@Value を並べるより @ConfigurationProperties でrecordにまとめるほうが見通しがよくなります。@ConfigurationProperties を付けただけではBeanにならないので、mainクラスに @ConfigurationPropertiesScan を付けます。自分でBeanを定義すると自動構成が引っ込むのは、自動構成側が @ConditionalOnMissingBean で書かれているからです。

@SpringBootApplication
@ConfigurationPropertiesScan
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

// application.yml の app.greeting / app.max-items を束ねて受け取る
@ConfigurationProperties("app")
public record AppProperties(String greeting, int maxItems) {
}

Bean登録とDIのアノテーション一覧

クラスをDIコンテナに登録するステレオタイプと、注入先を決めるアノテーションです。

アノテーション パッケージ 役割
@Component org.springframework.stereotype 汎用のBean登録
@Service org.springframework.stereotype 業務ロジック層のBean
@Repository org.springframework.stereotype データアクセス層のBean+例外変換
@Controller org.springframework.stereotype 画面を返すWebコントローラー
@RestController org.springframework.web.bind.annotation @Controller+@ResponseBody
@Autowired org.springframework.beans.factory.annotation 型が一致するBeanを注入
@Qualifier org.springframework.beans.factory.annotation 同じ型の候補を修飾子で絞り込む
@Primary org.springframework.context.annotation 同じ型が複数あるときの既定
@Lazy org.springframework.context.annotation 初回利用時まで生成を遅らせる
@Scope org.springframework.context.annotation singleton・prototypeなどのスコープを指定
@DependsOn org.springframework.context.annotation 指定Beanより後に生成する
@PostConstruct jakarta.annotation 注入完了後に1回呼ぶ初期化
@PreDestroy jakarta.annotation コンテナ終了時の後始末

@Autowiredとは:読み方と、省略できる条件

@Autowired(読み方はオートワイヤード)は、コンテナに登録されたBeanから型が一致するものを探して注入するアノテーションです。フィールド・セッター・コンストラクタに付けられますが、注入はコンストラクタで受け取るのが基本です。依存をfinalにでき、テストでは new で差し替えられます。Spring Frameworkのリファレンスにあるとおり、コンストラクタが1つしかないクラスでは @Autowired を書かなくても注入されます。フィールド注入・セッター注入との比較はSpringのDI(依存性注入)の仕組みと注入方法にまとめています。

@Service
public class BookService {
    private final BookRepository repository;

    // コンストラクタが1つだけなので @Autowired は不要
    public BookService(BookRepository repository) {
        this.repository = repository;
    }

    @Transactional(readOnly = true)
    public Book find(Long id) {
        return repository.findById(id).orElseThrow(() -> new BookNotFoundException(id));
    }
}

ステレオタイプ4種のうち、登録以外の機能を持つのは2つだけです。@Repository はJPAプロバイダの例外をSpringの DataAccessException へ変換する対象になり、@Controller はリクエストマッピングの走査対象になります。@Service を @Component に替えても動作は変わりません。それでも @Service を使うのは、層の境界がコードから読め、AOPのポイントカットで層単位に指定できるからです。サービス層の役割はサービスクラスとは?Java・Springでの役割と書き方で扱っています。

@Componentと@Beanの使い分け:ソースの編集可否と生成処理

判断の基準は1つです。自分で書いたクラスは @Component 系をクラスに付け、ソースを編集できないライブラリのクラスは @Configuration クラスの @Bean メソッドで登録します。RestClient や ObjectMapper には @Component を付けられないからです。生成時に引数を組み立てたり条件で分岐したりしたいときも @Bean を使います。次の例で RestClient.Builder の自動構成を使うには、Spring Boot 4の依存に spring-boot-starter-restclient を追加します。

@Configuration
public class HttpClientConfig {

    // RestClient はライブラリのクラスなので @Component を付けられない。@Bean で登録する
    @Bean
    public RestClient inventoryClient(RestClient.Builder builder) {
        return builder.baseUrl("https://inventory.example.com").build();
    }

    @Bean
    public AuditLog auditLog() {
        return new AuditLog();
    }

    @Bean
    public ReportService reportService() {
        // proxyBeanMethods = true(既定)なので、auditLog() を呼んでもコンテナ内の同じBeanが返る
        return new ReportService(auditLog());
    }
}

@Configuration クラスには既定でCGLIBのプロキシが掛かります(proxyBeanMethods = true)。7.0.10で試すと、上の reportService() の中で呼んだ auditLog() はコンテナ内と同じインスタンスを返し、@Configuration(proxyBeanMethods = false) にすると呼ぶたびに別のインスタンスになりました。Spring Boot自身の自動構成は起動を速くするためにfalseを多用していますが、アプリ側で @Bean メソッドを呼び合うなら既定のままにします(@Configurationの説明)。

@Primaryと@Qualifierの使い方:同じ型のBeanが複数あるとき

同じ型のBeanが2つ以上あり、1つの注入先に対して @Primary や名前の一致でも候補を1つに絞れないと、起動に失敗します。実際に試すと NoUniqueBeanDefinitionException(expected single matching bean but found 2)で止まりました。解決は2通りで、選ぶ基準は「既定を決められるか」です。

@Primary
@Component("emailNotifier")
public class EmailNotifier implements Notifier {
    public void send(String message) { }
}

@Component("slackNotifier")
public class SlackNotifier implements Notifier {
    public void send(String message) { }
}

@Service
public class AlertService {
    private final Notifier defaultNotifier;
    private final Notifier slackNotifier;
    // 1つ目は @Primary の EmailNotifier、2つ目は名前で SlackNotifier を指定
    public AlertService(Notifier defaultNotifier,
                        @Qualifier("slackNotifier") Notifier slackNotifier) {
        this.defaultNotifier = defaultNotifier;
        this.slackNotifier = slackNotifier;
    }
}

このコードを起動すると、1つ目の引数には @Primary の EmailNotifier、@Qualifier("slackNotifier") の引数には SlackNotifier が入りました。大半の呼び出し元が同じ実装を使うなら @Primary を付け、例外だけを @Qualifier で指定します。用途ごとに実装が分かれていて既定が無いなら、@Primary は付けずに全員が @Qualifier を書きます。この場合に @Primary を付けると、@Qualifier を書き忘れた新しい呼び出し元にも黙って既定の実装が入り、起動時エラーで気づく機会を失います。

Bean名と同じ引数名でも解決できますが、引数名をクラスファイルに残す -parameters オプションが前提で、リファクタリングで引数名を変えると壊れます。明示の @Qualifier のほうが確実です。

初期化と後始末:@PostConstruct・@PreDestroyと呼ばれる順序

@PostConstruct は依存の注入が終わった直後に1回呼ばれます。@PreDestroy の呼ばれる時点はスコープで変わり、singletonはコンテナの終了時、requestとsessionはそのスコープの終了時で、prototypeはSpringが破棄を管理しないため呼ばれません。どちらも jakarta.annotation パッケージで、spring-boot-starterが依存に含めています。

@Component
public class CacheWarmer {

    // 依存の注入が終わった直後に1回だけ呼ばれる
    @PostConstruct
    void warmUp() {
    }

    // このBeanは既定のsingletonなので、コンテナの終了時に呼ばれる
    @PreDestroy
    void shutdown() {
    }
}

Awareインタフェースや @Bean の initMethod と併用したときの順序は、1つのBeanにすべてを実装して7.0.10で起動・終了すると次のとおりでした。

  1. コンストラクタ → @Autowired のセッター(依存の注入)
  2. BeanNameAware#setBeanName → BeanFactoryAware#setBeanFactory
  3. @PostConstruct → InitializingBean#afterPropertiesSet → @Bean(initMethod)
  4. 終了時:@PreDestroy → DisposableBean#destroy → @Bean(destroyMethod)

Bean名や BeanFactory を @PostConstruct の中で使っても、Awareのセッターは先に呼ばれているので値は入っています。初期化処理は @PostConstruct 1つに寄せ、Springのインタフェースを実装するのはライブラリのクラスに限るほうが、コードがSpringに縛られません。

古い記事のコードにある javax.annotation.PostConstruct は、Spring Framework 7.0のリリースノートでサポートが削除されました。javax版のjarをクラスパスに置いたまま7.0.10で起動すると、コンパイルも起動も通り、jakarta版のメソッドだけが呼ばれ、javax版はエラーも警告も出さずに無視されました。@javax.inject.Inject と @javax.annotation.Resource も同じ扱いです。

Web(Spring MVC)のアノテーション一覧

HTTPリクエストを受けてレスポンスを返すコントローラーで使うアノテーションです。すべて org.springframework.web.bind.annotation パッケージにあります。

アノテーション 付ける場所 役割
@RequestMapping クラス・メソッド URLとHTTPメソッドの対応づけ
@GetMapping ほか メソッド GET/POST/PUT/PATCH/DELETE別の短縮形
@PathVariable 引数 URLの {id} 部分を受け取る
@RequestParam 引数 クエリ文字列・フォーム値を受け取る
@RequestBody 引数 JSON本文をオブジェクトへ変換
@ModelAttribute 引数・メソッド フォーム値をオブジェクトへ束ねる
@RequestHeader 引数 リクエストヘッダーを受け取る
@ResponseBody クラス・メソッド 戻り値をそのまま本文にする
@ResponseStatus メソッド・コントローラークラス・例外クラス 返すステータスコードを固定
@ExceptionHandler メソッド 指定した例外を処理
@ControllerAdvice クラス 全コントローラー共通の例外処理など
@RestControllerAdvice クラス @ControllerAdvice+@ResponseBody
@CrossOrigin クラス・メソッド CORSを許可

@ControllerAdvice と @RestControllerAdvice の差は、@Controller と @RestController の差と同じです。JSONを返すAPIでは @RestControllerAdvice を使い、@ExceptionHandler の戻り値をそのまま本文にします。属性を書かなければ全コントローラーが対象で、basePackages・assignableTypes・annotations で対象を絞れます。同じ例外を扱うアドバイスが複数あるときは、@Order の値が小さいほうが選ばれます。4.1.2で2つの @RestControllerAdvice に @Order(0) と @Order(1) を付け、値を入れ替えても、毎回0のほうが応答しました。コントローラー自身に @ExceptionHandler があれば、アドバイスより先にそちらが使われます(Controller Adviceの説明)。

Spring Framework 7から、@RequestMapping と @GetMapping 系に version 属性が加わりました。@GetMapping(path = "/books", version = "1.1") のようにAPIのバージョンごとにハンドラを分けられます。ただし属性を書くだけでは働かず、Spring Boot 4.1では spring.mvc.apiversion.use.header などのプロパティか WebMvcConfigurer#configureApiVersioning で、バージョンをどこから読むかを決めておく必要があります。Spring MVC自体の構成と依存名の変更はSpring MVCとspring-webmvcの役割で扱っています。

@RestController
@RequestMapping("/api/books")
public class BookController {
    private final BookService service;

    public BookController(BookService service) {
        this.service = service;
    }

    @GetMapping("/{id}")
    public String show(@PathVariable("id") Long id) {
        return service.find(id).getTitle();
    }

    // 入力検証とステータス指定の例。DBへの保存処理は省略しています
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public BookRequest create(@Valid @RequestBody BookRequest request) {
        return request;
    }
}

@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(BookNotFoundException.class)
    public ProblemDetail handleNotFound(BookNotFoundException e) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
    }
}

データアクセス(JPA・Spring Data JPA)のアノテーション一覧

データアクセスのアノテーションは提供元が2つに分かれます。エンティティ側はJakarta Persistenceの仕様、リポジトリ側はSpring Dataです。

エンティティとリレーション:@Entity・@OneToMany・@JoinColumn

アノテーション 役割 fetchの既定
@Entity テーブルに対応するクラス –
@Table テーブル名を指定 –
@Id 主キー –
@GeneratedValue 主キーの採番方法 –
@Column 列名・NOT NULL・長さ –
@ManyToOne 多対一(子から親) EAGER
@OneToOne 一対一 EAGER
@OneToMany 一対多(親から子) LAZY
@ManyToMany 多対多 LAZY
@JoinColumn 外部キー列を指定 –

表のfetch既定値は、jakarta.persistence 3.2.0の定義から読んだものです。@ManyToOne は既定でEAGER(関連を即時に読む)なので、一覧取得で親を毎回読み込みN+1問題の原因になります。fetch = FetchType.LAZY を明示しておくのが無難です。LAZYにしても一覧の各行で関連を参照すれば行ごとに追加のクエリが走るので、関連まで表示するならJPQLの join fetch でまとめて読み込みます。

@JoinColumn(name = "author_id") は、外部キーを持つ側(多対一の「多」側)に付けます。反対側の @OneToMany には mappedBy で相手のフィールド名を書き、外部キーの管理を片側に寄せます。エンティティクラスの設計そのものはエンティティクラスとは|JPAとEF Coreの書き方を参照してください。

@Entity
@Table(name = "books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String title;

    private int stock;

    // books.author_id 列で Author エンティティの主キーを参照する
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    private Author author;

    public String getTitle() {
        return title;
    }
}

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // 外部キーは Book 側が持つ。mappedBy には相手側のフィールド名を書く
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Book> books = new ArrayList<>();
}

リポジトリとクエリ:@Query・@Modifying・@Param

アノテーション パッケージ 役割
@Query org.springframework.data.jpa.repository JPQLやネイティブSQLを直接書く
@Modifying org.springframework.data.jpa.repository @QueryがUPDATE/DELETEであることの宣言
@Param org.springframework.data.repository.query 名前付きパラメータを引数と対応づけ
@Transactional org.springframework.transaction.annotation メソッドをトランザクション内で実行

@Param は同じ名前のアノテーションがMyBatisにもあり(org.apache.ibatis.annotations.Param)、Mapperインタフェースではこちらを使います。Spring Data JPAのリポジトリにMyBatis版をimportしても名前付きパラメータとは対応づきません。

@Query で更新系のJPQLを書くときは @Modifying が必須で、付け忘れると実行時に失敗します。一括UPDATEは永続化コンテキストを経由せずにDBを書き換えるため、実行前に読み込んだエンティティは古い値のまま残ります。clearAutomatically = true(既定false)は実行後に永続化コンテキストを空にするだけで、手元のオブジェクトは最新になりません。最新の値が要るなら取得し直します。clearで未反映の変更が捨てられないよう、flushAutomatically = true(既定false)も併せて付けます。

更新系のクエリはトランザクションの中で動かす必要があります。サービス層のメソッドに @Transactional を付けるのが基本ですが、下の例のようにリポジトリのメソッド側に付けても構いません。トランザクション終了後もWeb層でEntityManagerを開いておく既定設定の問題はspring.jpa.open-in-viewとは?OSIVの既定値trueで扱っています。

public interface BookRepository extends JpaRepository<Book, Long> {

    @Query("select b from Book b where b.author.name = :name")
    List<Book> findByAuthorName(@Param("name") String name);

    // UPDATE/DELETE の JPQL には @Modifying が要る
    @Modifying(flushAutomatically = true, clearAutomatically = true)
    @Transactional
    @Query("update Book b set b.stock = b.stock - 1 where b.id = :id and b.stock > 0")
    int decrementStock(@Param("id") Long id);
}

入力チェック(Jakarta Validation)のアノテーション一覧

値の条件を表す制約アノテーションは jakarta.validation.constraints パッケージ、検査対象を指定する @Valid は jakarta.validation パッケージにあります。Spring Bootでは spring-boot-starter-validation を足すと、APIと実装のHibernate Validator(4.1.2のBOMでは9.1.5)がそろいます。検証機能は実装がクラスパスにあることを条件に有効になるので、Validation APIだけを入れた構成では、コンパイルは通っても検査されません。

アノテーション 検査内容 nullの扱い
@NotNull nullでない NG
@NotEmpty nullでも長さ0でもない NG
@NotBlank 空白以外の文字を1つ以上含む NG
@Size(min, max) 文字数・要素数の範囲 OK(素通し)
@Min / @Max 数値の下限・上限 OK(素通し)
@Positive / @PositiveOrZero 正の数/0以上 OK(素通し)
@Email メールアドレス形式 OK(素通し)
@Pattern(regexp) 正規表現に一致 OK(素通し)
@Past / @Future 過去/未来の日時 OK(素通し)
@Valid 引数・入れ子のオブジェクトを検査対象にする –

null・空文字・空白の扱い:@NotNull・@NotEmpty・@NotBlankの違い

取り違えやすいのが、nullと空文字の扱いです。Hibernate Validator 9.1.5で次の3つの入力を検査しました。

入力 @NotNull @NotEmpty @NotBlank @Size(min = 1)
null NG NG NG OK
“”(空文字) OK NG NG NG
” “(空白1つ) OK OK NG OK

必須入力の文字列には @NotBlank を選びます。@Size(min = 1) だけでは、nullも空白だけの入力も通ってしまいます。@Min(1) を付けた Integer も同じで、nullは合格扱いです。必須にしたい項目は @NotNull を重ねます。@NotBlank は文字列専用なので、リストや配列が空でないことを求めるなら @NotEmpty を使います(空の List は違反になりました)。

@Size・@Pattern・@Emailの判定:文字数の数え方と全体一致

同じ9.1.5で、境界になりそうな値を流した結果です。

  • @Sizeの文字数:Javaの String#length() と同じくUTF-16の単位で数えます。「𠮷」のようなサロゲートペアの文字は1文字で2と数えられ、@Size(max = 2) に「𠮷a」(見た目は2文字)を渡すと違反になりました。人名や住所の上限をぎりぎりに設定すると、画面の文字数カウントと食い違います。
  • @Patternは全体一致:regexp = "\\d{3}" は「123」だけを通し、「1234」も「a123」も違反になりました。部分一致ではないので ^ と $ は要りません。nullは素通りしますが、空文字は検査されて違反になります。
  • @Emailは緩い:「a@b」のようにドメインにドットが無い値や、「あ@example.jp」も通りました。空文字も合格です。「a@」と「[email protected]」は違反でした。必須にするなら @NotBlank と重ね、ドメインの形まで縛りたいなら @Email(regexp = ...) で条件を足します。

下の BookRequest では、isbnがnullなら @Pattern を素通りし、”978-4″ なら @Pattern 違反、title=” “・stock=-1 なら @NotBlank と @Min の2件が違反になりました。

public record BookRequest(
        @NotBlank @Size(max = 100) String title,
        @Min(0) int stock,
        @Pattern(regexp = "\\d{13}") String isbn) {
}

@Validと@Validatedの使い分け

検査を起動する側には jakarta.validation.Valid の @Valid と、Springの org.springframework.validation.annotation.Validated の @Validated があります。コントローラーの @RequestBody 引数には @Valid を付けます。@Validated はクラスに付けてサービスのメソッド引数を検査したいときや、グループ指定で検査項目を切り替えたいときに使います。検証をどの層に置くかという設計はバリデーションとは?入力検証の実装パターンと設計で扱っています。

AOP・トランザクション・非同期・リトライのアノテーション一覧

メソッドの前後に処理を差し込むアノテーションです。@Transactional もこの仕組み(プロキシ)で動きます。Spring Bootが自動で有効にするものと、@Enable で始まるアノテーションを自分で付けないと働かないものがあります。

アノテーション 役割 有効化に必要なもの
@Aspect 横断処理をまとめるクラス starter-aspectj(Bootが自動有効化)
@Before / @After 対象メソッドの前/後 同上
@AfterReturning / @AfterThrowing 正常終了時/例外時 同上
@Around 前後を1つのメソッドで囲む 同上
@Pointcut 対象メソッドの条件に名前を付ける 同上
@Order 複数のアスペクトの適用順 不要
@Transactional トランザクション境界 TransactionManager(Bootが自動構成)
@Async 別スレッドで実行 @EnableAsync
@Scheduled 定期実行 @EnableScheduling
@Cacheable / @CacheEvict 結果のキャッシュ @EnableCaching
@Retryable 失敗時の再試行 @EnableResilientMethods
@ConcurrencyLimit 同時実行数の制限 @EnableResilientMethods
@EventListener アプリ内イベントの受信 不要

AOPのアノテーションは org.aspectj.lang.annotation パッケージで、AspectJの記法をSpring AOPが解釈します。Spring Boot 4ではstarterの名前が spring-boot-starter-aop から spring-boot-starter-aspectj に変わり、4.1.2でも旧名のstarterは公開されていません。アスペクトが複数あるときは org.springframework.core.annotation.Order の @Order で順番を決め、値の小さいほうが外側(先に開始し、後に終わる)になります。AOPの仕組みはSpring AOPの仕組みと使い方で詳しく扱っています。

@Aspect
@Order(1)
@Component
public class TimingAspect {

    @Around("execution(* com.example.demo..*Service.*(..))")
    public Object measure(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        try {
            return pjp.proceed();
        } finally {
            System.out.printf("%s: %d ms%n", pjp.getSignature().toShortString(),
                    (System.nanoTime() - start) / 1_000_000);
        }
    }
}

@Transactionalが適用されない条件とロールバック規則

@Transactional を付けたのにトランザクションが始まらない、またはロールバックされないときは、次の3点を確認します。1と2はプロキシが適用されないケース、3は適用されたうえで既定の規則によりコミットされるケースです。

  1. 同じクラス内からの呼び出し:プロキシは外から呼ばれたときだけ割り込めます。下の importAll() から saveOne() を呼ぶのは this への直接呼び出しなので、トランザクションは始まりません。境界は別のBeanへ切り出します。
  2. privateメソッド・finalメソッド:CGLIBのプロキシが上書きできないので効きません。Spring 6.0以降はクラスベースのプロキシならprotectedとパッケージプライベートのメソッドも対象です。
  3. チェック例外:既定でロールバックするのは RuntimeException と Error だけで、IOException などのチェック例外ではコミットされます(ロールバック規則)。@Transactional(rollbackFor = Exception.class) と書くか、アプリ全体を @EnableTransactionManagement(rollbackOn = RollbackOn.ALL_EXCEPTIONS) に切り替えます。
@Service
public class OrderImportService {
    public void importAll(List<Long> ids) {
        for (Long id : ids) {
            saveOne(id); // this.saveOne() と同じ。プロキシを通らないのでトランザクションは始まらない
        }
    }

    @Transactional
    public void saveOne(Long id) {
    }
}

どの層に付けるかと分離レベルの考え方はトランザクションとは?分離レベルの違い・ACID・commit/rollbackで扱っています。

Spring Framework 7で本体入りした@Retryable・@ConcurrencyLimit

@Retryable と @ConcurrencyLimit はSpring Framework 7で本体(org.springframework.resilience.annotation)に入り、設定クラスに @EnableResilientMethods を付けると有効になります。7.0.10のjarでは、@Retryable の既定は maxRetries = 3、delay = 1000(ミリ秒)でした。リファレンスのとおり総試行回数は「初回1回+maxRetries」で、外部ライブラリSpring Retryの maxAttempts(初回を含む回数)とは数え方が違います。値をそのまま移すと試行が1回ずれます。既存のSpring Retryから移るかどうかはSpring Retryの依存関係と@Retryable/@Recover実装で判断材料をまとめています。

セキュリティ(Spring Security)のアノテーション一覧

メソッド単位で認可を掛けるアノテーションです。Spring Boot 4.1.2が管理するSpring Security 7.1.2のjarで確認しました。

アノテーション パッケージ(org.springframework.security以下) 有効化の条件
@EnableWebSecurity config.annotation.web.configuration Bootでは自動構成
@EnableMethodSecurity config.annotation.method.configuration 自分で付ける
@PreAuthorize access.prepost prePostEnabled(既定true)
@PostAuthorize access.prepost prePostEnabled(既定true)
@Secured access.annotation securedEnabled(既定false)
@RolesAllowed (jakarta.annotation.security) jsr250Enabled(既定false)

@EnableMethodSecurity の属性の既定値は、jarの定義では prePostEnabled だけがtrueで、securedEnabled と jsr250Enabled はfalseでした。@Secured や @RolesAllowed を付けても、属性をtrueにしていなければ検査されずに素通りします。新しく書くなら、SpELで条件を書ける @PreAuthorize に寄せるのが簡単です。設定方法はMethod Securityのリファレンス、Spring Security全体の仕組みはSpring Securityとは?認証・認可の仕組みと基本設定にあります。

// prePostEnabled は既定で true。@Secured を使うなら securedEnabled = true を足す
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}

@Service
public class DocumentService {

    // 呼び出す前に検査する
    @PreAuthorize("hasRole('ADMIN')")
    public void delete(Long id) {
    }

    // 戻り値を見てから検査する。returnObject が戻り値
    @PostAuthorize("returnObject.owner() == authentication.name")
    public Document load(Long id) {
        return new Document(id, "alice");
    }
}

テストのアノテーション一覧

Spring Bootのテストは、アプリ全体を起動するか、特定の層だけを起動するか(スライステスト)をアノテーションで選びます。4.1.2でのパッケージを載せています。

アノテーション 起動する範囲 パッケージ(4.1.2)
@SpringBootTest アプリ全体 org.springframework.boot.test.context
@WebMvcTest コントローラー層だけ org.springframework.boot.webmvc.test.autoconfigure
@DataJpaTest JPAリポジトリ層だけ org.springframework.boot.data.jpa.test.autoconfigure
@AutoConfigureMockMvc @SpringBootTestにMockMvcを追加 org.springframework.boot.webmvc.test.autoconfigure
@TestConfiguration テスト専用のBean定義 org.springframework.boot.test.context
@MockitoBean BeanをMockitoのモックに差し替え org.springframework.test.context.bean.override.mockito
@MockitoSpyBean Beanをスパイで包む org.springframework.test.context.bean.override.mockito
@WithMockUser 認証済みユーザーとして実行 org.springframework.security.test.context.support

スライステスト用のアノテーションは、4系では層ごとに別のモジュールへ分かれました。@WebMvcTest を使うには spring-boot-starter-webmvc-test、@DataJpaTest には spring-boot-starter-data-jpa-test を依存に加えます。

@WebMvcTest(BookController.class)
class BookControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockitoBean
    BookService bookService;

    @Test
    void returns404WhenMissing() throws Exception {
        when(bookService.find(99L)).thenThrow(new BookNotFoundException(99L));
        mockMvc.perform(get("/api/books/99")).andExpect(status().isNotFound());
    }
}

Spring Securityを使うアプリでは、テストの依存に spring-boot-starter-security-test を足すかどうかで結果が変わります。4.1.2では @WebMvcTest にセキュリティの自動構成を差し込む設定がこのモジュール側にあり、足さないとセキュリティ無しでコントローラーだけが動きます。足すと、上のテストは404ではなく401で失敗しました。テストメソッドに @WithMockUser を付けると、認証済みとして扱われて404の検証が通りました。

この記事のコード例は、import文と補助クラス(BookNotFoundException・AuditLog・ReportService・Notifier・Document)を省いた抜粋です。省いた部分を補って1つのMavenプロジェクト(Spring Boot 4.1.2・JDK 25)にまとめ、すべてコンパイルが通ること、上の @WebMvcTest のテストが通ることを確かめました。DIの挙動(@Primary・@Qualifier・proxyBeanMethods・初期化の順序・javax版 @PostConstruct)と入力チェックも実際に動かした結果です。

Spring Boot 4で削除・移動したアノテーションと置き換え先

検索で見つかるSpring Bootのアノテーション解説は、多くが2系・3系の時代に書かれています。アノテーション名は同じでも、4系ではimport文が変わったものがあり、古いコードを貼るとコンパイルの段階で止まります。3.5.16と4.1.2のjarを突き合わせた結果が次の表です。

アノテーション 3.5までのパッケージ 4.1での扱い
@MockBean org.springframework.boot.test.mock.mockito 削除。@MockitoBeanへ
@SpyBean org.springframework.boot.test.mock.mockito 削除。@MockitoSpyBeanへ
@WebMvcTest org.springframework.boot.test.autoconfigure.web.servlet org.springframework.boot.webmvc.test.autoconfigureへ移動
@AutoConfigureMockMvc org.springframework.boot.test.autoconfigure.web.servlet 同上
@DataJpaTest org.springframework.boot.test.autoconfigure.orm.jpa org.springframework.boot.data.jpa.test.autoconfigureへ移動
@EntityScan org.springframework.boot.autoconfigure.domain org.springframework.boot.persistence.autoconfigureへ移動
@NonNull / @Nullable org.springframework.lang 非推奨。org.jspecify.annotationsへ
@PostConstruct ほか javax.annotation / javax.inject サポート削除。jakarta版へ

たとえば3系の書き方のまま import org.springframework.boot.test.mock.mockito.MockBean; を残してコンパイルすると、package org.springframework.boot.test.mock.mockito does not exist で失敗します。IDEの自動importで直る移動と違い、@MockBean は名前ごと変わるので、プロジェクト全体を検索して置き換えます。javax版は逆にコンパイルが通ってしまうので、import javax. で検索して洗い出します。

4系で増えたものは @RequestMapping の version 属性、本体入りした @Retryable・@ConcurrencyLimit、HTTPインタフェースのクライアントをまとめて登録する @ImportHttpServices です。3系のプロジェクトに先回りして書くとコンパイルできません。古いSpring Bootのアプリを4系へ移す作業ごと相談したい場合は、フルスクラッチ開発の一環で既存システムの改修も受けています。アノテーション以外を含むSpring Boot 4全体の変更点はSpring Boot 4とは?最新バージョン4.1の変更点、各版のサポート期限はSpring Bootバージョン一覧とサポート期限を参照してください。

よくある質問

@Autowiredは付けなくても動きますか?

コンストラクタが1つだけのクラスなら、付けなくても注入されます。コンストラクタが複数あるときは使うほうに @Autowired を付けます。付けないと引数なしのコンストラクタが選ばれ、それも無ければ起動時にエラーになります。

@DataはSpringのアノテーションですか?

いいえ、Lombokのアノテーションです。@RequiredArgsConstructor も同じくLombokで、未初期化のfinalフィールドと、Lombokの @NonNull を付けた未初期化フィールドを引数に取るコンストラクタを生成します。生成されるコンストラクタが1つだけなら、前の質問のとおり @Autowired なしで注入されます。依存に org.projectlombok:lombok を足さないと使えません。

@Componentと@Serviceはどう違いますか?

動作の差はありません。@Service は @Component をメタアノテーションに持つ別名で、どちらもBeanとして登録されます。@Service を選ぶのは、層の役割をコードで示し、AOPで層単位に対象を選べるようにするためです。

@Controllerと@RestControllerの違いは何ですか?

@RestController は @Controller と @ResponseBody を合わせたものです。@Controller のメソッドが文字列を返すとテンプレート(ThymeleafのHTMLなど)の名前として扱われ、@RestController では戻り値がそのままJSONなどの本文になります。REST APIには @RestController、画面を返すアプリには @Controller を使います。

@Transactionalはどの層に付けるべきですか?

複数のリポジトリ呼び出しを1つの単位にまとめるサービス層のメソッドに付けるのが基本です。コントローラーに付けると外部API呼び出しまでトランザクションに含まれ得て、リポジトリだけに付けると複数の更新が別々にコミットされます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

ほか 9 件の記事からもリンクされています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  4. 2025.02.27 コラム トラッキングビーコン(Webビーコン)とは?仕組み・用途・確認と対策を徹底解説
  5. 2026.10.28 テックブログ 楽天トラベル不正アクセス:最大約260万件の対象確認とカード下4桁の詐欺対策

RELATED POSTS 関連記事

目次