領先一步
VMware 提供培訓和認證,以加速您的進展。
了解更多雖然這篇文章的內容相當簡單,但它實際上將提供您 Spring 2.0 中一些相當重要的新功能的概貌。我希望透過一點想像力,您能夠將您在這裡看到的內容應用到遠遠不那麼瑣碎的用例中。
我將展示 2 個範例。第一個將使用一個相當簡單的記錄器
package example;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SimpleLogger {
private static Log log = LogFactory.getLog(SimpleLogger.class);
public void logOneString(String s) {
log.info("string=" + s);
}
public void logTwoStrings(String s1, String s2) {
log.info("string1=" + s1 + ",string2=" + s2);
}
}
我將使用 AOP 將記錄應用於字串串接服務。這是介面
package example;
public interface ConcatService {
public String concat(String s1, String s2);
}
以及一個實作類別
package example;
public class ConcatServiceImpl implements ConcatService {
public String concat(String s1, String s2) {
return s1 + s2;
}
}
好的 - 到目前為止,沒有什麼令人興奮的,但最重要的是要注意到,到目前為止,我只處理 POJO 。
現在,看看這些 Bean 定義。請注意新的 Spring 2.0 XML Schema 的使用,尤其是 ìaopî 命名空間
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:aspect id="loggingAspect" ref="simpleLogger">
<aop:before
method="logTwoStrings"
pointcut="execution(* example..*Service.*(..)) and args(s1,s2)"/>
<aop:after-returning
method="logOneString"
returning="s"
pointcut="execution(* example..*Service.*(..))"/>
</aop:aspect>
</aop:config>
<bean id="simpleLogger" class="example.SimpleLogger"/>
<bean id="concatService" class="example.ConcatServiceImpl"/>
</beans>
ìloggingAspectî 被定義為參考 ìsimpleLoggerî,您在上面的第一個程式碼片段中看到了它。同樣,有趣的是它是一個簡單的 POJO - 它沒有實作任何介面或遵循任何合約才能被用作 Aspect。事實上,您可能已經有類似這樣的程式碼了。 ;)
ìloggingAspectî 包含 2 種建議。一種是 ìbeforeî 類型的建議,另一種是 ìafterReturningî 類型的建議。接下來,您會看到建議實際上對應到以下的方法SimpleLoggerPOJO -logTwoStrings()用於before建議,以及logOneString()用於afterReturning建議。這種宣告式對應到 POJO 方法的選項是實作建議介面的一個有用的替代方案。
最後,簡單介紹一下綁定 (binding) 和切入點 (pointcut)。在 ìbeforeî 建議中,args(s1,s2) 指定當有 2 個可以綁定到 2 個參數的參數時,這個切入點將會應用String方法的參數 - 這正是即將發生的事情。在 ìafterReturningî 的情況下,傳回值將綁定到單個logTwoStrings()參數的方法。String參數的方法。logOneString()方法。
現在,關於切入點… 上面 ìpointcutî 屬性中的值實際上是標準的 AspectJ 切入點表達式。在這種情況下,它們定義了哪些方法將被建議 (advised)。 ì*î 是一個萬用字元,第一個 ì..î 表示任何子套件,而第二個 ì..î 表示任何數量和類型的參數。基本上,這個切入點將應用於任何以 ìServiceî 結尾的 Class 的任何方法,無論其參數類型或數量如何,只要它以某種方式從 ìexampleî 套件中派生出來。好的,所以也許這看起來不那麼簡單 - 但如果它至少聽起來很有趣,那麼您可以在 AspectJ 網站上閱讀更多關於 AspectJ 表達式語言的資訊。
注意:雖然這裡使用了 AspectJ 表達式,但建議仍然通過 Spring 的基於 Proxy 的 AOP 應用,而不是 AspectJ 編織 (weaving)。這意味著攔截器只能在方法執行連接點 (joinpoint) 添加行為。方法執行攔截很可能滿足您的大部分 AOP 用例。但是,為了在其他連接點(例如欄位存取)應用建議,您可以使用 AspectJ 的全部功能(這超出了本文的範圍)。
所以,不再猶豫… 這是一個簡單的main()方法來嘗試一下
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("example/simpleLoggerContext.xml");
ConcatService concatService = (ConcatService)context.getBean("concatService");
concatService.concat("some", "thing");
}
結果!
string1=some,string2=thing
string=something
現在,關於第二個範例…
當然,您可能想要記錄更多資訊,例如方法參數、呼叫方法本身等等。為了展示如何完成此操作,我將修改這個SimpleLogger一點。秘密在於JoinPoint類別(以及StaticPart類別),現在將提供給我新類別的方法,MethodLogger:
package example;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
public class MethodLogger {
private static Log log = LogFactory.getLog(MethodLogger.class);
public void logMethodEntry(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String name = joinPoint.getSignature().toLongString();
StringBuffer sb = new StringBuffer(name + " called with: [");
for(int i = 0; i < args.length; i++) {
Object o = args[i];
sb.append(o);
sb.append((i == args.length - 1) ? "]" : ", ");
}
log.info(sb);
}
public void logMethodExit(StaticPart staticPart, Object result) {
String name = staticPart.getSignature().toLongString();
log.info(name + " returning: [" + result + "]");
}
}
如你所見,JoinPoint提供對我需要的執行階段資訊的存取。在logMethodExit()方法中,只需要類型,所以StaticPart就足夠了(它實際上是JoinPoint的一部分,因為JoinPoint提供了一個getStaticPart()方法)。一般來說,只要您可以在不存取執行階段資訊的情況下完成所需的操作,就應該這樣做。
以下是使用MethodLogger:
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* example..*Service+.*(..))"/>
<aop:aspect id="loggingAspect" ref="methodLogger">
<aop:before
method="logMethodEntry"
pointcut-ref="servicePointcut"/>
<aop:after-returning
method="logMethodExit"
returning="result"
pointcut-ref="servicePointcut"/>
</aop:aspect>
</aop:config>
<bean id="methodLogger" class="example.MethodLogger"/>
<bean id="concatService" class="example.ConcatServiceImpl"/>
</beans>
的 Bean 定義。再次,您會看到 Aspect 和建議。這次 ìpointcutî 是單獨定義的,並為兩種建議類型重複使用。也許這裡最有趣的事情是,沒有方法參數的顯式綁定,也不需要配置任何東西來識別JoinPoint或StaticPart參數。事實上,您可以隨時將其中一個指定為方法的第一個參數,以便存取有關方法執行環境的更多資訊。
為了執行這個範例,我將使用相同的main(),但這次將新的 Bean 定義檔案的路徑傳遞到ClassPathXmlApplicationContext建構子。這是結果
public abstract java.lang.String example.ConcatService.concat(java.lang.String,java.lang.String) called with: [some, thing]
public abstract java.lang.String example.ConcatService.concat(java.lang.String,java.lang.String) returning: [something]
這就是這個簡單範例的全部內容。主要要點是,POJO 服務可以通過POJO Aspect 來裝飾額外的行為。事實上,在某些情況下,使它們成為 Aspect 的唯一原因就是配置。在其他情況下,當您需要更多執行階段資訊時,JoinPoint和StaticPart可能會非常有用。
如果您對更完整的涵蓋這個主題感興趣,請訪問 Adrian Colyer 的 這個部落格。
注意:在那篇文章中,您會看到 <aop:advice> 元素的範例。在 Spring 2.0 M3 中,這些元素已被本文中使用更具體的元素取代 - 例如:<aop:before>。