1、在XML配置文件中配置切面、切入點、增強處理。spring-1.5之前只能使用XML Schema方式配置切面、切入點、增強處理。
spring配置文件中,所有的切面、切入點、增強處理都必須定義在<aop:config.../>元素內部。<beans.../>元素可以包含多個<aop:config.../>元素,一個<aop:config>可以包含pointcut、advisor、aspect元素,且三個元素必須按照此順序來定義。其中aspect下可以包含多個子元素。
使用<aop:config.../>方式進行配置時,可能與spring的自動代理方式相沖突,例如使用<aop:aspectj-autoproxy>或類似方式顯示啟用了自動代理,則可能導致出現問題(比如有些增強處理沒有被織入)。所以要么全使用<aop:config.../>的配置方式,要么全使用自動代理方式,不要混合使用。
2、配置切面
使用<aop:aspect.../>定義,bean可以是Java bean也可以是容器bean(即可以給bean注入依賴),如果切面bean是容器bean,<aop:aspect.../>使用ref屬性引用。使用<aop:aspect.../>可以指定的屬性:
1》id:定義該切面的標識名。
2》ref:用於將ref引用的容器bean轉換成切面bean。
3》order:指定該切面bean的優先級,該屬性的作用與前面@AspectJ中的@Order注解、Ordered接口的作用完全一樣,order屬性值越小,該切面對應的優先級越高。
3、配置增強處理
使用XML配置增強處理需要以下元素:
<aop:before.../>:配置Before增強處理。
<aop:after.../> :配置After增強處理。
<aop:after-returning.../>:配置AfterReturning增強處理。
<aop:after-throwing.../>:配置AfterThrowing增強處理。
<aop:around.../>:配置Around增強處理。
這些元素都不支持使用子元素,但可以指定以下屬性:
pointcut:指定一個切入點表達式,spring將在匹配該表達式的連接點時織入該增強處理。
pointcut-ref:該屬性指定一個已經存在的切入點名稱,通常pointcut、pointcut-ref兩個屬性只是用其中之一。
method:該屬性指定一個方法名,指定將切面bean的該方法轉換為增強處理。
throwing:該屬性只對<after-throwing.../>元素有效,用於指定一個形參名,AfterThrowing增強處理方法可通過該形參訪問目標方法所拋出的異常。
returning:該屬性只對<after-returning.../>元素有效,用於指定一個形參名,AfterReturning增強處理方法可通過該形參訪問目標方法的返回值。
當定義切入點表達式時,XML配置方式完全支持excution、within、args、this、target、bean等切入點指示符。
要組合切入點表達式時,使用的是and(相當於&&)、or(相當於||)、not(相當於!)
舉個例子:
HelloImpl.java
package com.lfy.impl; import com.lfy.bean.Hello; /** * 被增強的目標類 * @author lfy * */ public class HelloImpl implements Hello { @Override public void foo() { System.out.println("執行Hello組件的foo()方法"); } @Override public void addUser(String name, String pass) { System.out.println("執行Hello組件的addUser()添加用戶: "+name); } @Override public int addGroup(String groupName, int groupMemberNumber) { System.out.println("執行Hello組件的addGroup()添加群組: "+groupName); if(groupMemberNumber<0||groupMemberNumber>100) { throw new IllegalArgumentException("群組成員數在0~100之間"); } return 0; } @Override public int fourAdviceGetParamer(String param) { System.out.println("執行Hello組件的fourAdviceGetParamer()方法,param:"+param); return 5; } }
FourAdviceGetParamerAspect.java
package com.lfy.aspect; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * Around、Before、After、AfterReturning四種增強處理<br> * 訪問目標方法最簡單的做法是定義增強處理方法時將第一個參數定義為JoinPoint類型, * 當該增強處理方法被調用時,該JoinPoint參數就代表了織入增強處理的連接點。JoinPoint * 提供了如下的常用訪問方法:<br> * 1>Objeact[] getArgs():返回目標方法的參數<br> * 2>Signature getSignature():返回目標方法的相關信息<br> * 3>Object getTarget():返回被增強處理的對象<br> * 4>Object getThis():返回AOP框架為目標對象生成的代理對象。<br> * 在這些方法中,只有Around增強處理中可以對目標方法的參數進行修改!<br> * @author lfy * * 切入點組合連接符有&&、||、! */ public class FourAdviceGetParamerAspect { public Object processTx(ProceedingJoinPoint jp) throws Throwable { System.out.println("FourAdviceGetParamerAspect.Around執行目標方法之前,模擬開始事務..."); //獲取目標方法原始的調用參數 Object[] args=jp.getArgs(); if(args!=null && args.length>0 && args[0].getClass() == String.class) { //修改目標方法調用參數的第一個參數 args[0]="[增強的前綴]"+args[0]; } //改變后的參數去執行目標方法,並保存目標方法執行后的返回值 Object rvt=jp.proceed(args); System.out.println("FourAdviceGetParamerAspect.Around執行目標方法之后,模擬結束事務..."); System.out.println("<---------------->"); //如果rvt的類型是Integer,將rvt改為它的平方 if(rvt!=null&&rvt instanceof Integer) { rvt=(Integer)rvt*(Integer)rvt; } return rvt; } public void authority(JoinPoint jp) { System.out.println("FourAdviceGetParamerAspect.Before模擬執行權限檢查"); //返回被織入增強的目標方法 System.out.println("被織入的目標方法是: "+jp.getSignature().getName()); System.out.println("被織入的目標方法的參數: "+Arrays.toString(jp.getArgs())); System.out.println("被織入增強處理的目標對象為: "+jp.getTarget()); System.out.println("<---------------->"); } public void release(JoinPoint jp) { System.out.println("FourAdviceGetParamerAspect.After模擬方法結束后的釋放資源..."); //返回被織入增強的目標方法 System.out.println("被織入的目標方法是: "+jp.getSignature().getName()); System.out.println("被織入的目標方法的參數: "+Arrays.toString(jp.getArgs())); System.out.println("被織入增強處理的目標對象為: "+jp.getTarget()); System.out.println("<---------------->"); } public void log(JoinPoint jp,Object rvt) { System.out.println("FourAdviceGetParamerAspect.AfterReturning獲取目標方法返回值: "+rvt); System.out.println("FourAdviceGetParamerAspect.AfterReturning模擬記錄日志功能..."); //返回被織入增強的目標方法 System.out.println("被織入的目標方法是: "+jp.getSignature().getName()); System.out.println("被織入的目標方法的參數: "+Arrays.toString(jp.getArgs())); System.out.println("被織入增強處理的目標對象為: "+jp.getTarget()); System.out.println("<---------------->"); } }
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- spring配置文件的根元素,使用spring-beans-4.0.xsd語義約束 --> <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-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <aop:config> <!-- 配置全局切入點,設置在<aop:config>下是全局切入點標簽, 可以被多個切面共享。設置在<aop:aspect>下是某個切面的 切入點標簽 ,表示該切入點只能在該切面中有效。 --> <aop:pointcut id="myPointcut0" expression="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))"/> <!-- 將fourAdviceGetParamerBean轉換成切面bean,切面 bean的名稱為fourAdviceGetParamerAspect,指定該 切面的優先級為2 --> <aop:aspect id="fourAdviceGetParamerAspect" ref="fourAdviceGetParamerBean" order="2"> <!-- 直接指定切入點表達式 --> <aop:around pointcut="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))" method="processTx"/> <aop:before pointcut-ref="myPointcut0" method="authority"/> <!-- 定義一個After增強處理,直接指定切入點,以切面bean(即:fourAdviceGetParamerAspect)中的release()方法作為增強處理方法 --> <aop:after pointcut-ref="myPointcut0" method="release"/> <aop:after-returning pointcut-ref="myPointcut0" method="log" returning="rvt"/> </aop:aspect> </aop:config> <bean id="fourAdviceGetParamerBean" class="com.lfy.aspect.FourAdviceGetParamerAspect"/> <bean id="hello" class="com.lfy.impl.HelloImpl"/> <bean id="world" class="com.lfy.impl.WorldImpl"/> </beans>
SpringAOPTest.java
package com.lfy.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lfy.bean.Hello; import com.lfy.bean.World; /** * 2、基於XML Schema方式 * @author lfy * 未登記的知識點: * 1>指定增強處理的優先級,@Order注解及Orderd接口 * 2>切入點指示符 */ public class SpringAOPTest { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Hello hello=ctx.getBean("hello", Hello.class); System.out.println("修改后的目標方法的返回值:"+hello.fourAdviceGetParamer("唐僧")); } }
運行結果:
4、配置切入點
XML配置方式也可以通過定義切入點來重復使用切入點表達式。spring提供了<aop:pointcut.../>元素定義切入點。當把<aop:pointcut.../>元素作為<aop:config.../>元素的子元素定義時,表明該切入點可被多個切面共享;當把<aop:pointcut.../>元素作為<aop:aspect.../>的子元素定義時,表明該切入點只能在該切面中有效。
配置<aop:pointcut.../>元素通常需要指定的屬性有:
id:指定該切入點的標識名。
expression:指定該切入點關聯的切入點表達式。
<aop:pointcut id="myPointcut0" expression="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))"/>
如果已經使用了注解定義了某個切入點,也可以給expression指定該已有的切入點。(但不推薦注解和XML Schema混合使用,我們在第十七篇講過,容易出錯。)
<aop:pointcut id="myPointcut0" expression="com.lfy.aspect.PointcutUtil.myPointcut()"/>