spring aop知識點整理

Aspect(切面): Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含着一些 Pointcut 以及相應的 Advice。
Joint point(連接點):表示在程序中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程序塊的執行等等,它自身還可以嵌套其它 joint point。
Pointcut(切點):表示一組 joint point,這些 joint point 或是通過邏輯關係組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
Advice(增強):Advice 定義了在 Pointcut 裏面定義的程序點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之後還是代替執行的代碼。(共有五種:before,after (無論是否發生異常,都進行執行的通知),around ,afterRunning,afterThrowing)
Target(目標對象):織入 Advice 的目標對象.。
Weaving(織入):將 Aspect 和其他對象連接起來, 並創建 Adviced object 的過程。
舉例
需要增強的類

package aop;
 import org.springframework.stereotype.Component;
 @Component("knight")
 public class BraveKnight {
     public void saying(String str,User user) throws Exception{ 
         System.out.println("我是騎士..(切點方法)");
         //throw new Exception();
     }
 }

切面類

 package aop;
 
 import java.util.Arrays;
 /**
  * 註解方式聲明aop
  * 1.用@Aspect註解將類聲明爲切面(如果用@Component("")註解註釋爲一個bean對象,那麼就要在spring配置文件中開啓註解掃描,<context:component-scan base-package="com.cjh.aop2"/>
  *      否則要在spring配置文件中聲明一個bean對象)
  * 2.在切面需要實現相應方法的前面加上相應的註釋,也就是通知類型。
  * 3.此處有環繞通知,環繞通知方法一定要有ProceedingJoinPoint類型的參數傳入,然後執行對應的proceed()方法,環繞才能實現。
  */
 @Component("annotationTest")
 @Aspect
 public class AnnotationTest {
     //定義切點
     @Pointcut("execution(* *.saying(..))")
     public void sayings(){}
     /**
      * 前置通知(註解中的sayings()方法,其實就是上面定義pointcut切點註解所修飾的方法名,那只是個代理對象,不需要寫具體方法,
      * 相當於xml聲明切面的id名,如下,相當於id="embark",用於供其他通知類型引用)
      * <aop:config>
         <aop:aspect ref="mistrel">
             <!-- 定義切點 -->
             <aop:pointcut expression="execution(* *.saying(..))" id="embark"/>
             <!-- 聲明前置通知 (在切點方法被執行前調用) -->
             <aop:before method="beforSay" pointcut-ref="embark"/>
             <!-- 聲明後置通知 (在切點方法被執行後調用) -->
             <aop:after method="afterSay" pointcut-ref="embark"/>
         </aop:aspect>
        </aop:config>
      */
     @Before("sayings()")
     public void sayHello(){
         System.out.println("註解類型前置通知");
     }
     //後置通知
     @After("sayings()")
     public void sayGoodbey(){
         System.out.println("註解類型後置通知");
     }
     //環繞通知。注意要有ProceedingJoinPoint參數傳入。
     @Around("sayings()")
     public void sayAround(ProceedingJoinPoint joinPoint) throws Throwable{
         System.out.println("註解類型環繞通知..環繞前");
         Object[] args = joinPoint.getArgs();
         Signature signature = joinPoint.getSignature();
         MethodSignature methodSignature = (MethodSignature) signature;
         //2.最關鍵的一步:通過這獲取到方法的所有參數名稱的字符串數組
         String[] parameterNames = methodSignature.getParameterNames();
         System.out.println("parameterNames"+Arrays.toString(parameterNames));
         joinPoint.proceed();//執行方法
         System.out.println("註解類型環繞通知..環繞後");
     }
 }

測試類

package aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 
 * @author Caijh
 * email:[email protected]
 * 2017年7月11日 下午6:27:06
 */
public class Test {
    public static void main(String[] args) throws Exception {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        BraveKnight br = (BraveKnight) ac.getBean("knight");
        User user = new User();
        user.setAge(24);
        br.saying("zhangfei",user);
    }
}

配置文件

<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!-- 開啓註解掃描 -->
    <context:component-scan base-package="aop"/>
    <!-- 開啓aop註解方式,此步驟s不能少,這樣java類中的aop註解纔會生效 -->
    <aop:aspectj-autoproxy/>
</beans>

@Pointcut註解詳解:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括號中各個pattern分別表示:

修飾符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern)可以爲表示任何返回值,全路徑的類名等
類路徑匹配(declaring-type-pattern?)
方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“
”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(*,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(…)表示零個或多個任意參數
異常類型匹配(throws-pattern?)
其中後面跟着“?”的是可選項

1)execution(* *(..))  
//表示匹配所有方法  
2)execution(public * com. savage.service.UserService.*(..))  
//表示匹配com.savage.server.UserService中所有的公有方法  
3)execution(* com.savage.server..*.*(..))  
//表示匹配com.savage.server包及其子包下的所有方法 

拓展一些沒用的
Spring AOP採用的是動態代理,在運行期間對業務方法進行增強,所以不會生成新類,對於動態代理技術,Spring AOP提供了對JDK動態代理的支持以及CGLib的支持。
JDK動態代理只能爲接口創建動態代理實例,而不能對類創建動態代理。需要獲得被目標類的接口信息(應用Java的反射技術),生成一個實現了代理接口的動態代理類(字節碼),再通過反射機制獲得動態代理類的構造函數,利用構造函數生成動態代理類的實例對象,在調用具體方法前調用invokeHandler方法來處理。
採用Cglib動態代理可以對沒有實現接口的類產生代理,實際上是生成了目標類的子類來增強

重載與重寫
重載:方法名相同, 參數列表不同的方法之間構成重載; 參數列表不同又包括參數數量, 參數類型, 參數順序的不同.重載的判斷只有這兩條, 與方法的修飾符, 返回值類型都無關,修飾符返回值類型不同也叫重載
重寫: 1. 三同一大: 返回值類型, 方法名, 參數列表必須與父類相同(返回值類型在java1.5中允許協變返回類型, 就是重寫之後的方法的返回值可以是重寫之前方法的子類); 一大是指訪問權限大於等於父類的訪問權限
    2. 子類方法只能拋出比父類方法更小的異常或者不拋出異常
    3. 被重寫的方法不能有final,private, static修飾符. 因爲final不允許被子類繼承; 而private方法隱含是final類型, 且只能在類中被訪問, 子類是無權訪問的. 子類中只能定義一個與父類完全相同的方法, 而不能稱之爲重寫;

spring事務實現原理:動態代理
對於純JDBC操作數據庫,想要用到事務,可以按照以下步驟進行:

1獲取連接 Connection con = DriverManager.getConnection()
2開啓事務con.setAutoCommit(true/false);
3執行CRUD
4提交事務/回滾事務 con.commit() / con.rollback();
5關閉連接 conn.close();

使用Spring的事務管理功能後,我們可以不再寫步驟 2 和 4 的代碼,而是由Spirng 自動完成。 那麼Spring是如何在我們書寫的 CRUD 之前和之後開啓事務和關閉事務的呢?解決這個問題,也就可以從整體上理解Spring的事務管理實現原理了。下面簡單地介紹下,註解方式爲例子
配置文件開啓註解驅動,在相關的類和方法上通過註解@Transactional標識。
spring 在啓動的時候會去解析生成相關的bean,這時候會查看擁有相關注解的類和方法,並且爲這些類和方法生成代理,並根據@Transaction的相關參數進行相關配置注入,這樣就在代理中爲我們把相關的事務處理掉了(開啓正常提交事務,異常回滾事務)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章