8.4.6 基於XML配置文件的管理方式
Spring 2.x 提供一個新的aop:命名空間來定義切面、切入點和增強處理。
XML配置方式優點:
⊙ 如果應用沒有使用JDK 1.5 以上版本,那么應用只能使用XML配置方式來管理切面、切入點和增強處理等。
⊙ 采用XML配置方式時對早期的Spring用戶來說更加習慣,而且這種方式允許使用純粹的POJO來支持AOP。當使用AOP作為工具來配置企業服務時,XML會是一個很好的選擇。
當使用XML風格時,可以在配置文件中清晰地看出系統中存在那些切面。
XML配置費方式缺點:
⊙ 使用XML配置方式不能將切面、切入點、增強處理等封裝到一個地方。如果需要查看切面、切入點、增強處理,必須同時結合Java文件和XML配置文件來查看;但使用@AspectJ時,則只需要一個單獨的類文件即可看到切面、切入點和增強處理的全部信息。
⊙ XML配置方式比@AspectJ方式有更多的限制:僅支持“singleton”切面Bean,不能在XML中組合多個命名連接點的聲明。
@AspectJ切面還有一個優點,就是能被Spring AOP和AspectJ同時支持,如果有一天需要將應用改為使用AspectJ來實現AOP,使用@AspectJ將非常容易遷移到基於AspectJ的AOP實現中。
在Spring 配置文件中,所有的切面、切入點和增強處理都必須定義在<aop:config.../>元素內部。
<beans.../>元素下可以包含多個<aop:config.../>元素,一個<aop:config>可以包含pointcut、advisor和aspect元素,且這三個元素必須按照此順序來定義。
<aop:config.../>元素所包含的子元素如圖:
圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖
圖 圖
圖 圖
圖 圖
圖 圖
圖 圖
圖 圖
圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖圖
注意:
使用<aop:config.../>方式進行配置時,可能與Spring的自動代理方式相沖突,例如使用<aop:aspectj-autoproxy/>或類似方式顯式啟用了自動代理,則可能會導致出現問題(比如有些增強處理沒有被織入)。因此建議:要么全部使用<aop:config.../>配置方式,要么全部使用自動代理方式,不要兩者混合使用。
1.配置切面
定義切面使用<aop:aspect.../>元素,使用該元素來定義切面時,其實質是將一個已有的Spring Bean轉換成切面Bean,所以需要先定義個一個普通的Spring Bean。
因為切面Bean可以當成一個普通的Spring Bean來配置,所以完全可以為該切面Bean 配置依賴注入。當切面Bean定義完成后,通過在<aop:aspect.../>元素中使用ref屬性來引用該Bean,就可以該Bean轉換成一個切面Bean了。
配置<aop:aspect.../>元素時可以指定如下三個屬性:
⊙ id : 定義該切面的標識名。
⊙ ref : 用於該ref屬性所引用的普通Bean轉換為切面Bean。
⊙ order : 指定該切面Bean的優先級,該屬性的作用與前面@AspectJ中的@Order注解、Ordered接口的作用完全一樣,order屬性值越小,該切面對應的優先級越高。
擴展:關於Order接口點這里,暫時懶得看。留在明天的明天的明天的明天的明天有空再看吧。
配置定義一個切面:
<?xml version="1.0" encoding="UTF-8"?> <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd語義約束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:P="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 定義一個普通Bean實例,該Bean實例將作為Aspect Bean --> <bean id="afterAdviceBean" class="edu.pri.lime._8_4_6.xmlaspect.AfterAdviceTest" /> <aop:config> <!-- 將容器中AfterAdviceBean轉換成切面Bean 切面Bean的新名稱為:afterAdviceAspect --> <aop:aspect id="afterAdviceAspect" ref="afterAdviceBean" order="100"/> </aop:config> </beans>
由於Spring支持將切面Bean當成普通Bean來管理,所以完全可以利用依賴注入來管理切面Bean,管理切面Bean的屬性值、依賴管理等。
2.配置增強處理
使用XML一樣可以配置Before、After、AfterReturning、AfterThrowing、Around五種增強處理,而且完全支持和@AspectJ完全一樣的語義。
使用XML配置增強處理分別依賴於如下幾個元素:
⊙ <aop:before.../> : 配置Before增強處理。
⊙ <aop:after.../> : 配置After增強處理。
⊙ <aop:after-returning.../>:配置AfterReturning增強處理。
⊙ <aop:after-throwing.../>:配置AfterThrowing增強處理。
⊙ <aop:around.../> : 配置AfterThrowing增強處理。
這些元素都不支持使用子元素,但通常可指定如下屬性:
⊙ pointcut : 該屬性指定一個切入表達式,Spring將在匹配該表達式的連接點時織入該增強處理。
⊙ pointcut-ref : 該屬性指定一個已經存在的切入點名稱,通常pointcut和pointcut-ref兩個屬性只需要使用其中之一。
⊙ method : 該屬性指定一個方法名,指定該切面Bean的該方法轉換為增強處理。
⊙ throwing : 該屬性只對<after-throwing.../>元素有效,用於指定一個形參名,AfterThrowing增強處理方法可通過該形參訪問目標方法所拋出的異常。
⊙ returning : 該屬性只對<after-returning.../>元素有效,用於指定一個形參名,AfterReturning增強處理方法可通過該形參訪問目標方法的返回值。
當定義切入點表達式時,XML配置方式和@AspectJ注解方式支持完全相同的切入點指示符,一樣可以支持execution、within、args、this、target和bean等切入點指示符。
XML配置方式和@AspectJ注解方式一樣支持組合切入點表達式,但XML配置方式不再使用簡單的&&、|| 和 ! 作為組合運算符(因為直接在XML文件中需要使用實體引用來表示它們),而是使用如下三個組合運算符:and、or和not。
Class : 刪除Annotation注釋--FourAdviceTest
package edu.pri.lime._8_4_6.xmlaspect; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class FourAdviceTest { public Object processTx(ProceedingJoinPoint jp) throws Throwable{ System.out.println("Around 增強 : 執行目標方法之前,模擬開始事務..."); Object[] args = jp.getArgs(); if(null != args && args.length > 0 && args[0].getClass() == String.class){ args[0] = "【增加的前綴】" + args[0]; } Object rvt = jp.proceed(args); System.out.println("Around 增強 : 執行目標方法后,模擬結束事務..."); if(null != rvt && rvt instanceof Integer){ rvt = (Integer)rvt * (Integer)rvt; } return rvt; } public void authorith(JoinPoint jp){ System.out.println("Before 增強 : 模擬執行權限檢查"); System.out.println("Before 增強 : 被織入增強處理的目標方法為 : " + jp.getSignature().getName()); System.out.println("Before 增強 : 目標方法的參數為 : " + Arrays.toString(jp.getArgs())); System.out.println("Before 增強 : 被織入增強處理的目標對象為 : " + jp.getTarget()); System.out.println("Before 增強 : AOP框架為目標對象生成的代理對象為 : " + jp.getThis()); } public void log(JoinPoint jp,Object rvt){ System.out.println("AfterReturning 增強 : 獲取目標方法返回值 : " + rvt); System.out.println("AfterReturning 增強 : 模擬記錄日志功能..."); System.out.println("AfterReturning 增強 : 被織入增強處理的目標方法為 : " + jp.getSignature().getName()); System.out.println("AfterReturning 增強 : 目標方法的參數為 : " + Arrays.toString(jp.getArgs())); System.out.println("AfterReturning 增強 : 被織入增強處理的目標對象為 : " + jp.getTarget()); System.out.println("AfterReturning 增強 : AOP框架為目標對象生成的代理對象為 : " + jp.getThis()); } public void release(JoinPoint jp){ System.out.println("After 增強 : 模擬方法結束后的釋放資源..."); System.out.println("After 增強 : 被織入增強處理的目標方法為 : " + jp.getSignature().getName()); System.out.println("After 增強 : 目標方法的參數為 : " + Arrays.toString(jp.getArgs())); System.out.println("After 增強 : 被織入增強處理的目標對象為 : " + jp.getTarget()); System.out.println("After 增強 : AOP框架為目標對象生成的代理對象為 : " + jp.getThis()); } }
Class : SecondAdviceTest
package edu.pri.lime._8_4_6.xmlaspect; public class SecondAdviceTest { // 視圖通過該arg形參來訪問目標方法的參數值,這需要在配置該切面Bena時使用args切入點指示符 public void authorith(String arg){ System.out.println("目標方法的參數為 : " + arg); System.out.println("①號Before增強 : 模擬執行權限檢查"); } }
XML :
<?xml version="1.0" encoding="UTF-8"?> <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd語義約束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:P="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <aop:config> <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2"> <!-- 定義一個Around增強處理 直接指定切入點表達式 以切面Bean中的processTx()方法作為增強處理方法 --> <aop:around method="processTx" pointcut="execution(* edu.pri.lime._8_4_6.xmladvice.bean.impl.*.*(..))" /> <!-- 定義一個Before增強處理 直接指定切入點表達式 以切面Bean中的authority()方法作為增強處理方法 --> <aop:before method="authority" pointcut="execution(* edu.pri.lime._8_4_6.xmladvice.bean.impl.*.*(..))" /> <!-- 定義一個AfterReturning增強處理 直接指定切入點表達式 以切面Bean中的log()方法作為增強處理方法 --> <aop:after-returning method="log" pointcut="execution(* edu.pri.lime._8_4_6.xmladvice.bean.impl.*.*(..))" returning="rvt" /> <!-- 定義一個After增強 直接指定切入點表達式 以切面Bean中的release()方法作為增強處理方法 --> <aop:after method="release" pointcut="execution(* edu.pri.lime._8_4_6.xmladvice.bean.impl.*.*(..))" /> </aop:aspect> <!-- 將secondAdviceBean轉換成切面Bean 切面Bean的新名稱為:secondAdviceAspect 指定該切面的優先級為1,該切面里的增強處理將被有限織入 --> <aop:aspect id="secondAdviceAspect" ref="secondAdviceBean" order="1"> <aop:before method="authority" pointcut="execution(* edu.pri.lime._8_4_6.xmladvice.bean.impl.*.*(..)) and args(arg)" /> </aop:aspect> </aop:config> <bean id="hello" class="edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl" /> <bean id="fourAdviceBean" class="edu.pri.lime._8_4_6.xmladvice.FourAdviceTest" /> <bean id="secondAdviceBean" class="edu.pri.lime._8_4_6.xmladvice.SecondAdviceTest" /> </beans>
Class : AspectTest
package edu.pri.lime._8_4_6.xmladvice.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.pri.lime._8_4_6.xmladvice.bean.Hello; public class AspectTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("app_8_4_6_advice.xml"); Hello hello = (Hello) ctx.getBean("hello"); hello.foo(); System.out.println("--------------Bang bang, he shot me down----------------"); hello.addUser("lime", "1234"); System.out.println("--------------C'est la vie----------------"); hello.deleteUser("lime"); } }
Console :
Around 增強 : 執行目標方法之前,模擬開始事務... 22:03:38.837 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' ② Before 增強 : 模擬執行權限檢查 ② Before 增強 : 被織入增強處理的目標方法為 : foo ② Before 增強 : 目標方法的參數為 : [] ② Before 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 ② Before 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 調用HelloImpl組件的foo()方法... Around 增強 : 執行目標方法后,模擬結束事務... 22:03:38.840 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' AfterReturning 增強 : 獲取目標方法返回值 : null AfterReturning 增強 : 模擬記錄日志功能... AfterReturning 增強 : 被織入增強處理的目標方法為 : foo AfterReturning 增強 : 目標方法的參數為 : [] AfterReturning 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 AfterReturning 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 22:03:38.840 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' After 增強 : 模擬方法結束后的釋放資源... After 增強 : 被織入增強處理的目標方法為 : foo After 增強 : 目標方法的參數為 : [] After 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 After 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 --------------Bang bang, he shot me down---------------- 22:03:38.841 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' Around 增強 : 執行目標方法之前,模擬開始事務... 22:03:38.841 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' ② Before 增強 : 模擬執行權限檢查 ② Before 增強 : 被織入增強處理的目標方法為 : addUser ② Before 增強 : 目標方法的參數為 : [【增加的前綴】lime, 1234] ② Before 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 ② Before 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 調用HelloImpl組件的addUser()方法添加用戶 : 【增加的前綴】lime Around 增強 : 執行目標方法后,模擬結束事務... 22:03:38.841 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' AfterReturning 增強 : 獲取目標方法返回值 : 484 AfterReturning 增強 : 模擬記錄日志功能... AfterReturning 增強 : 被織入增強處理的目標方法為 : addUser AfterReturning 增強 : 目標方法的參數為 : [【增加的前綴】lime, 1234] AfterReturning 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 AfterReturning 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 22:03:38.841 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' After 增強 : 模擬方法結束后的釋放資源... After 增強 : 被織入增強處理的目標方法為 : addUser After 增強 : 目標方法的參數為 : [【增加的前綴】lime, 1234] After 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 After 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 --------------C'est la vie---------------- 22:03:38.849 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'secondAdviceBean' 目標方法的參數為 : lime ① Before增強 : 模擬執行權限檢查 22:03:38.850 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' Around 增強 : 執行目標方法之前,模擬開始事務... 22:03:38.850 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' ② Before 增強 : 模擬執行權限檢查 ② Before 增強 : 被織入增強處理的目標方法為 : deleteUser ② Before 增強 : 目標方法的參數為 : [【增加的前綴】lime] ② Before 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 ② Before 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 調用HelloImpl組件的deleteUser()方法刪除用戶 : 【增加的前綴】lime Around 增強 : 執行目標方法后,模擬結束事務... 22:03:38.850 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' AfterReturning 增強 : 獲取目標方法返回值 : 54756 AfterReturning 增強 : 模擬記錄日志功能... AfterReturning 增強 : 被織入增強處理的目標方法為 : deleteUser AfterReturning 增強 : 目標方法的參數為 : [【增加的前綴】lime] AfterReturning 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 AfterReturning 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 22:03:38.850 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'fourAdviceBean' After 增強 : 模擬方法結束后的釋放資源... After 增強 : 被織入增強處理的目標方法為 : deleteUser After 增強 : 目標方法的參數為 : [【增加的前綴】lime] After 增強 : 被織入增強處理的目標對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0 After 增強 : AOP框架為目標對象生成的代理對象為 : edu.pri.lime._8_4_6.xmladvice.bean.impl.HelloImpl@40238dd0
3.配置切入點
XML配置方式也可以通過定義切入點來重用切入點表達式,Spring提供了<aop:pointcut.../>元素來定義切入點。
當把<aop:pointcut.../>元素作為<aop:config.../>的子元素定義時,表明該切入點可別多個切面共享;
當把<aop:pointcut.../>元素作為<aop:aspect.../>的子元素定義時,表明該切入點只能在切面中有效;
配置<aop:pointcut.../>元素時通常需要指定如下兩個屬性:
⊙ id : 指定該切入點的標識名;
⊙ expression : 指定該切入點關聯的切入點表達式;
配置切入點表達式Demo :
<aop:config> <aop:pointcut expression="execution(* edu.pri.lime._8_4_6.pointcut.demo.bean.impl.*.*(..))" id="myPointcut"/> </aop:config>
配置已有的切入點Demo :
<aop:config> <aop:pointcut expression="edu.pri.lime._8_4_6.pointcut.demo.SystemArchitecture.myPointcut()" id="myPointcut"/> </aop:config>
Class : RepairAspect
package edu.pri.lime._8_4_6.pointcut; public class RepairAspect { public void doRecoveryActions(Throwable ex){ System.out.println("目標方法中拋出的異常 : " + ex); System.out.println("模擬Advice對異常的修復..."); } }
XML :
<?xml version="1.0" encoding="UTF-8"?> <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd語義約束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:P="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="hello" class="edu.pri.lime._8_4_6.pointcut.bean.impl.HelloImpl"/> <bean id="repairAspect" class="edu.pri.lime._8_4_6.pointcut.RepairAspect"/> <aop:config> <aop:pointcut expression="execution(* edu.pri.lime._8_4_6.pointcut.bean.impl.*.*(..))" id="myPointcut"/> <aop:aspect id="afterThrowingAdviceBean" ref="repairAspect"> <aop:after-throwing method="doRecoveryActions" pointcut-ref="myPointcut" throwing="ex"/> </aop:aspect> </aop:config> </beans>
Class : PointcutTest
package edu.pri.lime._8_4_6.pointcut.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.pri.lime._8_4_6.pointcut.bean.Hello; public class PointcutTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("app_8_4_6_pointcut.xml"); Hello hello = (Hello) ctx.getBean("hello"); hello.addUser("li", "2345"); } }
Console :
調用組件的addUser方法添加用戶 : li 22:44:03.511 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'repairAspect' 目標方法中拋出的異常 : java.lang.IllegalArgumentException: name 長度小於3 或 大於10 模擬Advice對異常的修復... Exception in thread "main" java.lang.IllegalArgumentException: name 長度小於3 或 大於10 at edu.pri.lime._8_4_6.pointcut.bean.impl.HelloImpl.addUser(HelloImpl.java:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy2.addUser(Unknown Source) at edu.pri.lime._8_4_6.pointcut.main.PointcutTest.main(PointcutTest.java:13)
啦啦啦
啦啦啦
啦啦啦
啦啦啦
