1.XMLベースの宣言型
XMLベースの宣言型とは、Spring構成ファイルを介してアスペクト、ポイントカット、および宣言通知を定義することを意味し、すべてのアスペクトと通知は<aop:config>要素で定義する必要があります。
次のように実行します。
(1)jarパッケージをインポートします。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
(2)Bookクラスを作成します
public class Book {
public void addBook(){
System.out.println("add Book……");
}
public void updateBook(){
System.out.println("update Book……");
}
}
(3)BookProxyクラスを作成します
public class BookProxy {
public void before(){
System.out.println("before……");
}
public void after(){
System.out.println("after……");
}
public void afterReturing(){
System.out.println("afterReturing……");
}
public void afterThrowing(){
System.out.println("afterThrowing……");
}
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("around 之前……");
point.proceed();
System.out.println("around 之后……");
}
}
(4)bean.xmlのBean構成とaop構成。
<!-- 基于XML的AOP实现 -->
<!-- 配置bean -->
<bean id="book" class="com.yht.example6.Book"></bean>
<bean id="bookProxy" class="com.yht.example6.BookProxy"></bean>
<!-- aop配置 -->
<aop:config>
<!--
expression : 配置切入点的具体位置
id:切入点的id
-->
<aop:pointcut id="p1" expression="execution(* com.yht.example6.Book.*(..))"/>
<!-- -->
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="p1"></aop:before>
<aop:after method="after" pointcut-ref="p1"></aop:after>
<aop:after-returning method="afterReturing" pointcut-ref="p1"></aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="p1"></aop:after-throwing>
<aop:around method="around" pointcut-ref="p1"></aop:around>
</aop:aspect>
</aop:config>
(5)ユニットテストを実行します
@Test
public void testBook(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean("book", Book.class);
book.addBook();
}
実行結果は以下のとおりです。
2つの注釈ベースの宣言型
Springでは、XML構成ファイルを使用することでAOP開発を実現できますが、関連するすべての構成が構成ファイルに集中していると、必然的にXML構成ファイルが肥大化し、保守やアップグレードに一定の困難が生じます。AspectJフレームワークは、AOP開発のための別の開発方法であるアノテーションに基づく宣言型を提供します。AspectJでは、アノテーションを使用してアスペクト、ポイントカット、および拡張機能を定義できますが、Springフレームワークでは、これらのアノテーションに基づいてAOPプロキシを識別および生成できます。一般的な注釈は次のように導入されています。
名前 | 説明 |
---|---|
@側面 | アスペクトを定義するために使用されます |
@前 | BeforeAdviceと同等の事前通知を定義するために使用されます |
@AfterReturning | AfterReturningAdviceと同等の投稿通知を定義するために使用されます |
@Around | MethodInterceptorと同等のサラウンド通知を定義するために使用されます |
@AfterThrowing | ThrowAdviceと同等の例外スロー通知を定義するために使用されます |
@After | 最終(最終)通知を定義するために使用され、異常であるかどうかに関係なく、通知が実行されます |
@DeclareParents | IntroductionInterceptor(理解)と同等の紹介通知を定義するために使用されます |
(1)bean.xmlで構成します。
<!-- 配置注解扫描的包 -->
<context:component-scan base-package="com.yht.example6"></context:component-scan>
<!--
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
aop名前空間の<aop:aspectj-autoproxy />宣言により、Springコンテナの@aspectJアスペクトで設定されたBeanのプロキシが自動的に作成され、アスペクトが織り込まれます。<aop:aspectj-autoproxy />にはproxy-target-class属性があり、これはデフォルトでfalseです。proxy-target-classの属性値がfalseに設定されているか、この属性が省略されている場合、標準のJDKインターフェースベースのプロキシが有効になります。<aop:aspectj-autoproxy poxy-target-class = "true" />として構成されている場合、それはCGLib動的プロキシテクノロジーを使用して織り込み、拡張することを意味します。ただし、proxy-target-classがfalseに設定されている場合でも、ターゲットクラスがインターフェイスを宣言していない場合、springは自動的にCGLib動的プロキシを使用します。
(2)Userクラスを作成します
@Component
public class User {
public void addUser(){
System.out.println("添加用户");
}
public void updateUser(){
System.out.println("修改用户");
}
public void deleteUser(){
System.out.println("删除用户");
}
public void searchUser(){
System.out.println("查找用户");
}
}
(3)UserProxyを作成します
@Component
@Aspect
public class UserProxy {
@Before(value = "execution(* com.yht.example6.User.addUser(..))")
public void before(){
System.out.println("方法执行之前");
}
@After(value = "execution(* com.yht.example6.User.addUser(..))")
public void after(){
System.out.println("方法执行之后");
}
@AfterReturning(value = "execution(* com.yht.example6.User.addUser(..))")
public void afterReturning(){
System.out.println("返回后通知");
}
@AfterThrowing(value = "execution(* com.yht.example6.User.addUser(..))")
public void afterThrowing(){
System.out.println("抛出异常后通知");
}
@Around(value = "execution(* com.yht.example6.User.addUser(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知——方法执行之前");
point.proceed();
System.out.println("环绕通知——方法执行之后");
}
}
(4)ユニットテストを実行します。
@Test
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.addUser();
}
実行結果は以下のとおりです。
いくつかの注意:
(1)プログラムが異常な場合にafterThrowingメソッドを実行し、ユーザーのaddUser()がプログラムを異常なものにします。メソッドが実行されるかどうかを確認します。
@AfterThrowing(value = "execution(* com.yht.example6.User.addUser(..))")
public void afterThrowing(){
System.out.println("抛出异常后通知");
}
NumberFormatExceptionを作成します。
public void addUser(){
int val = Integer.parseInt("123ab");//异常
System.out.println("添加用户");
}
結果は次のとおりです。
(2)@Orderの紹介
@Orderアノテーションは、コンポーネントの順序を宣言するために使用されます。値が小さいほど、優先度が高くなり、実行/初期化が早くなります。そのような注釈がない場合、優先度は最低です。上記のコードでは、@ Order(1)を指定して別のPersonProxyクラスを追加し、@ Order(5)を指定してUserProxyを追加すると、実行結果は、PersonProxyのbeforeメソッドがUserProxyのbeforeメソッドの前に実行されることになります。
1)PersonProxyクラスを定義します。
@Component
@Aspect
@Order(1)
public class PersonProxy {
@Before(value = "execution(* com.yht.example6.User.addUser(..))")
public void beforeMethod(){
System.out.println("我是PersonProxy的before方法");
}
}
2)@Order(5)をUserProxyに追加します。
3)テストを実行します。結果は次のとおりです。
(3)同じエントリポイントの抽出
上記の例では、UserProxyの5つの通知構成に同じエントリポイント式があり、それらを抽出して、各通知の注釈で引用することができます。したがって、UserProxyクラスは次のようになります。
@Component
@Aspect
@Order(5)
public class UserProxy {
@Pointcut(value = "execution(* com.yht.example6.User.addUser(..))")
public void commonMsg(){}
@Before(value = "commonMsg()")
public void before(){
System.out.println("方法执行之前");
}
@After(value = "commonMsg()")
public void after(){
System.out.println("方法执行之后");
}
@AfterReturning(value = "commonMsg()")
public void afterReturning(){
System.out.println("返回后通知");
}
@AfterThrowing(value = "commonMsg()")
public void afterThrowing(){
System.out.println("抛出异常后通知");
}
@Around(value = "commonMsg()")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知——方法执行之前");
point.proceed();
System.out.println("环绕通知——方法执行之后");
}
}
最終結果は以前と同じになります。
(4)AOP純粋な注釈の開発
AOPの純粋なアノテーション開発は基本的にIOCアノテーション開発に似ており、構成クラスを提供します。コメントを追加:
@Configuration
@ComponentScan(basePackages = "com.yht.example6")
//@EnableAspectJAutoProxy标记在主配置类上,表示开启基于注解的aop模式
@EnableAspectJAutoProxy
public class AopConfig {
}
テストは次のとおりです。
@Test
public void testUserAnno(){
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
User user = context.getBean("user", User.class);
user.addUser();
}