10.AOP

10.AOP

什麼是AOP?java

  • AOP(Aspect Oriented Programming)爲面向切面編程web

  • 經過預編譯方式運行期間動態代理實現程序功能的統一維護的一種技術。spring

  • AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring]框架中的一個重要內容,是函數式編程的一種衍生範型。express

  • 利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降提升程序的可重用性,同時提升了開發的效率。編程

     

AOP的主要功能:api

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

  • 日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指定業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。app

10.1 AOP在Spring中的做用

提供聲明式事務管理,容許用戶自定義切面框架

AOP相關概念:ide

  • 橫切關注點:對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點

  • Aspect(切面): 一般是一個類,裏面能夠定義切入點和通知

  • JointPoint(鏈接點): 程序執行過程當中明確的點,通常是方法的調用。被攔截到的點,由於Spring只支持方法類型的鏈接點,因此在Spring中鏈接點指的就是被攔截到的方法,實際上鍊接點還能夠是字段或者構造器。(實際上就是被加強的方法

  • Advice(通知): AOP在特定的切入點上執行的加強處理,有before(前置),after(後置),afterReturning(最終),afterThrowing(異常),around(環繞) 其實afterReturningafterThrowing兩者只會有其中一個執行

    • Before: 在目標方法被調用以前作加強處理,@Before只須要指定切入點表達式便可

    • AfterReturning: 在目標方法正常完成後作加強,@AfterReturning除了指定切入點表達式後,還能夠指定一個返回值形參名returning,表明目標方法的返回值

    • AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式後,還能夠指定一個throwing的返回值形參名,能夠經過該形參名來訪問目標方法中所拋出的異常對象

    • After: 在目標方法完成以後作加強,不管目標方法時候成功完成。@After能夠指定一個切入點表達式

    • Around:環繞通知在目標方法完成先後作加強處理,環繞通知是最重要的通知類型,像事務,日誌等都是環繞通知,注意編程中核心是一個ProceedingJoinPoint

  • Pointcut(切入點): 就是帶有通知的鏈接點,在程序中主要體現爲書寫切入點表達式(expression)

  • weave(織入): 將切面應用到目標對象並致使代理對象建立的過程

  • introduction(引入): 在不修改代碼的前提下,引入能夠在運行期爲類動態地添加一些方法或字段

  • AOP代理(AOP Proxy): AOP框架建立的對象,代理就是目標對象的增強。Spring中的AOP代理可使JDK動態代理,也能夠是CGLIB代理,前者基於接口,後者基於子類

  • 目標對象(Target Object): 包含鏈接點的對象。也被稱做被通知或被代理對象。

  •  

     

 

 

img

 

 

10.2 Spring的AOP實現方式一:使用spring的API接口實現

先上代碼,每一部分代碼都會有講解

第一步:導入相關依賴

  • 這個依賴中文翻譯過來叫切面(裏面能夠定義切入點和通知)織入(將切面應用到目標對象並致使代理對象建立的過程)

 <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.9.5</version>
 </dependency>

第二步:編寫UserService接口以及UserServiceImpl實現類

 public interface UserService {
 
     public void add();
     public void delete();
     public void update();
     public void select();
 
 }
 public class UserServiceImpl implements UserService{
     @Override
     public void add() {
         System.out.println("增長了一個用戶");
    }
 
     @Override
     public void delete() {
         System.out.println("刪除了一個用戶");
    }
 
     @Override
     public void update() {
         System.out.println("修改了一個用戶");
    }
 
     @Override
     public void select() {
         System.out.println("查詢了一個用戶");
    }
 }

第三步:編寫Log(用於前置通知的類) 這個類最後會在方法執行前 執行

  • 咱們編寫的這個Log類實現了前置通知的方法類

  • 參數method指定咱們須要執行的方法

  • 參數args 指定咱們方法的參數

  • 參數target表示咱們須要代理的對象

 package com.xuan.log;
 
 import org.springframework.aop.MethodBeforeAdvice;
 
 import java.lang.reflect.Method;
 
 public class Log implements MethodBeforeAdvice {
     /**
      *
      * @param method the method being invoked
      * @param args the arguments to the method
      * @param target the target of the method invocation. May be {@code null}.
      * @throws Throwable
      */
     @Override
     public void before(Method method, Object[] args, Object target) throws Throwable {
         System.out.println(target.getClass().getName()+"執行了"+method.getName()+"方法");
    }
 }

 

第四步:編寫AfterLog類實現AfterReturningAdvice完成通知 這個類中方法會在方法執行完執行

  • 咱們編寫的這個AfterLog類實現了完成通知的方法類

  • 參數returnValue就是方法執行後的返回值類型,因爲咱們UserServiceImpl類中方法並無返回值因此執行完這個通知應該是返回null

  • Throwable:這個參數就是執行錯誤拋出的異常

 package com.xuan.log;
 
 import org.springframework.aop.AfterReturningAdvice;
 
 import java.lang.reflect.Method;
 
 public class AfterLog implements AfterReturningAdvice {
     /**
      *
      * @param returnValue
      * @param method
      * @param args
      * @param target
      * @throws Throwable
      */
     @Override
     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
         System.out.println("執行"+method.getName()+"方法返回"+returnValue);
    }
 }

第五步:編寫配置文件

  • 第一點因爲你要使用到aop切面,因此你須要導入相關的約束

  • 第二點:須要將咱們寫的類都注入到springIOC容器中

  • 而後配置原生的Spring api接口

  • aop:config標籤:表示配置的是aop

  • aop:pointcut標籤:表示要配置的是aop的切入點(切入點就是帶有通知的鏈接點) id指定惟一標識 expression指定切入點表達式

  • 切入點表達式 execution寫法: 返回值類型 類名.方法名(參數) * com.xuan.service.UserServiceImpl.*(..)

  • aop:advisor標籤:執行通知加強的 須要指定切入點id屬性以及咱們本身編寫的前置和完成通知的引用

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
 
     <!--註冊bean-->
     <bean id="userService" class="com.xuan.service.UserServiceImpl"/>
     <bean id="log" class="com.xuan.log.Log"/>
     <bean id="afterLog" class="com.xuan.log.AfterLog"/>
 
 
     <!--方式一:使用原生的spring api接口-->
     <!--配置aop-->
     <aop:config>
         <!--切入點表達式 execution 返回值類型 類名.方法名(參數) -->
         <aop:pointcut id="pointcut" expression="execution(* com.xuan.service.UserServiceImpl.*(..))"/>
 
         <!--增長環繞通知-->
         <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
         <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
     </aop:config>
 
 </beans>

第五步:編寫測試類

  • 這裏須要注意的就是動態代理代理的是接口,因此咱們不能用其實現類去調用方法,並不能起到加強的做用還會報錯

 import com.xuan.service.UserService;
 import com.xuan.service.UserServiceImpl;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
 public class TestAOP {
 
     public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         //動態代理的是接口
         UserService userService = context.getBean("userService", UserService.class);
 
         userService.add();
    }
 }

第六步:看看測試結果

 com.xuan.service.UserServiceImpl執行了add方法    ------>Log類中before方法輸出的結果
 增長了一個用戶
 執行add方法返回null ----------->AfterLog類中afterReturning方法輸出的結果

 

 

10.3 Spring的AOP實現方式二:自定義實現

這個自定義的關鍵就是aspect切面對應的aop:aspect 標籤的使用

第一步:編寫本身定義的類

 package com.xuan.custom;
 
 public class CustomPointCut {
     public void before(){
         System.out.println("前置通知");
    }
 
     public void after(){
         System.out.println("後置通知");
    }
 }

第二步:編寫配置文件

  • aop:aspect標籤訂義的是一個類

  • aop:pointcut標籤訂義的是切入點表達式

  • aop:before標籤指定前置通知method屬性指定方法

  • aop:after標籤指定後置通知method屬性指定方法

  <bean id="costomPointCut" class="com.xuan.custom.CustomPointCut"/>
 <!--方式二:自定義類-->
 <!--切面;橫切關注點,被模塊化的特殊對象,是一個類-->
 <aop:config>
 
     <aop:aspect ref="costomPointCut">
         <aop:pointcut id="pointcut" expression="execution(* com.xuan.service.UserServiceImpl.*(..))"/>
         <aop:before method="before" pointcut-ref="pointcut"/>
         <aop:after method="after" pointcut-ref="pointcut"/>
     </aop:aspect>
     
 </aop:config>

最後就測試,而後就測試成功!

 

 

 

10.4 SpringAOP實現方式三:使用註解實現

使用註解開發是相對簡單的,下面分析下一個關鍵點

  •  <!--開啓aop註解支持--> 
     <aop:aspectj-autoproxy/>
  • @Aspect註解:代表這是一個切面類至關於aop:aspect標籤

  • @Before註解:說明這是前置通知 裏面須要指定execution表達式

  • @After註解:說明這是後置通知 裏面須要指定execution表達式

相關文章
相關標籤/搜索