DI/AOP

Spring BootでfinalメソッドにAOPを適用してみた

AOP(Aspect Oriented Programming)を利用する際は、そのAOP指定対象となるメソッドがfinalメソッドの場合はNullPointerExceptionが発生してしまうことがわかったので、今回はそのサンプルプログラムを共有する。

AOPについては、以下の記事に記載しているので参照のこと。

Spring BootでAOPを利用してみたSpringフレームワークの基本として、AOP(Aspect Oriented Programming)という概念がある。 AOP...

AOPの指定対象となるメソッドがfinalメソッドで、そのfinalメソッド内で何らかのDIオブジェクトを参照しようとすると、finalメソッドのサブクラスでDIオブジェクト(=proxy)を作成しようとして作成できないため、NullPointerExceptionが発生してしまう。その詳細については以下のサイトを参照のこと。
https://backpaper0.github.io/2018/02/22/spring_proxy.html

そのため、AOPの指定対象となるメソッドに、finalメソッドを含まないよう注意する必要がある。

前提条件

下記記事の実装、及び、user_dataテーブルへのデータの登録が完了していること

Spring BootでAjaxを利用してみたSpring BootのWEB画面上では、Ajax通信も行うことができる。今回は、jQueryを利用しない形で、Ajax通信を含むサンプ...

サンプルプログラムの作成

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから変更または追加したプログラムである。

まず、AOPを適用しているクラスの内容は以下の通りで、コントローラクラス/サービスクラスそれぞれでアノテーションの位置を切り替えられるようにしている。



次に、コントローラクラスの内容は以下の通りで、途中サービスクラスを利用してユーザーデータを取得するようになっている。

また、サービスクラスの内容は以下の通りで、ユーザーデータを取得するメソッドを、finalを使う場合と使わない場合それぞれで定義している。

HTMLファイルの内容は以下の通りで、初期表示画面「index.html」、final未使用_確認画面「index_nofinal.html」、final使用_確認画面「index_final.html」をそれぞれ定義している。

また、build.gradleの内容は以下の通り。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-aop-final/demo

サンプルプログラムの実行結果

DemoInvocation.javaで、サンプルプログラム記載通りに、finalメソッドを含まないコントローラクラスがAOP対象の場合の実行結果は以下の通りで、「final使用_確認画面」が問題なく参照できることが確認できる。

1) Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスし、「final未使用_確認画面に遷移」ボタンを押下
実行結果_1

2) ユーザーデータが取得され、JSON形式で表示されることが確認できる
実行結果_2

3) 1)と同じ画面上で、「final使用_確認画面に遷移」ボタンを押下
実行結果_3

4) 2)と同様に、ユーザーデータが取得され、JSON形式で表示されることが確認できる
実行結果_4

また、DemoInvocation.javaで、@Before/@Afterの各アノテーションの指定を、finalメソッドを含むサービスクラスに変更した場合の実行結果は以下の通りで、「final未使用_確認画面」は参照できるものの、「final使用_確認画面」が参照できないことが確認できる。

5) Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスし、「final未使用_確認画面に遷移」ボタンを押下
実行結果_5

6) ユーザーデータが取得され、JSON形式で表示されることが確認できる
実行結果_6

7) 5)と同じ画面上で、「final使用_確認画面に遷移」ボタンを押下
実行結果_7

8) 正常な画面遷移が行えず、エラーが表示されることが確認できる
実行結果_8_1

また、このときのコンソールログの内容は以下の通りで、getUserDataByFinalメソッド内の「repository.findUserDataById(1L)」を実行する際に、NullPointerExceptionが発生していることが確認できる。
実行結果_8_2

要点まとめ

  • AOPの指定対象となるメソッドがfinalメソッドの場合、NullPointerExceptionが発生する場合があるので、AOPの指定対象となるメソッドには、finalを含まないようにする必要がある。