Spring Frameworkは、依存性注入とアスペクト指向プログラミング(Spring AOP)の2つのコアコンセプトに基づいて開発されています。
Spring AOP
すでにSpringの依存性注入がどのように機能するかを見てきましたが、今日はアスペクト指向プログラミングのコアコンセプトと、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ダイナミックプロキシを使用して、ターゲットクラスとアドバイスの呼び出しでProxyクラスを作成します。これらはAOPプロキシクラスと呼ばれます。Spring AOPプロジェクトに依存関係としてCGLIBプロキシを追加することで、CGLIBプロキシも使用できます。
- 織り込み:アスペクトを他のオブジェクトとリンクしてアドバイスされたプロキシオブジェクトを作成するプロセスです。これは、コンパイル時、ロード時、または実行時に行うことができます。Spring AOPは、実行時に織り込みを行います。
AOPアドバイスの種類
アドバイスの実行戦略に基づいて、次のタイプがあります。
- 前のアドバイス:これらのアドバイスは、結合ポイントメソッドの実行の前に実行されます。アドバイスタイプを前のアドバイスとしてマークするには、
@Before
注釈を使用できます。 - 後(最終的な)アドバイス:結合ポイントメソッドが通常または例外を投げて実行を終了した後に実行されるアドバイスです。
@After
注釈を使用して後のアドバイスを作成できます。 - 戻り後のアドバイス:結合ポイントメソッドが正常に実行された場合にのみ、アドバイスメソッドを実行することが必要な場合があります。戻り後のアドバイスとしてメソッドをマークするには、
@AfterReturning
注釈を使用できます。 - アドバイスの投げる後: このアドバイスは、結合ポイントのメソッドが例外をスローしたときにのみ実行され、トランザクションを宣言的にロールバックするために使用できます。この種のアドバイスには、
@AfterThrowing
注釈を使用します。 - 周囲のアドバイス: これは最も重要で強力なアドバイスです。このアドバイスは、結合ポイントのメソッドを囲み、結合ポイントのメソッドを実行するかどうかを選択することもできます。結合ポイントのメソッドの実行の前後に実行されるアドバイスコードを記述できます。周囲のアドバイスは、結合ポイントのメソッドを呼び出し、メソッドが何かを返す場合は値を返す責任があります。周囲のアドバイスメソッドを作成するには、
@Around
注釈を使用します。
上記のポイントは混乱するかもしれませんが、Spring AOPの実装を見れば、事情がより明確になります。SpringプロジェクトにAOPの実装を簡単に行うために、SpringはAspectJ注釈を使用するサポートを提供しています。すべての上記のAOP注釈は、org.aspectj.lang.annotation
パッケージに定義されています。Spring Tool Suiteは、アスペクトに関する有用な情報を提供しているので、それを使用することをお勧めします。STSに慣れていない場合は、Spring MVCチュートリアルを見てみることをお勧めします。
Spring AOPの例
新しいシンプルなSpring Mavenプロジェクトを作成して、Spring Coreライブラリがpom.xmlファイルに自動的に含まれるようにします。最終的なプロジェクトは以下の画像のようになります。Springのコアコンポーネントとアスペクトの実装について詳しく見ていきます。
Spring AOP AspectJの依存関係
Springフレームワークはデフォルトで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ビーンを作成しましょう。この例で使用するいくつかの追加メソッドがあります。Employee.javaのコード:
package com.journaldev.spring.model;
import com.journaldev.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.journaldev.spring.service;
import com.journaldev.spring.model.Employee;
public class EmployeeService {
private Employee employee;
public Employee getEmployee(){
return this.employee;
}
public void setEmployee(Employee e){
this.employee=e;
}
}
I could have used Spring annotations to configure it as a Spring Component, but we will use XML based configuration in this project. EmployeeService class is very standard and just provides us an access point for Employee beans.
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.journaldev.spring.model.Employee">
<property name="name" value="Dummy Name"></property>
</bean>
<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" />
</beans>
SpringビーンでSpring AOPを使用するには、次の操作を行う必要があります:
- AOP名前空間をxmlns:aop=”に宣言するhttps://www.springframework.org/schema/aop””
- aop:aspectj-autoproxy要素を追加して、実行時にSpring AspectJサポートを自動プロキシで有効にする
- 他のSpringビーンとしてアスペクトクラスを構成します
以下は、Springビーン構成ファイルで定義された多くの側面があることがわかります。それらを1つずつ調べる時が来ました。
Spring AOPの前のアスペクトの例
EmployeeAspect.javaのコード:
package com.journaldev.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.journaldev.spring.service.*.get*())")
public void getAllAdvice(){
System.out.println("Service method getter called");
}
}
上記のアスペクトクラスの重要なポイントは次のとおりです:
- アスペクトクラスには
@Aspect
注釈が必要です。 - @Before注釈は、Beforeアドバイスを作成するために使用されます。
- @Before注釈で渡される文字列パラメータは、Pointcut式です。
- getNameAdvice()アドバイスは、シグネチャ
public String getName()
を持つSpring Beanメソッドに対して実行されます。これは非常に重要なポイントです。新しい演算子を使用してEmployeeビーンを作成すると、アドバイスは適用されません。 ApplicationContextを使用してビーンを取得すると、アドバイスが適用されます。 - Pointcut式ではアスタリスク(*)をワイルドカードとして使用できます。 getAllAdvice()は、
com.journaldev.spring.service
パッケージ内のすべてのクラスに適用されます。get
で始まり、引数を取らない名前のクラス
アドバイスがすべての異なるタイプのアドバイスを調べた後にテストクラスでアドバイスを実行します。
Spring AOP ポイントカットメソッドと再利用
時折、同じポイントカット表現を複数の場所で使用する必要があります。`@Pointcut` 注釈を付けた空のメソッドを作成し、それをアドバイス内の表現として使用できます。EmployeeAspectPointcut.java コード:
package com.journaldev.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("within(com.journaldev.spring.service.*)")
public void allMethodsPointcut(){}
}
上記の例は非常に明確で、表現の代わりにアドバイス注釈の引数にメソッド名を使用しています。
Spring AOP JoinPoint と Advice 引数
JoinPoint をアドバイスメソッドのパラメータとして使用し、メソッドのシグネチャや対象オブジェクトを取得できます。ポイントカットで args()
表現を使用して引数パターンに一致するメソッドに適用できます。これを使用すると、引数の型が決定されるアドバイスメソッドで同じ名前を使用する必要があります。ジェネリックオブジェクトもアドバイスの引数として使用できます。EmployeeAspectJoinPoint.java コード:
package com.journaldev.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.journaldev.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()));
}
//アドバイスの引数、単一の文字列引数を持つビーンメソッドに適用されます
@Before("args(name)")
public void logStringArguments(String name){
System.out.println("String argument passed="+name);
}
}
Spring AOPの後処理の例
単純なアスペクトクラスを見て、After、After Throwing、およびAfter Returningアドバイスの例を示します。EmployeeAfterAspect.javaコード:
package com.journaldev.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.journaldev.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アドバイスを使用して、アドバイスされたメソッドによって返されるオブジェクトを取得できます。Employee BeanにはthrowException()メソッドがあり、After Throwingアドバイスの使用例を示しています。
Spring AOP Around Aspectの例
前述のように、Aroundアスペクトを使用してメソッドの実行を前後に切り取ることができます。アドバイスされたメソッドが実行されるかどうかを制御するために使用できます。また、返された値を検査して変更することもできます。これは最も強力なアドバイスであり、適切に適用する必要があります。EmployeeAroundAspect.javaコード:
package com.journaldev.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.journaldev.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 を引数に取る必要があり、それを使用して対象のオブジェクトのアドバイスされたメソッドを呼び出すべきです。アドバイスされたメソッドが何かを返す場合、それを呼び出し元のプログラムに返す責任があります。void メソッドの場合、アドバイスメソッドは null を返すことができます。アラウンドアドバイスはアドバイスされたメソッドを切り取るため、メソッドの入力と出力、およびその実行動作を制御することができます。
Spring Advice with Custom Annotation Pointcut
上記のアドバイスのポイントカット式をすべて見ると、意図しない別のビーンに適用される可能性があります。たとえば、誰かがgetName()メソッドを持つ新しいSpringビーンを定義した場合、意図しないところにアドバイスが適用されるようになります。そのため、ポイントカット式のスコープをできるだけ狭く保つべきです。別のアプローチとして、カスタムアノテーションを作成し、アドバイスを適用したいメソッドにアノテーションを付ける方法があります。これが、EmployeeのsetName()メソッドに@Loggableアノテーションを付ける目的です。Spring Frameworkの@Transactionalアノテーションは、Springトランザクション管理のためのこのアプローチの素晴らしい例です。Loggable.javaのコード:
package com.journaldev.spring.aspect;
public @interface Loggable {
}
EmployeeAnnotationAspect.javaのコード:
package com.journaldev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAnnotationAspect {
@Before("@annotation(com.journaldev.spring.aspect.Loggable)")
public void myAdvice(){
System.out.println("Executing myAdvice!!");
}
}
myAdvice()メソッドは、setName()メソッドにのみアドバイスを提供します。これは非常に安全なアプローチであり、アドバイスを任意のメソッドに適用したい場合は、単にそのメソッドにLoggableアノテーションを付けるだけです。
Spring AOP XML Configuration
I always prefer annotation but we also have the option to configure aspects in the spring configuration file. For example, let’s say we have a class as below. EmployeeXMLConfigAspect.java code:
package com.journaldev.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.journaldev.spring.aspect.EmployeeXMLConfigAspect" />
<!-- Spring AOP XML Configuration -->
<aop:config>
<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
<aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/>
<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
</aop:aspect>
</aop:config>
AOP XMLの構成要素の目的は、その名前から明確ですので、詳細には触れません。
Spring AOPの例
簡単なSpringプログラムを作成し、これらの側面がビーンメソッドを通過する方法を見てみましょう。SpringMain.javaのコード:
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.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.journaldev.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.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
at com.journaldev.spring.model.Employee.throwException(Employee.java:19)
at com.journaldev.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.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)
ポイントカットの設定に基づいてアドバイスが1つずつ実行されていることがわかります。混乱を避けるために、1つずつそれらを設定する必要があります。これでSpring AOPの例チュートリアルは以上です。SpringとAOPの基本を学んで、例からさらに学べることを願っています。以下のリンクからサンプルプロジェクトをダウンロードして、試してみてください。