0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

@PostConstruct / @PreDestroy で、生成直後・破棄直前にフックする

0
Last updated at Posted at 2026-09-03

はじめに

前回の記事では、DIコンテナが起動時に「①収集→②生成→③注入」という3フェーズをこなしていることを見ました。

ここで1つ疑問が残ります。@Autowired による注入が終わった瞬間、そのBeanは本当に「使える状態」になっているのでしょうか?

たとえば、次のような処理をしたい場面を考えてみてください。

  • 注入された設定値を使って、キャッシュを事前に温めておきたい
  • 注入されたコネクション情報を使って、DB接続を開いておきたい
  • アプリ終了時に、開いたコネクションをきちんと閉じたい

これらは「依存性が注入された あと」でないとできませんし、「コンテナが破棄される 直前」に確実に実行してほしい処理です。今回は、この「注入完了後」と「破棄直前」にフックする仕組みを見ていきます。

対象読者:

  • 前回・前々回の記事を読んだ人
  • Beanの初期化処理をどこに書けばいいか迷ったことがある人
  • アプリ終了時のリソース解放処理に不安がある人

1. Beanのライフサイクル全体像

前回図示した「収集→生成→注入」の流れに、今回のテーマである「初期化」と「破棄」を足すと、Beanの一生はこうなります。

今回説明する @PostConstruct@PreDestroy は、まさに④と⑥、「注入の直後」と「破棄の直前」というピンポイントのタイミングにフックするためのアノテーションです。


2. @PostConstruct:注入が終わった直後に呼ばれる

@PostConstruct を付けたメソッドは、そのBeanに対する依存性の注入が全て完了した直後に、1回だけ 呼び出されます。

@Component
class ClassA {

    @Autowired
    InterfaceI i;

    @PostConstruct
    public void init() {
        // この時点で i は注入済みであることが保証されている
        System.out.println("ClassAの初期化処理を実行");
        i.methodA(); // すでに利用可能
    }
}

ポイントは、コンストラクタの中では保証できないことが、@PostConstruct の中では保証されている という点です。

前々回の記事で触れた通り、フィールド注入は「①空のインスタンスを作ってから、②あとからフィールドに値を差し込む」という2段階で行われます。つまり、コンストラクタが実行されている瞬間はまだ inull の可能性がある、ということです。

「注入されたものを使った初期化処理」は、コンストラクタではなく @PostConstruct に書く。これが鉄則です。

前回の記事で推奨したコンストラクタ注入を使っている場合は、コンストラクタの中で初期化してしまっても NullPointerException にはなりません。しかし、コンストラクタは「オブジェクトの生成と値のセット」にとどめ、DB接続や外部API呼び出しのような重い業務的初期化処理は @PostConstruct に切り出すのが、設計として美しいとされています。


3. @PreDestroy:破棄される直前に呼ばれる

@PreDestroy は、その逆です。アプリケーションの終了処理(コンテナのシャットダウン)が始まると、Springは管理しているBeanを破棄していきますが、そのBeanが実際にメモリ上から消える 直前 に、@PreDestroy を付けたメソッドを呼び出してくれます。

@Component
class ClassA {

    @Autowired
    InterfaceI i;

    @PreDestroy
    public void cleanup() {
        // コネクションを閉じる、キャッシュを退避する、などの後片付け
        System.out.println("ClassAの終了処理を実行");
    }
}

これがあることで、「開いたリソースは、閉じるコードとセットで、同じクラスの中に書いておける」ようになります。呼び出し側が明示的に close() を呼び忘れる、といった事故を防ぎやすくなるわけです。


4. 補足:InitializingBean / DisposableBean というもう1つの方法

余談ですが、Springには @PostConstruct / @PreDestroy が登場する以前から使われている、インタフェースベースの方法も存在します。

方法 初期化 破棄
アノテーション(今回紹介) @PostConstruct @PreDestroy
インタフェース実装(旧来) InitializingBean#afterPropertiesSet() DisposableBean#destroy()

@PostConstruct はJava標準(JSR-250)のアノテーションであり、Spring独自の仕組みに縛られないというメリットがあるため、現在は基本的にこちらが使われます。「昔のコードでこんなメソッドを見かけたら、今回の話と同じ役割だ」と分かれば十分です。

⚠️ 注意:Spring Boot 3からのパッケージ変更(javax vs jakarta

@PostConstruct@PreDestroy をインポートする際、使用しているSpring Bootのバージョンによって選ぶべきパッケージが異なります。

  • Spring Boot 2.xまで:javax.annotation.PostConstruct
  • Spring Boot 3.x以降:jakarta.annotation.PostConstruct

IDE(EclipseやIntelliJなど)の自動インポートで古い javax の方を選んでしまうと、アノテーションを付けても一切実行されません(エラーにもなりません)。最近のSpring Bootを使っている場合は、必ず jakarta から始まる方をインポートしてください。


5. 注意点:シングルトン以外では@PreDestroyが呼ばれないことがある

前々回の記事で「Beanは基本シングルトン」という話をしましたが、これは今回のテーマにも関係します。

@PreDestroy がきちんと呼ばれるのは、シングルトンスコープのBean に限られます。@Scope("prototype") を指定したBeanは、コンテナが生成には関与しても、その後の破棄タイミングまでは管理してくれません。プロトタイプスコープのBeanで後片付けが必要な場合は、呼び出し側が自分で面倒を見る必要がある、という点は覚えておいてください。


まとめ

  • Beanのライフサイクルは「生成→注入→初期化→利用→破棄前処理→破棄」という流れ
  • @PostConstruct は依存性の注入が完了した直後に1回だけ呼ばれる。「注入されたものを使う初期化処理」はここに書く
  • @PreDestroy はコンテナのシャットダウン時、Beanが破棄される直前に呼ばれる。リソースの後片付けに使う
  • 同じ役割を持つ古い方法として InitializingBean / DisposableBean インタフェースもあるが、現在はアノテーションが主流
  • @PreDestroy はシングルトンスコープでのみ確実に呼ばれる

「注入されたら、はいおしまい」ではなく、その前後にもきちんとフックできる場所が用意されている——これでBeanの一生を一通り追えたことになります。

0
1
0

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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?