AspectJは、Java言語を拡張するJava言語に基づくAOPフレームワークです。Spring 2.0以降、AspectJモードのサポートが追加されました。Springフレームワークの新しいバージョンでは、AspectJモードを使用してAOPを開発することをお勧めします。
AspectJを使用してAOPを開発するには、通常2つの方法があります。
- XMLに基づく宣言。
- 注釈に基づく宣言。
次に、これら2つのAOP開発方法について説明します。
XMLベースの宣言
Spring構成ファイルを介してアスペクト、エントリポイント、宣言通知を定義するXMLベースの宣言的手段であり、すべてのアスペクトと通知は<aop:config>要素で定義する必要があります。
次の例は、SpringでXMLベースのAOP開発の宣言型実装を使用する方法を示しています。
1. JARパッケージをインポートする
AspectJの使用Spring AOP JARパッケージのインポートに加えて、次のように、AspectJに関連するJARパッケージもインポートする必要があります。
- spring-aspects-3.2.13.RELEASE.jar:SpringがAspectJに提供する実装は、Springパッケージですでに提供されています。
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:はAspectJによって提供される仕様です。公式Webサイトhttps://repo.spring.io/webapp/#/search/quick/で 検索およびダウンロードでき ます。。
2.アスペクトクラスMyAspectを作成する
srcディレクトリにcom.mengma.aspectj.xmlという名前のパッケージを作成し、パッケージの下にアスペクトクラスMyAspectを作成して、以下のように編集します。
package com.mengma.aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class MyAspect {
// 前置通知
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知,目标:");
System.out.print(joinPoint.getTarget() + "方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
// 后置通知
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知,方法名称:" + joinPoint.getSignature().getName());
}
// 环绕通知
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
System.out.println("环绕开始"); // 开始
Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法
System.out.println("环绕结束"); // 结束
return obj;
}
// 异常通知
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知" + "出错了" + e.getMessage());
}
// 最终通知
public void myAfter() {
System.out.println("最终通知");
}
}
上記のコードでは、いくつかの異なる通知タイプのメソッドが定義されていますが、これらのメソッドでは、JoinPointパラメータを介して、ターゲットオブジェクトのクラス名、ターゲットメソッド名、およびターゲットメソッドパラメータを取得できます。ラップアラウンド通知はタイプProceedingJoinPointのパラメーターを受け取る必要があり、戻り値はタイプObjectである必要があり、例外がスローされる必要があることに注意してください。スロー可能なタイプのパラメーターを例外通知に渡して、例外情報を出力できます。
3. Spring構成ファイルを作成する
次に示すように、com.mengma.aspectj.xmlパッケージの下にapplicationContext.xmlの構成ファイルを作成します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!--目标类 -->
<bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" />
<!--切面类 -->
<bean id="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean>
<!--AOP 编程 -->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution ( * com.mengma.dao.*.* (..))"
id="myPointCut" />
<!--前置通知,关联通知 Advice和切入点PointCut -->
<aop:before method="myBefore" pointeut-ref="myPointCut" />
<!--后置通知,在方法返回之后执行,就可以获得返回值returning 属性 -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!--环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!--抛出通知:用于处理程序发生异常,可以接收当前方法产生的异常 -->
<!-- *注意:如果程序没有异常,则不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!--最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
上記のコードでは、まず、AOP名前空間が4、7、8行のコードでインポートされます。12行目のコードは、アスペクトクラスを指定します。
17行目と18行目は、エントリポイントを構成し、どのメソッドを強化する必要があるかを通知します。メソッド
。20行目から32行目は、通知(アドバイス)とポイントカット(PointCut)を関連付けるために使用されます。20行目の事前通知を例にとると、<aop:before>タグのmethod属性を使用して通知を指定します、pointcut-refこの属性は、拡張されるメソッドであるエントリー・ポイントを指定するために使用されます。他のいくつかの通知の構成については、コードのコメントを参照できます。
4.テストクラスを作成する
以下に示すように、com.mengma.aspectj.xmlパッケージの下にテストクラスXMLTestを作成します。
package com.mengma.aspectj.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mengma.dao.CustomerDao;
public class XMLTest {
@Test
public void test() {
String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
xmlPath);
// 从spring容器获取实例
CustomerDao customerDao = (CustomerDao) applicationContext
.getBean("customerDao");
// 执行方法
customerDao.add();
}
}
5.プロジェクトを実行して結果を表示します
JUnitテストを使用してtest()メソッドを実行します。操作が正常に完了すると、コンソールの出力が図1のようになります。
図1実行結果
例外通知をよりよく示すために、次にCustomerDaoImplクラスのadd()メソッドに例外をスローするコード行を追加します。たとえば、「int i = 1/0;」のようにXMLTestテストクラスを再実行すると、例外通知が表示されます。実行され、コンソールの出力が図2に示されています。
図2実行結果
図1および図2の出力結果から、XMLベースの宣言型AOP開発が正常に実装されていることがわかります。
注釈に基づく宣言
Springでは、XML構成ファイルを使用してAOP開発を実装できますが、関連するすべての構成が構成ファイルに集中していると、必然的にXML構成ファイルが肥大化し、メンテナンスとアップグレードが困難になります。
このため、AspectJフレームワークは、注釈に基づくAOP開発宣言の別の開発方法を提供します。AspectJを使用すると、アノテーションでアスペクト、エントリポイント、および拡張処理を定義でき、Springフレームワークはこれらのアノテーションに基づいてAOPプロキシを認識および生成できます。
アノテーションアノテーションの概要を表1に示します。
名前 | 解説 |
---|---|
@側面 | スライスを定義するために使用されます。 |
@前 | BeforeAdviceと同等の事前アドバイスを定義するために使用されます。 |
@AfterReturning | ポスト通知を定義するために使用されます。AfterReturningAdviceと同等です。 |
@周り | MethodInterceptorと同等のサラウンド通知を定義するために使用されます。 |
@AfterThrowing | ThrowAdviceと同等のスロー通知を定義するために使用されます。 |
@After | 最終通知を定義するために使用され、異常かどうかにかかわらず、通知が実行されます。 |
@DeclareParents | 導入通知の定義に使用されます。IntroductionInterceptorと同等です(マスタリングは必要ありません)。 |
以下では、アノテーションを使用して、「XMLベースの宣言」部分の機能を再実装しています。
1.セクションMyAspectを作成します
以下に示すように、srcディレクトリにcom.mengma.aspectj.annotationという名前のパッケージを作成し、パッケージの下にファセットクラスMyAspectを作成します。
package com.mengma.aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//切面类
@Aspect
@Component
public class MyAspect {
// 用于取代:<aop:pointcut
// expression="execution(*com.mengma.dao..*.*(..))" id="myPointCut"/>
// 要求:方法必须是private,没有值,名称自定义,没有参数
@Pointcut("execution(*com.mengma.dao..*.*(..))")
private void myPointCut() {
}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知,目标:");
System.out.print(joinPoint.getTarget() + "方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
// 后置通知
@AfterReturning(value = "myPointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知,方法名称:" + joinPoint.getSignature().getName());
}
// 环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
System.out.println("环绕开始"); // 开始
Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法
System.out.println("环绕结束"); // 结束
return obj;
}
// 异常通知
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知" + "出错了" + e.getMessage());
}
// 最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知");
}
}
上記のコードでは、13行目の@Aspectアノテーションを使用して、これがコンポーネントとして使用されるアスペクトクラスであることを宣言しているため、@ Componentアノテーションを追加して有効にする必要があります。19行目の@Poincutアノテーションは、ポイントカットを構成するために使用され、XMLファイルでポイントカットを構成するためのコードを置き換えます。
注釈は各通知の対応するメソッドに追加され、ポイントカットメソッド名「myPointCut」が実行されるメソッドにパラメーターとして渡されます。他のパラメーター(例外通知の例外パラメーターなど)が必要な場合は、コードプロンプトに従って渡すことができます対応する属性値。
2.ターゲットクラスに注釈を付ける
注釈@Repository( "customerDao")をcom.mengma.dao.CustomerDaoImplターゲットクラスに追加します。
3. Spring構成ファイルを作成する
以下に示すように、com.mengma.aspectj.annotationパッケージの下にapplicationContext.xml構成ファイルを作成します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描含com.mengma包下的所有注解-->
<context:component-scan base-package="com.mengma"/>
<!-- 使切面开启自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
上記のコードでは、AOP名前空間とそのサポート制約が最初にインポートされ、アスペクトクラスの@AspectJアノテーションが適切に機能します。スキャンパッケージが行13に追加され、アノテーションが有効になっています。ここでターゲットクラスcom.mengma.dao.CustomerDaoImplのアノテーションも含まれているため、base-packageの値はcom.mengmaであることに注意してください。コードの15行目の機能は、アスペクトで自動プロキシを開くことです。
4.テストクラスを作成する
以下に示すように、com.mengma.aspectj.annotationパッケージの下にAnnotationTestという名前のテストクラスを作成します。
package com.mengma.aspectj.annotation;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mengma.dao.CustomerDao;
public class AnnotationTest {
@Test
public void test() {
String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
xmlPath);
// 从spring容器获取实例
CustomerDao customerDao = (CustomerDao) applicationContext
.getBean("customerDao");
// 执行方法
customerDao.add();
}
}
5.プロジェクトを実行して結果を表示します
JUnitテストを使用してtest()メソッドを実行します。操作が正常に完了すると、コンソールの出力が図3に表示されます。
図3実行結果
add()メソッドで「int i = 1/0;」を削除し、test()メソッドを再実行すると、コンソールの出力が図4のようになります。
図4実行結果
図3と図4の出力結果からわかるように、AOP開発はAnnotationを使用して正常に実装されています。他の方法と比較して、これは注釈に基づくAOPの効果を達成する最も便利な方法であるため、実際の開発では注釈を使用することをお勧めします。