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

        首先,需要加載依賴的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包的具體作用。

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章