中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring AOP的使用詳解

發布時間:2020-09-28 22:31:06 來源:腳本之家 閱讀:149 作者:Wan QingHua 欄目:編程語言

什么是AOP

AOP(Aspect Oriented Programming 面向切面編程),通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

常用于日志記錄,性能統計,安全控制,事務處理,異常處理等等。

定義AOP術語

切面(Aspect):切面是一個關注點的模塊化,這個關注點可能是橫切多個對象;

連接點(Join Point):連接點是指在程序執行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候;

通知(Advice):指在切面的某個特定的連接點上執行的動作。Spring切面可以應用5中通知:

  • 前置通知(Before):在目標方法或者說連接點被調用前執行的通知;
  • 后置通知(After):指在某個連接點完成后執行的通知;
  • 返回通知(After-returning):指在某個連接點成功執行之后執行的通知;
  • 異常通知(After-throwing):指在方法拋出異常后執行的通知;
  • 環繞通知(Around):指包圍一個連接點通知,在被通知的方法調用之前和之后執行自定義的方法。

切點(Pointcut):指匹配連接點的斷言。通知與一個切入點表達式關聯,并在滿足這個切入的連接點上運行,例如:當執行某個特定的名稱的方法。

引入(Introduction):引入也被稱為內部類型聲明,聲明額外的方法或者某個類型的字段。

目標對象(Target Object):目標對象是被一個或者多個切面所通知的對象。

AOP代理(AOP Proxy):AOP代理是指AOP框架創建的對對象,用來實現切面契約(包括通知方法等功能)

織入(Wearving):指把切面連接到其他應用出程序類型或者對象上,并創建一個被通知的對象。或者說形成代理對象的方法的過程。

Spring對AOP的支持

  1. 基于代理的經典SpringAOP;
  2. 純POJO切面;
  3. @AspectJ注解驅動的切面;
  4. 注入式AspectJ切面(適用于Spring各版本);

前三種都是SpringAOP實現的變體,SpringAOP構建在動態代理基礎之上,因此,Spring對AOP的支持局限于方法的攔截。

切入點表達式

使用SpringAOP

SpringAOP的支持必須呀導入spring-aspects的jar包

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>4.3.5.RELEASE</version>
</dependency>

使用注解定義切面

采用注解的方式定義切面以及通知

@Aspect
public class Audience {
  //使用@Pointcut注解聲明頻繁使用的切入點表達式
  @Pointcut("execution(* com.wqh.concert.Performance.perform(..))")
  public void performance(){}
  
  @Before("performance()")
  public void silenceCellPhones(){
    System.out.println("Sillencing cell phones");
  }
  @Before("performance()")
  public void takeSeats(){
    System.out.println("Task Seat");
  }
  @AfterReturning("performance()")
  public void applause(){
    System.out.println("CLAP CLAP CLAP");
  }
  @AfterThrowing("performance()")
  public void demandRefund(){
    System.out.println("Demand a Refund");
  }
}

另外需要在applicationContext.xml也就是spring的配置文件中添加配置:

<!--啟用AspectJ的自動代理-->
<aop:aspectj-autoproxy/>
<!--聲明bean-->
<bean class="com.wqh.concert.Audience"/>

在XML中聲明切面

定義pojo類,這里只是把上面定義的注解全public class AudienceXML {

public void silenceCellPhones() {
  System.out.println("Sillencing cell phones");
}
public void takeSeats() {
  System.out.println("Task Seat");
}
public void applause() {
  System.out.println("CLAP CLAP CLAP");
}
public void demandRefund() {
  System.out.println("Demand a Refund");
}

applicationContext.xml配置

<!--聲明bean-->
<bean name="audienceXML" class="com.wqh.concert.AudienceXML"/>
<aop:config>
  <!--引入bean-->
  <aop:aspect ref="audienceXML">
    <!--定義切點-->
    <aop:pointcut id="perform"
           expression="execution(* com.wqh.concert.Performance.perform(..))"/>
    <!--定義通知
      method:通知,也就是具體的方法
      pointcut-ref:引用的切點
      pointcut:切點-->
    <aop:before method="silenceCellPhones"
          pointcut-ref="perform"/>
    <aop:before method="takeSeats" pointcut-ref="perform"/>
    <aop:after-returning method="applause" pointcut-ref="perform"/>
    <aop:after-throwing method="demandRefund"
              pointcut="execution(* com.wqh.concert.Performance.perform(..))"/>
  </aop:aspect>
</aop:config>

環繞通知

在springAOP中有五種通知,環繞通知是最為強大的通知。它能夠讓你編寫的邏輯將被通知的目標方法完全包裝起來。實際上就像在一個通知方法中同時編寫前置通知和后置通知。
本片文章具體講解環繞通知的使用。

使用注解

使用環繞通知定義切面:

@Aspect
public class AudienceAround {
  //使用@Pointcut注解聲明頻繁使用的切入點表達式
  @Pointcut("execution(* com.wqh.concert.Performance.perform(..))")
  public void performance(){}
  @Around("performance()")
  public void watchPerformance(ProceedingJoinPoint joinPoint){
    try {
      System.out.println("Silencing cell phones");
      System.out.println("Taking seats");
      joinPoint.proceed();
      System.out.println("Demanding a refund");
    } catch (Throwable throwable) {
      throwable.printStackTrace();
    }
  }
}

可以看到在上面的代碼中,定義通知的時候在通知方法中添加了入參:ProceedingJoinPoint。在創建環繞通知的時候,這個參數是必須寫的。因為在需要在通知中使用ProceedingJoinPoint.proceed()方法調用被通知的方法。

另外,如果忘記調用proceed()方法,那么通知實際上會阻塞對被通知方法的調用。

在XML中定義

首先去掉上面類的所有注解:這里為了區別就重新創建一個類

public class AudienceAroundXML {
  public void watchPerformance(ProceedingJoinPoint joinPoint){
    try {
      System.out.println("Silencing cell phones");
      System.out.println("Taking seats");
      joinPoint.proceed();
      System.out.println("Demanding a refund");
    } catch (Throwable throwable) {
      throwable.printStackTrace();
    }
  }
}

配置:

<!--聲明bean-->
<bean name="audienceAroundXML" class="com.wqh.concert.AudienceAroundXML"/>
 <!--配置切面及通知-->
<aop:config>
  <aop:aspect ref="audienceAroundXML">
    <aop:pointcut id="performance"
           expression="execution(* com.wqh.concert.Performance.perform(..))"/>
    <aop:around method="watchPerformance" pointcut-ref="performance"/>
  </aop:aspect>
</aop:config>

處理通知中的參數

Spring借助AspectJ的切點表達式語言來定義Spring切面

在spring中嘗試使用其他指示器時,會拋出IllegalArgument-Exception異常。

如上的這些指示器,只有exception指示器是實際執行匹配的,而其他都是用來限制匹配的。

切面表達式分析

帶參數的切點表達式分解

在該切點表達式中使用了args(trackNumber)限定符。表示傳遞給playTrack()方法的int類型參數也會傳遞到通知中去。參數名trackNumber也與切點方法簽名中的參數相匹配。

創建切面

@Aspect
public class TrackCounter {
  @Pointcut("execution(* com.wqh.aop.CompactDisc.playTrack(int))&&args(trackNumber)")
  public void trackPlayder(int trackNumber){}
  @Before("trackPlayder(trackNumber)")
  public void countTrack(int trackNumber) {
    System.out.println("前置通知:targetNumber=" + trackNumber);
  }
}

連接點類

@Service
public class CompactDisc {
  public void playTrack(int trackNumber){
    System.out.println("trackNumber =" + trackNumber);
  }
}

XML配置

<!--啟用AspectJ的自動代理-->
<aop:aspectj-autoproxy/>
<!--聲明bean-->
 <bean class="com.wqh.aop.TrackCounter"/>
 <!--自動掃描包下的類-->
<context:component-scan base-package="com.wqh.aop"/>

測試

@Test
public void testT(){
  ApplicationContext applicationContext =
      new ClassPathXmlApplicationContext(
          new String[]{"classpath:/spring/applicationContext.xml"});
  CompactDisc compactDisc = (CompactDisc) applicationContext.getBean("compactDisc");
  compactDisc.playTrack(12);
}

上面給指定方法傳入的參數是12,在通知中獲取到了該參數

另外:在xml中配置切面來處理通知中的參數,其實也差不多,只是把切點表達式放到了XML配置文件中。

給類添加新的功能

引入Spring實戰中的知識

在SpringAOP中,我們可以為Bean引入新的方法。代理攔截器調用并委托給實現該方法的其他對象。

當引入接口的方法被調用時,代理會把此調用委托給實現了新接口的某給其他對象。

使用注解方式引入

代碼
首先是連接點的接口及其實現類

public interface Person {
  void say();
}
public class ChinesePerson implements Person {
  @Override
  public void say() {
    System.out.println("說中文");
  }
}

創建需要添加的功能,這里個人類擴展一個吃的功能

public interface Food {
  void eat();
}
public class ChineseFood implements Food {
  @Override
  public void eat() {
    System.out.println("吃中餐");
  }
}

編寫切面

@Aspect
public class addFuction {
  @DeclareParents(value = "com.wqh.addfunction.Person+",defaultImpl = ChineseFood.class)
  public static Food food;
}

注意這里的表達式使用的式@DeclareParents注解;該注解所標注的靜態屬性指明了要引入的接口。

注解中使用的value屬性指定哪種類型的bean要引入該接口,這里Person后后面的“+”號表示所有子類型,而不是該類的本身。defaultImpl,指定了為引入功能提供實現的類。

使用XML配置bean:

<!--啟用AspectJ的自動代理-->
<aop:aspectj-autoproxy/>
<!--聲明bean-->
<bean class="com.wqh.addfunction.addFuction"/>
<bean name="chinesePerson" class="com.wqh.addfunction.ChinesePerson"/>

測試

@Test
public void testAdd(){
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
      "classpath:spring/applicationContext.xml");
  Person person = (Person) applicationContext.getBean("chinesePerson");
  person.say();
  //這里可以將chinesePerson bean轉換為Food類,所以添加成功
  Food food = (Food) applicationContext.getBean("chinesePerson");
  food.eat();
}

在XML中引入

首先將上面的addFuction注解全部刪除,其他不變;然后在xml中添加相應的配置:

<!--啟用AspectJ的自動代理-->
<aop:aspectj-autoproxy/>
<!--聲明bean-->
<bean name="chinesePerson" class="com.wqh.addfunction.ChinesePerson"/>
 <aop:config>
   <aop:aspect>
     <aop:declare-parents types-matching="com.wqh.addfunction.Person+"
               implement-interface="com.wqh.addfunction.Food"
     default-impl="com.wqh.addfunction.ChineseFood"/>
   </aop:aspect>
 </aop:config>

這里的types-matching與上面的vale作用一樣;

default-impl與defaultImpl作用一樣,這也可以使用delegate-ref;當然如果使用delegate-ref則是要引用SpringBean;

implement-interface則是要引入的接口

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

镇平县| 金塔县| 正蓝旗| 璧山县| 中西区| 黄平县| 巫溪县| 依安县| 湄潭县| 永嘉县| 元谋县| 扎鲁特旗| 积石山| 武清区| 南丹县| 务川| 城固县| 云林县| 宁国市| 巫山县| 崇阳县| 荣成市| 海南省| 巴东县| 楚雄市| 益阳市| 邹城市| 陆川县| 奉贤区| 长乐市| 岢岚县| 晋州市| 郁南县| 曲阜市| 五峰| 屏东市| 上饶县| 收藏| 连州市| 古交市| 德庆县|