1. 導入:なぜInvocationHandlerが重要なのか
Javaの開発において、似たような処理(ログ出力、トランザクション管理、権限チェックなど)を複数のクラスに書くのは非常に非効率です。これを解決するのが「動的プロキシ(Dynamic Proxy)」という手法です。InvocationHandlerを使うことで、元のクラスのコードを一切書き換えることなく、実行時に後から機能を追加したり、処理を差し込んだりすることが可能になります。
2. 基礎知識:動的プロキシの仕組み
動的プロキシを理解するために、以下のキーワードを整理しましょう。
・Proxy: 実行時にインターフェースを実装したクラスを自動生成する仕組みです。
・InvocationHandler: 生成されたプロキシに対するメソッド呼び出しを「横取り」するためのインターフェースです。
・invokeメソッド: プロキシ経由でメソッドが呼ばれた際、実際に実行される処理を記述する場所です。
・アノテーション(Retention/Target): メタデータとしてクラスやメソッドに情報を付与します。特にRetentionPolicy.RUNTIMEを指定することで、リフレクションを使って実行時にアノテーション情報を取得できるようになります。
3. 実装/解決策:動的プロキシの作り方
動的プロキシを実装する手順は以下の通りです。
1. インターフェースを定義する。
2. InvocationHandlerを実装したクラスを作成し、invokeメソッド内に共通処理を書く。
3. Proxy.newProxyInstanceを使用して、プロキシインスタンスを生成する。
4. サンプルプログラム
以下のコードは、メソッド呼び出し時に自動でログを出力する簡単な例です。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 対象のインターフェース
interface Service {
void execute();
}
// 2. 実際の処理クラス
class ServiceImpl implements Service {
public void execute() {
System.out.println("メインの処理を実行中...");
}
}
// 3. InvocationHandlerの実装
class LogHandler implements InvocationHandler {
private final Object target;
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// メソッド実行前の処理
System.out.println("[ログ] メソッド開始: " + method.getName());
// 本来の処理を実行
Object result = method.invoke(target, args);
// メソッド実行後の処理
System.out.println("[ログ] メソッド終了");
return result;
}
}
public class Main {
public static void main(String[] args) {
Service realService = new ServiceImpl();
// プロキシの生成
Service proxy = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new LogHandler(realService)
);
// プロキシ経由で実行
proxy.execute();
}
}
5. 応用・注意点:現場での活用と落とし穴
・パフォーマンスへの影響: リフレクションは通常のメソッド呼び出しよりもオーバーヘッドが発生します。高頻度で呼び出される箇所での多用は避けましょう。
・デバッグの難しさ: プロキシ経由で実行されるため、スタックトレースが複雑になりがちです。予期せぬ挙動をした際は、invokeメソッド内で引数やメソッド名をログ出力して追跡してください。
・アノテーションとの併用: invokeメソッド内で method.isAnnotationPresent(YourAnnotation.class) をチェックすることで、「特定のアノテーションが付いているメソッドだけ処理を差し替える」といった高度な制御が可能になります。これがSpring Frameworkなどのフレームワークが裏側で行っている魔法の正体です。
まずはシンプルなログ出力から試して、少しずつ動的プロキシの便利さを体感してみてください。これが理解できると、Javaのフレームワークの裏側の仕組みが手に取るようにわかるようになりますよ!

コメント