天天看點

Spring AOP---基于Xml配置應用demo

        首先,需要加載依賴的Spring相關類庫,如下所示,這裡建立的是普通java工程,web工程的配置,這裡不再多說:

Spring AOP---基于Xml配置應用demo

        其次,配置 .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包的具體作用。