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的相關參數進行相關配置注入,這樣就在代理中爲我們把相關的事務處理掉了(開啓正常提交事務,異常回滾事務)。