1. 程式人生 > >Spring AOP梳理

Spring AOP梳理

xml配置 調用 beans spec 必須 源碼 什麽 config color

一、Srping AOP

  AOP(Aspect Oriented Programming)解釋為面向切面編程,何為切面,用刀把一塊面包切成兩半,刀切下去形成的面就叫切面,那麽面向切面的就是形成切面的這把,刀切在哪(切入點),怎麽切(通知),切成什麽樣(通知實現方法),切的過程就是切面織入的過程。這種編程方式主要為了分離關註點並且能夠增強類的輔助功能。比如做日誌管理,權限控制等工作,在特定的時候和位置做特定的事情,而無需在原來方法中做特殊處理。

  AOP概念:

      • 切面:多個對象或方法要實現的交叉功能的集合。
      • 連接點:被關註的方法,執行了該方法會觸發通知。
      • 切入點:一系列連接點的集合。
      • 通知:切面的實際實現,在連接點方法被執行的時候在適當的地方插入通知。
        1. before:前置通知,在方法被調用之前調用
        2. after:後置通知,在方法執行結束之後調用,無論方法是否執行成功
        3. around:環繞通知,在目標方法執行前和執行結束之後分別執行自定義方法
        4. after-return:在方法成功執行之後調用,
        5. after-throwing:在方法拋出異常後調用
      • 目標對象:被通知的對象,可以是自己的編寫的類也可以是,第三方類對象。(連接點就是目標對象中的某些方法)
      • 代理:將通知應用到目標對象後創建的對象。
      • 織入:將切面應用到目標對象從而創建一個新的代理對象的過程。
      • 引入:為類添加新的方法和屬性。(<aop:declare-parents ../>)

二、AOP配置範例:

  1.spring中的aop的xml配置方式簡單實例

  2.spring中aop的註解實現方式簡單實例

  3.利用切面為特定的類添加新功能:

<aop:config>
        <!-- 兩個切面
--> <aop:aspect ref="company"> <!-- 利用切面為特定的類添加新功能 --> <aop:declare-parents types-matching="com.springAop.InstanceMine+" <!-- 實現了InstanceMine接口的實現類可以強轉成InstanceOtheer類型的對象並使用他的實現類的方法的方法 -->
        implement-interface
="com.springAop.InstanceOther"   default-impl="com.springAop.OtherImpl"/>  <!--InstanceOther的默認實現類,當InstanceMine對象強轉成InstanceOther的對象時,默認實現類為OtherImpl--> </aop:aspect> </aop:config>

  接口類:instanceMine和instanceOther

public interface InstanceMine {
    public void speak1(String meg);
}


public interface InstanceOther {
    public void speak2(String meg);
}

  接口實現類: OtherImple和MineImpl

public class MineImpl implements instanceMine {

    public void speak1(String meg) {
        System.out.println("s1");
    }
}



public class OtherImpl implements instanceOther {
    public void speak2(String meg) {
        System.out.println(meg);
    }
}

  測試類:Test

public class Test {
    @org.junit.jupiter.api.Test
    public void TestDemo(){
        ApplicationContext app = new FileSystemXmlApplicationContext("src/main/java/com/springAop/bean.xml");
       
        InstanceMine mine = app.getBean("mine",instanceMine.class);
        ((InstanceOther)mine).speak2("我可以說話嗎");//強轉成InstanceOther,並調用實現類OtherImpl的實現方法
    }
}

  結果:

  技術分享

三.以代理對象的方式實現AOP:

  1.前置通知:

 1 /**
 2  * 前置通知:實現MethodBeforeAdvice接口,在目標方法執行之前執行
 3  * 相當於aop配置<aop:before method="before" pointcut-ref="當前類的bean"/>
 4  */
 5 public class BeforeNotify implements MethodBeforeAdvice {
 6 
 7     public void before(Method method, Object[] objects, Object o) throws Throwable {
 8         System.out.println("=========== 前置通知 =========");
 9     }
10 }

  

  2.後置通知:

 1 /**
 2  * 後置(返回)通知:在方法結束返回時調用,一般包含在環繞通知結束前執行,
 3  * 方法成功執行有效,異常結束則該方法無效。
 4  * 相當於aop配置:<aop:after-returning method="afterReturning" pointcut-ref="當前類的bean"/>
 5  */
 6 public class AfterNotify implements AfterReturningAdvice {
 7     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
 8         System.out.println("=========== 後置通知 =========");
 9     }
10 }

  

  3.環繞通知:

 1 /**
 2  * 環繞通知:在方法執行前和執行後均可以執行自定義的方法,
 3  * 相當於aop配置:<aop:around method="invoke" pointcut-ref="當前類的Bean"/>
 4  */
 5 public class AroundNotify implements MethodInterceptor {
 6 
 7     public Object invoke(MethodInvocation methodInvocation) throws Throwable {
 8         System.out.println("=========== 環繞通知 =========");
 9         Object obj = methodInvocation.proceed();//執行目標方法
10         System.out.println("=========== 環繞通知 =========");
11         return obj;
12     }
13 }

  

  4.異常通知:

 1 /**
 2  * 異常通知:在目標方法執行異常時執行,
 3  * 註意:ThrowsAdvice是個空接口,裏面未定義任何待實現類
 4  * 相當於aop配置:<aop:after-throwing method="afterThrowing" pointcut-ref="當前類的bean"/>
 5  */
 6 public class ExceptionNotify implements ThrowsAdvice {
 7     public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
 8         System.out.println("方法:"+method.getName()+"出錯了\n原因:"+ex.getMessage());
 9     }
10 }

  此處的ThrowAdvice接口是沒有實現方法的,但是又不允許隨便定義,在源碼中我們看到了規定的幾個方法,因為此接口中,沒有任何實現方法,當被利用反射機制調用的時候,必須實現一下方法中的一種,這是源碼註釋的說明:

技術分享

技術分享

  5.bean配置

 1 <beans xmlns="http://www.springframework.org/schema/beans"
 2        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 4 
 5         <!--  被代理對象 -->
 6         <bean class="com.springAop.proxyAOP.UserDao" id="userDao"/>
 7 
 8         <!-- 關聯通知 -->
 9         <bean class="com.springAop.proxyAOP.BeforeNotify" id="before"/>
10         <bean class="com.springAop.proxyAOP.AroundNotify" id="aroundNotify"/>
11         <bean class="com.springAop.proxyAOP.ExceptionNotify" id="exceptionNotify"/>
12         <bean class="com.springAop.proxyAOP.AfterNotify" id="afterNotify"/>
13 
14         <!-- 代理對象 -->
15         <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
16             <!-- 代理對象接口,接口方法就是連接點 -->
17             <property name="proxyInterfaces">
18                 <list>
19                     <!--  目標對象實現的接口類,實現了那些接口都可以補充進去,接口的方法就是一個個連接點 -->
20                     <value>com.springAop.proxyAOP.IUserDao</value>
21                     <value>com.springAop.proxyAOP.BookDao</value>
22                 </list>
23             </property>
24 
25             <!-- 關聯通知類型 -->
26             <property name="interceptorNames">
27                 <list>
28                     <value>before</value><!-- 前置通知織入 -->
29                     <value>aroundNotify</value><!-- 環繞通知織入 -->
30                     <value>exceptionNotify</value><!-- 異常通知織入 -->
31                     <value>afterNotify</value><!-- 後置通知織入 -->
32                 </list>
33             </property>
34 
35             <!-- 關聯被代理對象(aop的目標對象) -->
36             <property name="target" ref="userDao"/>
37         </bean>
38 </beans>

  6.目標對象:(實現了兩個接口)

 1 public class UserDao implements IUserDao,BookDao{
 2     public void save() {
 3         System.out.println("保存用戶信息中。。。。");
 4         // int i=1/0;
 5     }
 6 
 7     public void getBook() {
 8         System.out.println("拿到一本書");
 9     }
10 }

  7.測試類:

 1 public class Test{
 2     @org.junit.jupiter.api.Test
 3     public void testDemo(){
 4         ApplicationContext app = new FileSystemXmlApplicationContext("file:G:\\DevelopSoftware\\IDEA\\workspace\\springDemo\\src\\main\\java\\com\\springAop\\proxyAOP\\bean.xml");
 5         /**
 6          * 獲取的bean是目標對象的代理對象,可以將代理對象強轉成任何目標對象實現的接口對象
 7          */
 8         IUserDao userDao = app.getBean("proxyFactoryBean",IUserDao.class);
 9         userDao.save();
10         System.out.println("=========================");
11         /**
12          * 將代理對象強轉成BookDao接口對象,並調用該接口方法。
13          */
14         ((BookDao)userDao).getBook();
15     }
16 }

  運行結果:

技術分享

  

Spring AOP梳理