[Java] AOPの「落とし穴」にはまらないために。仕組みを知るべき本当の理由

こんにちは。ゆうせいです。

これまた非常に良い質問ですね!

「AOPは便利だから、魔法として使えればそれでいいのでは?」と思いますよね。

結論から言うと、「仕組み(カラクリ)は絶対に知っておいたほうがいい」 です。

なぜなら、AOPという魔法には「効力が発動しない条件」という落とし穴が明確に存在するからです。

この仕組みを知らないと、将来あなたは「アノテーションをつけたのに動かない!なぜだ!」と、何時間もパソコンの前で頭を抱えることになってしまいます。

今日は、新人エンジニアが必ず一度はハマる「AOPの罠」と、それを回避するために知っておくべき「プロキシ(代理人)」という仕組みについて解説します。


SpringのAOPは「着ぐるみ」を着ている?

SpringのAOPがどのように実現されているか、その裏側の仕組みを一言で言うと 「プロキシ(Proxy)」 です。

プロキシとは日本語で「代理人」という意味ですが、ここでは 「着ぐるみ」 をイメージしてください。

AOPの正体

あなたが「勇者クラス(Hero)」を作ったとします。そして、それに @Transactional (AOP)をつけたとしましょう。

Springが動くとき、実はあなたの作った「生の勇者クラス」はそのまま使われません。

Springは、「勇者の着ぐるみ(プロキシ)」 をこっそり作成し、その中にあなたの勇者クラスを入れ込みます。

外部のプログラムが「勇者、攻撃して!」と命令すると、まずこの「着ぐるみ」が命令を受け取ります。

  1. 着ぐるみ:「攻撃ですね、分かりました。ではトランザクションを開始します(雑用)」
  2. 中身(あなた作):「とりゃー!(実際の攻撃処理)」
  3. 着ぐるみ:「攻撃終わりましたね。ではコミットして保存します(雑用)」

これがAOPの仕組みです。

外から見ると勇者に見えますが、実際には「着ぐるみ」が窓口になり、その前後にこっそり仕事をしているのです。

知っておくべき「AOPが効かない」パターン

さて、ここからが本題です。

この「着ぐるみ(プロキシ)」の仕組みを知らないとハマる落とし穴があります。

それは、「自分のクラスの中で、自分のメソッドを呼び出したとき」 です。

よくある失敗例

例えば、勇者クラスの中に2つの技があるとします。

  • 技A:普通のパンチ
  • 技B:必殺技(@Transactional がついている)

ここで、技Aの中で技Bを呼んでみましょう。

public class Hero {

    // 普通のメソッド
    public void wazaA() {
        // ここで自分のクラスの「技B」を呼ぶ
        this.wazaB();
    }

    // AOPをかけたメソッド
    @Transactional
    public void wazaB() {
        // データベース更新など
    }
}

外部から hero.wazaA() を呼ぶとどうなるでしょうか?

予想では、「技A経由でも、技Bには @Transactional がついているから、そこでAOPが発動するはず」と思いますよね?

残念ながら、この場合、AOPは発動しません。

技Bの @Transactional は完全に無視されます。これが最大の罠です。

なぜ無視されるのか?

ここで先ほどの「着ぐるみ」の話を思い出してください。

  1. 外部から wazaA() が呼ばれると、まず「着ぐるみ」が受け取ります。
  2. 着ぐるみは中身の勇者に「技Aやって!」と伝えます。ここまではOKです。
  3. 中身の勇者は wazaA() を実行します。その中で this.wazaB() を呼びます。

ここがポイントです。this というのは「着ぐるみの中身(自分自身)」のことですよね。

中身の勇者が、自分自身の技Bを直接使うとき、外側の着ぐるみを経由しません。

着ぐるみを経由しないということは、「トランザクション開始などの雑用も行われない」 ということです。

これが、「仕組みを知っておいたほうがいい理由」です。

「AOPは着ぐるみ(プロキシ)を経由したときしか発動しない」というルールを知っていれば、このバグに遭遇したときすぐに原因に気づけます。

AOPの仕組みを理解するメリット

仕組みを理解することには、以下のメリットがあります。

  1. 「動かない」理由がわかる上記のような「内部呼び出し」でAOPが効かないトラブルは、現場で頻発します。仕組みを知っていれば「ああ、プロキシを通ってないからだ」と一瞬で解決できます。
  2. パフォーマンスへの意識が変わる「着ぐるみ」を一枚かぶせている分、わずかですが処理が増えていることが理解できます。無闇にAOPを使いすぎない判断ができるようになります。
  3. デバッグが上手くなるエラーログを見たときに、クラス名の後ろに $$EnhancerBySpring... のような謎の文字列がついていることがあります。仕組みを知っていれば「ああ、これはSpringが作った着ぐるみ(プロキシ)クラスのことだな」と冷静に対処できます。

今後の学習の指針

仕組みを知るといっても、Springの内部コード(ソースコード)をすべて読む必要はありません。

以下のイメージだけ頭の片隅に置いておいてください。

  • AOPは「着ぐるみ(プロキシ)」である。
  • 外からの命令には「着ぐるみ」が反応する。
  • 中での会話(内部呼び出し)には「着ぐるみ」は気づけない。

次のステップ

もし余裕があれば、今回の「内部呼び出し問題」を実際に試してみてください。

  1. @Transactional をつけたメソッドを、同じクラスの別のメソッドから呼んでみる。
  2. わざと例外を発生させて、ロールバックされない(データが保存されてしまう)ことを確認する。

「本当に動かないんだ!」と実感できれば、もうあなたは立派なAOPマスターの入り口に立っています。

仕組みを知ることは、トラブルシューティング最強の武器になります。

焦らず、一つずつ武器を増やしていきましょう!

それでは、また次の記事でお会いしましょう。


セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。