Spring AOPの例のチュートリアル – アスペクト、アドバイス、ポイントカット、ジョインポイント、アノテーション、XMLの設定
Spring Framework(スプリングフレームワーク)は、2つの基本的なコンセプトである「依存性注入」と「アスペクト指向プログラミング(Spring AOP)」に基づいて開発されています。
春のAOP
私たちはすでにSpring Dependency Injectionがどのように機能するかを見ましたが、今日はAspect-Oriented Programmingの基本的なコンセプトと、それをSpring Frameworkを使用して実装する方法について見ていきます。
Spring AOPの概要
ほとんどのエンタープライズアプリケーションには、さまざまな種類のオブジェクトやモジュールに適用される共通の横断的な関心事があります。共通の横断的な関心事の一部は、ログ記録、トランザクション管理、データの検証などです。オブジェクト指向プログラミングでは、アプリケーションのモジュール化はクラスによって達成されますが、アスペクト指向プログラミングではアスペクトによってアプリケーションのモジュール化が達成され、これらのアスペクトは異なるクラスを横断するように設定されます。Spring AOPは、通常のオブジェクト指向プログラミングモデルでは達成できないクラスからの直接的な横断的なタスクの依存を排除します。たとえば、ログ記録のために別のクラスを持つことができますが、再び機能的なクラスはこれらのメソッドを呼び出さなければならず、アプリケーション全体でのログ記録が達成されます。
アスペクト指向プログラミングの基本的な概念
Spring AOPの実装に入る前に、AOPの基本的な概念を理解する必要があります。
-
- アスペクト:アスペクトは、トランザクション管理など、複数のクラスにまたがるエンタープライズアプリケーションの関心事を実装するクラスです。アスペクトは、Spring XML設定を介して通常のクラスとして構成されるか、@Aspectアノテーションを使用してクラスをアスペクトとして定義するためにSpring AspectJの統合を使用できます。
ジョインポイント:ジョインポイントは、メソッドの実行、例外処理、オブジェクト変数の値の変更など、アプリケーション内の特定のポイントを指します。Spring AOPでは、ジョインポイントは常にメソッドの実行です。
アドバイス:アドバイスは、特定のジョインポイントで実行されるアクションです。プログラミングの観点からは、アプリケーションで一致するポイントカットのジョインポイントに到達したときに実行されるメソッドです。アドバイスは、Struts2のインターセプターやServletフィルターのようなものと考えることができます。
ポイントカット:ポイントカットは、アドバイスが実行されるかどうかを決定するためにジョインポイントと一致する式です。ポイントカットは、関連するジョインポイントと一致するさまざまな種類の式を使用し、SpringフレームワークはAspectJポイントカット式言語を使用します。
ターゲットオブジェクト:アドバイスが適用されるオブジェクトです。Spring AOPはランタイムプロキシを使用して実装されているため、このオブジェクトは常にプロキシオブジェクトです。つまり、実行時にサブクラスが作成され、ターゲットメソッドがオーバーライドされ、アドバイスがその構成に基づいて含まれます。
AOPプロキシ:Spring AOPの実装では、JDKダイナミックプロキシを使用して、ターゲットクラスとアドバイス呼び出しのプロキシクラスが作成されます。これらはAOPプロキシクラスと呼ばれます。Spring AOPプロジェクトでの依存関係としてCGLIBプロキシも使用することができます。
ウィービング:アスペクトを他のオブジェクトにリンクしてアドバイス付きのプロキシオブジェクトを作成するプロセスです。これはコンパイル時、ロード時、または実行時に行うことができます。Spring AOPはランタイムでウィービングを実行します。
AOPアドバイスの種類
アドバイスの実行戦略に基づいて、以下のようなタイプがあります。
-
- 前のアドバイス:これらのアドバイスは、ジョインポイントメソッドの実行前に実行されます。Beforeアノテーションを使用して、アドバイスのタイプをBeforeアドバイスとしてマークすることができます。
-
- 後(finally)のアドバイス:ジョインポイントメソッドの実行が正常に終了した場合、または例外をスローした場合に実行されるアドバイスです。Afterアノテーションを使用して、後のアドバイスを作成することができます。
-
- 戻りのアドバイス:ジョインポイントメソッドが正常に実行された場合にのみ、アドバイスメソッドが実行されることがあります。AfterReturningアノテーションを使用して、メソッドを戻りのアドバイスとしてマークすることができます。
-
- 例外のアドバイス:ジョインポイントメソッドが例外をスローした場合にのみ実行されるアドバイスであり、トランザクションを宣言的にロールバックするために使用できます。このタイプのアドバイスには@AfterThrowingアノテーションを使用します。
- アラウンドアドバイス:これは最も重要で強力なアドバイスです。このアドバイスはジョインポイントメソッドを囲み、ジョインポイントメソッドの実行を行うかどうかを選択することもできます。アドバイスコードを記述して、ジョインポイントメソッドの実行前と実行後に実行されるようにすることができます。アラウンドアドバイスは、ジョインポイントメソッドを呼び出し、メソッドが何かを返す場合には値を返す責任があります。アラウンドアノテーションを使用して、アラウンドアドバイスメソッドを作成します。
上記のポイントは混乱を引き起こすかもしれませんが、Spring AOPの実装を見れば、より明確になるでしょう。では、AOPの実装がされたシンプルなSpringプロジェクトを作成してみましょう。Springでは、簡単さのためにAspectJアノテーションを使用してアスペクトを作成することができます。上記のAOPのアノテーションは、org.aspectj.lang.annotationパッケージに定義されています。Spring Tool Suiteはアスペクトについての有用な情報を提供していますので、使用することをおすすめします。もしSTSに慣れていない場合は、Spring MVCチュートリアルを見てみることをおすすめします。そこでSTSの使い方を説明しています。
春のAOPの例
新しいSimple Spring Mavenプロジェクトを作成し、pom.xmlファイルにSpring Coreライブラリがすべて含まれるようにします。明示的に追加する必要はありません。最終的なプロジェクトは以下の画像のようになります。Springのコアコンポーネントとアスペクトの実装について詳しく見ていきます。
Spring AOP AspectJ の依存関係
スプリングフレームワークはデフォルトでAOPサポートを提供しますが、アスペクトやアドバイスの設定にAspectJのアノテーションを使用しているため、pom.xmlファイルにそれらを含める必要があります。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringAOPExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
<!-- AspectJ -->
<aspectj.version>1.7.4</aspectj.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
プロジェクトに、aspectjrtとaspectjtoolsの依存関係(バージョン1.7.4)を追加しました。また、Springフレームワークのバージョンを最新の4.0.2.RELEASEに更新しました。
モデルクラス
私たちの例で使用するシンプルなJava Beanを作成しましょう。Employee.javaコードにいくつかの追加メソッドを追加します。
package com.scdev.spring.model;
import com.scdev.spring.aspect.Loggable;
public class Employee {
private String name;
public String getName() {
return name;
}
@Loggable
public void setName(String nm) {
this.name=nm;
}
public void throwException(){
throw new RuntimeException("Dummy Exception");
}
}
“setName()メソッドがLoggable注釈でアノテーションされていることに気づきましたか。これはプロジェクト内で私たちが定義したカスタムのJavaアノテーションです。後でその使用方法を確認します。”
サービスクラス
従業員ビーンと一緒に作業するためのサービスクラスを作成しましょう。EmployeeService.javaのコード:
package com.scdev.spring.service;
import com.scdev.spring.model.Employee;
public class EmployeeService {
private Employee employee;
public Employee getEmployee(){
return this.employee;
}
public void setEmployee(Employee e){
this.employee=e;
}
}
このプロジェクトでは、Springの注釈を使用してSpringコンポーネントとして設定することもできましたが、XMLベースの設定を使用します。EmployeeServiceクラスは非常に標準的であり、単にEmployeeビーンへのアクセスポイントを提供しています。
AOPを使ったSpring Beanの設定
もしSTSを使っている場合、”Spring Bean Configuration File”を作成してAOPスキーマの名前空間を選択することができますが、他のIDEを使っている場合は、springのbean設定ファイルに簡単に追加することができます。私のプロジェクトのbean設定ファイルは以下のようになっています。spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />
<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.scdev.spring.model.Employee">
<property name="name" value="Dummy Name"></property>
</bean>
<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.scdev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.scdev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.scdev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.scdev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.scdev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.scdev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.scdev.spring.aspect.EmployeeAnnotationAspect" />
</beans>
SpringビーンでSpring AOPを使用するには、次の手順を実行する必要があります。
-
- 「xmlns:aop=”https://www.springframework.org/schema/aop”」のようにAOPの名前空間を宣言する
-
- 実行時に自動プロキシを持つSpring AspectJサポートを有効にするために「aop:aspectj-autoproxy」要素を追加する
- Aspectクラスを他のSpring beansとして設定する
スプリングのBean設定ファイルには多くの側面が定義されていますので、一つずつ見てみる時が来ました。
SpringのAOP Beforeアスペクトの例えを教えてください。
EmployeeAspect.javaのコード:
package com.scdev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspect {
@Before("execution(public String getName())")
public void getNameAdvice(){
System.out.println("Executing Advice on getName()");
}
@Before("execution(* com.scdev.spring.service.*.get*())")
public void getAllAdvice(){
System.out.println("Service method getter called");
}
}
上記のアスペクトクラスでの重要なポイントは次のとおりです。
- Aspect classes are required to have @Aspect annotation.
- @Before annotation is used to create Before advice
- The string parameter passed in the @Before annotation is the Pointcut expression
- getNameAdvice() advice will execute for any Spring Bean method with signature public String getName(). This is a very important point to remember, if we will create Employee bean using new operator the advices will not be applied. Only when we will use ApplicationContext to get the bean, advices will be applied.
- We can use asterisk (*) as wild card in Pointcut expressions, getAllAdvice() will be applied for all the classes in com.scdev.spring.service package whose name starts with get and doesn’t take any arguments.
私たちは、あらゆる種類のアドバイスを調べた後に、テストクラスでそのアドバイスを実践してみましょう。
Spring AOPのポイントカットメソッドと再利用
時々、同じポイントカット式を複数の場所で使用する必要がある場合、@Pointcutアノテーションを持つ空のメソッドを作成し、それをアドバイスの式として使用することができます。EmployeeAspectPointcut.javaのコード:
package com.scdev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class EmployeeAspectPointcut {
@Before("getNamePointcut()")
public void loggingAdvice(){
System.out.println("Executing loggingAdvice on getName()");
}
@Before("getNamePointcut()")
public void secondAdvice(){
System.out.println("Executing secondAdvice on getName()");
}
@Pointcut("execution(public String getName())")
public void getNamePointcut(){}
@Before("allMethodsPointcut()")
public void allServiceMethodsAdvice(){
System.out.println("Before executing service method");
}
//Pointcut to execute on all the methods of classes in a package
@Pointcut("within(com.scdev.spring.service.*)")
public void allMethodsPointcut(){}
}
上記の例は非常に明確です。アドバイス注釈引数には、表現ではなくメソッド名を使用しています。
Spring AOPのJoinPointとAdviceの引数
アドバイスメソッドでJoinPointをパラメータとして使用し、その中からメソッドの署名や対象オブジェクトを取得することができます。引数パターンに一致するメソッドに適用するために、ポイントカットでargs()式を使用することができます。これを使用する場合、引数の型が決まるのはアドバイスメソッドで同じ名前を使用する必要があります。アドバイスの引数にはジェネリックオブジェクトも使用できます。EmployeeAspectJoinPoint.javaのコードを以下に示します。
package com.scdev.spring.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspectJoinPoint {
@Before("execution(public void com.scdev.spring.model..set*(*))")
public void loggingAdvice(JoinPoint joinPoint){
System.out.println("Before running loggingAdvice on method="+joinPoint.toString());
System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));
}
//Advice arguments, will be applied to bean methods with single String argument
@Before("args(name)")
public void logStringArguments(String name){
System.out.println("String argument passed="+name);
}
}
Spring AOPのアフターアドバイスの例
単純なアスペクトクラスを見て、アフター、アフタースローイング、およびアフターリターニングアドバイスの例を説明しましょう。EmployeeAfterAspect.javaコードをご覧ください。
package com.scdev.spring.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAfterAspect {
@After("args(name)")
public void logStringArguments(String name){
System.out.println("Running After Advice. String argument passed="+name);
}
@AfterThrowing("within(com.scdev.spring.model.Employee)")
public void logExceptions(JoinPoint joinPoint){
System.out.println("Exception thrown in Employee Method="+joinPoint.toString());
}
@AfterReturning(pointcut="execution(* getName())", returning="returnString")
public void getNameReturningAdvice(String returnString){
System.out.println("getNameReturningAdvice executed. Returned String="+returnString);
}
}
クラス内のすべてのメソッドにアドバイスを適用するために、ポイントカット式内でwithinキーワードを使用できます。アドバイスされたメソッドから返されるオブジェクトを取得するために、@AfterReturningアドバイスを使用できます。After Throwingアドバイスの使用例として、EmployeeビーンにthrowException()メソッドがあります。
Spring AOP の Around アスペクトの例
前述の通り、Aroundアスペクトを使用して、メソッドの実行を前後で切り取ることができます。アドバイス対象のメソッドが実行されるかどうかを制御するために使用することもできます。また、返された値を検査して変更することもできます。これは最も強力なアドバイスであり、適切に適用する必要があります。EmployeeAroundAspect.javaのコード:
package com.scdev.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAroundAspect {
@Around("execution(* com.scdev.spring.model.Employee.getName())")
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("After invoking getName() method. Return value="+value);
return value;
}
}
アドバイスには常にProceedingJoinPointが引数として必要で、それを使用してアドバイスが対象オブジェクトのメソッドを呼び出すためにproceed()メソッドを使用する必要があります。アドバイスされたメソッドが返り値を返す場合、返り値はアドバイスの責任で呼び出し元のプログラムに返されます。voidメソッドの場合、アドバイスメソッドはnullを返すことができます。アラウンドアドバイスはアドバイスされたメソッドを包み込むため、メソッドの入力や出力、実行の振る舞いを制御することができます。
カスタム注釈ポイントカットを使用した春のアドバイス
上記のアドバイスポイントカット式をすべて見ると、意図されていない他のビーンに適用される可能性があります。たとえば、誰かがgetName()メソッドを持つ新しいSpringビーンを定義した場合、そのアドバイスが意図しないところに適用されるようになります。そのため、ポイントカット式のスコープはできるだけ狭く保つ必要があります。別の方法として、カスタム注釈を作成し、アドバイスを適用したいメソッドに注釈を付けることがあります。これが、@Loggable注釈を使用してEmployeeのsetName()メソッドに注釈を付ける目的です。Spring Frameworkの@Transactional注釈は、Spring Transaction Managementのこのアプローチの素晴らしい例です。Loggable.javaのコードは以下の通りです。
package com.scdev.spring.aspect;
public @interface Loggable {
}
従業員のアノテーションの側面を担当するEmployeeAnnotationAspect.javaコード
package com.scdev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAnnotationAspect {
@Before("@annotation(com.scdev.spring.aspect.Loggable)")
public void myAdvice(){
System.out.println("Executing myAdvice!!");
}
}
myAdvice()メソッドは、setName()メソッドにだけアドバイスを提供します。これは非常に安全なアプローチであり、どのメソッドにアドバイスを適用したい場合は、そのメソッドにLoggableアノテーションを付けるだけです。
Spring AOP XMLの設定
私は常にアノテーションを好むが、スプリングの設定ファイルでアスペクトを設定するオプションもあります。たとえば、以下のようなクラスがあるとしましょう。EmployeeXMLConfigAspect.javaのコード:
package com.scdev.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class EmployeeXMLConfigAspect {
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);
return value;
}
}
Spring Beanの設定ファイルに以下の設定を含めることで、それを設定することができます。
<bean name="employeeXMLConfigAspect" class="com.scdev.spring.aspect.EmployeeXMLConfigAspect" />
<!-- Spring AOP XML Configuration -->
<aop:config>
<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
<aop:pointcut expression="execution(* com.scdev.spring.model.Employee.getName())" id="getNamePointcut"/>
<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
</aop:aspect>
</aop:config>
AOPのXML設定要素は、その名前から目的が明確なので、詳細には立ち入りません。
春のAOPの例
簡単なSpringプログラムを作成して、これらの側面がbeanメソッドを通過する様子を見てみましょう。SpringMain.javaのコードです。
package com.scdev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.scdev.spring.service.EmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
System.out.println(employeeService.getEmployee().getName());
employeeService.getEmployee().setName("Pankaj");
employeeService.getEmployee().throwException();
ctx.close();
}
}
上記のプログラムを実行すると、以下の出力結果が得られます。
Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.scdev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.scdev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
at com.scdev.spring.model.Employee.throwException(Employee.java:19)
at com.scdev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
at com.scdev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
at com.scdev.spring.main.SpringMain.main(SpringMain.java:17)
アドバイスはポイントカットの設定に基づいて1つずつ実行されていることがわかります。混乱を避けるために、1つずつ設定してください。これがSpring AOPの例のチュートリアルの全てです。SpringとAOPの基本を学び、例からさらに学ぶことができれば幸いです。以下のリンクからサンプルプロジェクトをダウンロードし、自由に操作してみてください。
「Spring AOPプロジェクトをダウンロードしてください。」