0.0
這是一個基於代理的 spring AOP 簡單示例
這種通過配置來達到修改程序邏輯的方式,必須被小心/留意/謹慎使用,因爲這對原始 bean 的代碼來說是透明的,這很可能給調試代碼帶來不必要的阻礙。所以在 切點位置 最好/必須 添加必要的註釋 說明/強調 AOP 這種影響代碼邏輯的特殊情況。
A pointcut picks out certain join points and values at those points. A piece of advice is code that is executed when a join point is reached. — from : Introduction to AspectJ - Chapter 1. Getting Started with AspectJ
1. spring 基於代理的 AOP 示例
首先定義一個接口(該接口用於客戶端編程,基於面向接口編程的原則。並非一定必須。)
package com. willhonor. test. useaop;
public interface IAPersonA {
void saySucceedA ( ) ;
void saySucceedB ( ) ;
void sayFailed ( ) ;
}
上面接口 IAPersonA 的具體實現類
package com. willhonor. test. useaop;
public class APersonA implements IAPersonA {
public void saySucceedA ( ) {
System. out. println ( "A say hello, this world." ) ;
}
public void saySucceedB ( ) {
System. out. println ( "B say hello, this world." ) ;
}
public void sayFailed ( ) {
throw new RuntimeException ( "unknown exception" ) ;
}
}
下面連續定義 3 個通知,它們分別用於上述接口 IAPersonA 中的 3 個方法(即 saySucceedA / saySucceedB / sayFailed ,它們的通知方式分別爲: 方法執行之前 / 方法成功返回之後 / 方法拋出異常)
package com. willhonor. test. useaop;
import java. lang. reflect. Method;
import org. springframework. aop. MethodBeforeAdvice;
public class MMthodBeforeAdvice implements MethodBeforeAdvice {
private int methodInvokeCount = 0 ;
public void before ( Method method, Object[ ] args, Object target) throws Throwable {
this . methodInvokeCount++ ;
System. out. println ( String. format ( "methodBeforeAdvice: 方法:[%s]執行之前通知" , new Object [ ] { method. getName ( ) } ) ) ;
}
public int getMethodInvokeCount ( ) {
return methodInvokeCount;
}
}
package com. willhonor. test. useaop;
import java. lang. reflect. Method;
import org. springframework. aop. AfterReturningAdvice;
public class MAfterMethodReturningAdvice implements AfterReturningAdvice {
private int methodSucceedReturningCount = 0 ;
public void afterReturning ( Object returnValue, Method method, Object[ ] args, Object target) throws Throwable {
this . methodSucceedReturningCount++ ;
System. out. println ( String. format ( "afterReturningAdvice: 方法[%s]成功返回" , new Object [ ] { method. getName ( ) } ) ) ;
}
public int getMethodSucceedReturningCount ( ) {
return methodSucceedReturningCount;
}
}
package com. willhonor. test. useaop;
import java. lang. reflect. Method;
import org. springframework. aop. ThrowsAdvice;
public class MThrowsAdvice implements ThrowsAdvice {
private int methodInvokeException = 0 ;
public void afterThrowing ( Method method, Object[ ] args, Object obj, Throwable ex) {
this . methodInvokeException++ ;
System. out. println ( String. format ( "throwsAdvice: 方法:[%s]拋出異常:" + ex, new Object [ ] { method. getName ( ) } ) ) ;
}
public int getMethodInvokeException ( ) {
return methodInvokeException;
}
}
spring 配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans SYSTEM "spring-beans.dtd">
< beans>
< bean id = " persona"
class = " com.willhonor.test.useaop.APersonA" >
</ bean>
< bean id = " afteradvice" class = " com.willhonor.test.useaop.MAfterMethodReturningAdvice" >
< description> 爲了切入:方法成功返回 returning 之後</ description>
</ bean>
< bean id = " beforeadvice" class = " com.willhonor.test.useaop.MMthodBeforeAdvice" >
< description> 爲了切入:方法被調用 invoke 之前</ description>
</ bean>
< bean id = " throwsadvice" class = " com.willhonor.test.useaop.MThrowsAdvice" >
< description> 爲了切入:方法拋出異常 throws ex 之後</ description>
</ bean>
< bean id = " pointcut:saySucceedA" class = " org.springframework.aop.support.NameMatchMethodPointcut" >
< description> 切點爲 saySucceedA 方法</ description>
< property name = " mappedNames" >
< list>
< value> saySucceedA</ value>
</ list>
</ property>
</ bean>
< bean id = " pointcut:saySucceedB" class = " org.springframework.aop.support.NameMatchMethodPointcut" >
< description> 切點爲 saySucceedB 方法</ description>
< property name = " mappedName" >
< value> saySucceedB</ value>
</ property>
</ bean>
< bean id = " pointcut:sayFailed" class = " org.springframework.aop.support.NameMatchMethodPointcut" >
< description> 切點爲 sayFailed 方法</ description>
< property name = " mappedName" >
< value> sayFailed</ value>
</ property>
</ bean>
< bean name = " advisor:before"
class = " org.springframework.aop.support.DefaultPointcutAdvisor" >
< constructor-arg index = " 0" >
< ref local = " pointcut:saySucceedA" />
</ constructor-arg>
< constructor-arg index = " 1" >
< ref local = " beforeadvice" />
</ constructor-arg>
</ bean>
< bean name = " advisor:after"
class = " org.springframework.aop.support.DefaultPointcutAdvisor" >
< constructor-arg index = " 0" >
< ref local = " pointcut:saySucceedB" />
</ constructor-arg>
< constructor-arg index = " 1" >
< ref local = " afteradvice" />
</ constructor-arg>
</ bean>
< bean name = " advisor:throws"
class = " org.springframework.aop.support.DefaultPointcutAdvisor" >
< constructor-arg index = " 0" >
< ref local = " pointcut:sayFailed" />
</ constructor-arg>
< constructor-arg index = " 1" >
< ref local = " throwsadvice" />
</ constructor-arg>
</ bean>
< bean id = " personb" parent = " proxytemplate" >
< property name = " target" >
< bean class = " com.willhonor.test.useaop.APersonA" >
</ bean>
</ property>
< property name = " interfaces" >
< list>
< value> com.willhonor.test.useaop.IAPersonA</ value>
</ list>
</ property>
</ bean>
< bean id = " proxytemplate" abstract = " true"
class = " org.springframework.aop.framework.ProxyFactoryBean" >
< property name = " interceptorNames" >
< list>
< value> advisor:before</ value>
< value> advisor:after</ value>
< value> advisor:throws</ value>
</ list>
</ property>
</ bean>
</ beans>
測試代碼
package com. willhonor. test. useaop;
import org. junit. Test;
import org. springframework. context. support. ClassPathXmlApplicationContext;
public class Test_use_aop_1 {
@Test
public void test_use_aop_1 ( ) throws Exception {
String pathA = "com/willhonor/test/configs/application.m.xml" ;
String[ ] path = new String [ ] { pathA} ;
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ( path) ;
IAPersonA persona = ( IAPersonA) context. getBean ( "persona" ) ;
persona. saySucceedA ( ) ;
System. out. println ( "--" ) ;
persona. saySucceedB ( ) ;
System. out. println ( "--" ) ;
try { persona. sayFailed ( ) ; } catch ( RuntimeException e) { System. out. println ( e. getMessage ( ) ) ; }
System. out. println ( "--" ) ;
System. out. println ( "\n--------------------------------\n" ) ;
IAPersonA personb = ( IAPersonA) context. getBean ( "personb" ) ;
personb. saySucceedA ( ) ;
System. out. println ( "--" ) ;
personb. saySucceedB ( ) ;
System. out. println ( "--" ) ;
try { personb. sayFailed ( ) ; } catch ( RuntimeException e) { System. out. println ( e. getMessage ( ) ) ; }
System. out. println ( "--" ) ;
context. close ( ) ;
}
}
測試執行結果爲(通過原始 bean 實例 和 被代理的 bean 實例之間執行結果的對比,可見,通過代理正確切入了指定方法的執行前/後以及方法執行異常的情況。基於代理的 AOP 已經可以滿足很多一般性的需求了。)(在實際應用中,如果需要對原始的 bean 進行切入,可以僅僅通過修改 spring 的配置文件,即 使用本文介紹的基於代理的 AOP 實現即可達到目的,而客戶端代碼對此卻毫不知情,即無需修改客戶端代碼。)(這種通過配置來達到修改程序邏輯的方式,必須被小心/留意/謹慎使用,因爲這對原始 bean 的代碼來說是透明的,這很可能給調試代碼帶來不必要的阻礙。所以在 切點 位置最好/必須添加必要的註釋說明/強調 AOP 這種影響代碼邏輯的特殊情況。)
.. .
.. .
A say hello, this world.
--
B say hello, this world.
--
unknown exception
--
--------------------------------
methodBeforeAdvice: 方法:[ saySucceedA] 執行之前通知
A say hello, this world.
--
B say hello, this world.
afterReturningAdvice: 方法[ saySucceedB] 成功返回
--
throwsAdvice: 方法:[ sayFailed] 拋出異常:java.lang.RuntimeException: unknown exception
unknown exception
--
.. .
.. .