首先,需要加載依賴的Spring相關類庫,如下所示,這裡建立的是普通java工程,web工程的配置,這裡不再多說:
其次,配置 .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"
<!-- 配置aop命名空間 -->
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">
<!-- 基于注解的aop動态代理配置 -->
<aop:aspectj-autoproxy/>
<bean id="duke" class = "com.springaction.springidol.Juggler">
<constructor-arg value="15">
</constructor-arg>
</bean>
<!-- 切面 -->
<bean id="audience" class="com.springaction.aop.domain.Audience" />
<aop:config>
<aop:aspect ref="audience">
<!--定義切點,隻在audienct切面中使用,如果想被多個切面使用,可以放到<aop:config>元素中 -->
<aop:pointcut id="performance" expression="execution(* com.springaction.springidol.Performer.perform(..))"/>
<!--應用切點,前置通知-->
<aop:before pointcut-ref="performance"
method="takeSeats" />
<aop:before pointcut-ref="performance"
method="turnOffCellPhones" />
<!-- 後置通知,方法成功執行之後調用通知 -->
<aop:after-returning pointcut-ref="performance"
method="applaud" />
<!--後置通知, 方法抛出異常之後調用通知 -->
<aop:after-throwing pointcut-ref="performance"
method="demandRefund" />
<!-- 聲明環繞通知 -->
<aop:around pointcut-ref="performance"
method="watchPerformance" />
</aop:aspect>
</aop:config>
</beans>
上面的配置中,定義了切面、切點以及各種通知方式,其中比較特殊的是環繞通知。
然後,看看切面:
public class Audience
{
//表演之前:前置通知
public void takeSeats()
{
System.out.println("The audience is taking their seats.");
}
//表演之前:前置通知
public void turnOffCellPhones()
{
System.out.println("The audience is turning off their cellPhones");
}
//表演之後:後置通知,成功傳回之後執行
public void applaud()
{
System.out.println("CLAP CLAP CLAP CLAP");
}
//表演失敗之後:後置通知,抛異常之後執行
public void demandRefund()
{
System.out.println("Boo! We want out money back!");
}
/**
* 環繞通知:
* Around advice可以通過一個在joinpoint執行前後做一些事情的機會,
* 可以決定什麼時候,怎麼樣去執行joinpoint,甚至可以決定是否真的執行joinpoint的方法調用。
* Around advice通常是用在下面這樣的情況:
* 在多線程環境下,在joinpoint方法調用前後的進行中需要共享一些資料。
* 如果使用Before advice和After advice也可以達到目的,
* 但是就需要在aspect裡面建立一個存儲共享資訊的field,但這種做法并不是線程安全的。
* &&
* ProceedingJoinPoint作為入參,可以讓我們在通知裡調用被通知方法。
* @param joinpoint
*/
public void watchPerformance(ProceedingJoinPoint joinpoint)
{
try
{
//執行前的操作
System.out.println("Around-before:The audience is taking their seats.");
System.out.println("Around-before:The audience is turning off their cellPhones");
long start = System.currentTimeMillis();
//執行被通知的方法
//如果沒有執行這個方法,則通知會阻止被通知方法的調用。
joinpoint.proceed();
//執行後的操作
long end = System.currentTimeMillis();
System.out.println("Around-after:CLAP CLAP CLAP CLAP CLAP");
System.out.println("Around-after:The performance took " + (end - start)
+ " milliseconds.");
}
catch (Throwable e)
{
System.out.println("Boo!We want our money back!");
}
}
}
上面代碼對該切面中關鍵點都做了注釋,其餘的不用多說,其中環繞通知必須包含入參org.aspectj.lang.ProceedingJoinPoint
通過它的方法proceed()執行被通知的方法。另外,任何通知方法可以将第一個參數定義為
org.aspectj.lang.JoinPoint
類型。
JoinPoint
接口提供了一系列有用的方法, 比如
getArgs()
(傳回方法參數)、
getThis()
(傳回代理對象)、
getTarget()
(傳回目标)、
getSignature()
(傳回正在被通知的方法相關資訊)和
toString()
(列印出正在被通知的方法的有用資訊)。
然後,目标對象:
public class Juggler implements Performer
{
private int beanBags = 3;
public Juggler()
{
}
public Juggler(int beanBags)
{
this.beanBags = beanBags;
}
public void perform() throws Exception
{
//抛異常則觸發after-throwing通知
//throw new Exception("");
//執行成功則觸發after-returning通知
System.out.println("JUGGLING " + beanBags + " BeanBags");
//無論成功與否,則觸發after通知
}
}
最後是測試代碼:
public class Test
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("file:../../src/spring.xml");
Performer performer = (Performer) ctx.getBean("duke");
try
{
performer.perform();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
測試代碼中,使用應用上下文ClassPathXmlApplicationContext加載類路徑下的配置檔案,關于加載配置檔案的方式,後面需要具體具體分析下。
執行結果如下:
The audience is taking their seats.
The audience is turning off their cellPhones
Around-before:The audience is taking their seats.
Around-before:The audience is turning off their cellPhones
JUGGLING 15 BeanBags
Around-after:CLAP CLAP CLAP CLAP CLAP
Around-after:The performance took 2 milliseconds.
CLAP CLAP CLAP CLAP
上面的這個Demo是基于XML配置切面的應用,基于注解的應用以及動态代理等後面會給出具體解釋和Demo。
在這個例子調試中,也遇到了一些問題,主要包括如下幾個:
>.1.spring 的依賴注入是面向接口程式設計的,在測試代碼中Performer performer = (Performer) ctx.getBean("duke");必須轉換為接口,而不是具體實作,否則報錯:
Exception in thread "main" java.lang.ClassCastException: $Proxy2 cannot be cast to com.springaction.springidol.Juggler
at com.springaction.springidol.Test.main(Test.java:14)
ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [../../../src/share/back/util.c:820]
>.2.主要是缺少一些jar包,比如commons-logging.jar, spring-expression.jar等,這個根據具體報錯資訊添加具體jar包就可以,關鍵是了解這些jar包的具體作用。