[Java] AOPの「落とし穴」にはまらないために。仕組みを知るべき本当の理由
こんにちは。ゆうせいです。
これまた非常に良い質問ですね!
「AOPは便利だから、魔法として使えればそれでいいのでは?」と思いますよね。
結論から言うと、「仕組み(カラクリ)は絶対に知っておいたほうがいい」 です。
なぜなら、AOPという魔法には「効力が発動しない条件」という落とし穴が明確に存在するからです。
この仕組みを知らないと、将来あなたは「アノテーションをつけたのに動かない!なぜだ!」と、何時間もパソコンの前で頭を抱えることになってしまいます。
今日は、新人エンジニアが必ず一度はハマる「AOPの罠」と、それを回避するために知っておくべき「プロキシ(代理人)」という仕組みについて解説します。
SpringのAOPは「着ぐるみ」を着ている?
SpringのAOPがどのように実現されているか、その裏側の仕組みを一言で言うと 「プロキシ(Proxy)」 です。
プロキシとは日本語で「代理人」という意味ですが、ここでは 「着ぐるみ」 をイメージしてください。
AOPの正体
あなたが「勇者クラス(Hero)」を作ったとします。そして、それに @Transactional (AOP)をつけたとしましょう。
Springが動くとき、実はあなたの作った「生の勇者クラス」はそのまま使われません。
Springは、「勇者の着ぐるみ(プロキシ)」 をこっそり作成し、その中にあなたの勇者クラスを入れ込みます。
外部のプログラムが「勇者、攻撃して!」と命令すると、まずこの「着ぐるみ」が命令を受け取ります。
- 着ぐるみ:「攻撃ですね、分かりました。ではトランザクションを開始します(雑用)」
- 中身(あなた作):「とりゃー!(実際の攻撃処理)」
- 着ぐるみ:「攻撃終わりましたね。ではコミットして保存します(雑用)」
これが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 は完全に無視されます。これが最大の罠です。
なぜ無視されるのか?
ここで先ほどの「着ぐるみ」の話を思い出してください。
- 外部から
wazaA()が呼ばれると、まず「着ぐるみ」が受け取ります。 - 着ぐるみは中身の勇者に「技Aやって!」と伝えます。ここまではOKです。
- 中身の勇者は
wazaA()を実行します。その中でthis.wazaB()を呼びます。
ここがポイントです。this というのは「着ぐるみの中身(自分自身)」のことですよね。
中身の勇者が、自分自身の技Bを直接使うとき、外側の着ぐるみを経由しません。
着ぐるみを経由しないということは、「トランザクション開始などの雑用も行われない」 ということです。
これが、「仕組みを知っておいたほうがいい理由」です。
「AOPは着ぐるみ(プロキシ)を経由したときしか発動しない」というルールを知っていれば、このバグに遭遇したときすぐに原因に気づけます。
AOPの仕組みを理解するメリット
仕組みを理解することには、以下のメリットがあります。
- 「動かない」理由がわかる上記のような「内部呼び出し」でAOPが効かないトラブルは、現場で頻発します。仕組みを知っていれば「ああ、プロキシを通ってないからだ」と一瞬で解決できます。
- パフォーマンスへの意識が変わる「着ぐるみ」を一枚かぶせている分、わずかですが処理が増えていることが理解できます。無闇にAOPを使いすぎない判断ができるようになります。
- デバッグが上手くなるエラーログを見たときに、クラス名の後ろに $$EnhancerBySpring... のような謎の文字列がついていることがあります。仕組みを知っていれば「ああ、これはSpringが作った着ぐるみ(プロキシ)クラスのことだな」と冷静に対処できます。
今後の学習の指針
仕組みを知るといっても、Springの内部コード(ソースコード)をすべて読む必要はありません。
以下のイメージだけ頭の片隅に置いておいてください。
- AOPは「着ぐるみ(プロキシ)」である。
- 外からの命令には「着ぐるみ」が反応する。
- 中での会話(内部呼び出し)には「着ぐるみ」は気づけない。
次のステップ
もし余裕があれば、今回の「内部呼び出し問題」を実際に試してみてください。
@Transactionalをつけたメソッドを、同じクラスの別のメソッドから呼んでみる。- わざと例外を発生させて、ロールバックされない(データが保存されてしまう)ことを確認する。
「本当に動かないんだ!」と実感できれば、もうあなたは立派なAOPマスターの入り口に立っています。
仕組みを知ることは、トラブルシューティング最強の武器になります。
焦らず、一つずつ武器を増やしていきましょう!
それでは、また次の記事でお会いしましょう。
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール
- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。