一、什麼是AOP
AOP = Aspect Oriental Programing,即面向切面編程。什麼概念,咱們看以下的圖片:
三個方法中,重複使用了代碼A和代碼B,典型的場景好比「開啓事務,數據處理,提交事務」。這些重複的代碼大可能是所謂的權限管理、日誌登錄、事務管理等必需卻又污染着業務邏輯代碼的內容,咱們天然但願將它們提取出來,還業務邏輯一個清新的世界。
你知道Servlet過濾器,可咱們目前對象的粒度已經不是整個方法了,而是更加細化到了方法中的代碼片斷。你固然能夠曲線救國地
使用匿名內部類來抽取重複代碼,可是它並不怎麼優雅,而AOP,就能夠經過橫向切割的方式來抽取代碼,達到咱們的目的。
二、Spring AOP
首先來看一下Spring AOP中一些核心組件的概念:
- 切面:封裝通用業務邏輯的組件,即咱們想要插入的代碼內容
- 切入點:指定哪些Bean組件的哪些方法使用切面組件
- 通知:用於指定具體做用的位置,是方法以前或以後等等
- 前置通知(before) - 在目標方法被調用以前調用通知功能
- 後置通知(after) - 在目標方法完成以後調用通知(不論程序是否出現異常),此時不會關心方法的輸出是什麼
- 返回通知(after-returning) - 在目標方法成功執行以後調用通知
- 異常通知(after-throwing) - 在目標方法拋出異常後調用通知
- 環繞通知(around) - 通知包裹了被通知的方法,在被通知的方法調用以前和調用以後執行自定義的行爲
那麼在Spring中使用AOP就意味着你須要:
- 目標程序,某個須要被插入通用代碼片斷的方法
- 切面程序,即通用代碼,用來插入方法的那些代碼片斷(無返回類型,參數類型與通知類型有關)
- 配置文件,用來指定切入點和通知
三、Demo示例和說明
下面就來看一個簡單的Spring AOP的Demo
3.1相關配置環境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
3.2 準備一個目標程序
編寫某個目標程序,並註冊爲Spring的Bean組件
public class Target {
public void print(String param) {
System.out.println("I'm a target function and my param is [" + param + "]" );
}
}
3.3 編寫切面程序
編寫切面程序,並註冊爲Spring的Bean組件
- 切面程序必須無返回值,即void
- org.aspectj.lang.JoinPoint 封裝了切面方法的參數和對象等,可經過它獲取相關內容
- 切面程序的參數除JoinPoint外,還與通知類型有關,如後置通知則能夠獲取目標程序返回值(須要配置,此處不展開)
public class Section {
public void writeLog(JoinPoint point) {
System.out.println("write logs start");
System.out.println("獲取目標函數的參數: " + Arrays.toString(point.getArgs()));
System.out.println("獲取目標函數的反射對象: " + point.getSignature());
System.out.println("獲取目標函數的所在對象: " + point.getTarget());
System.out.println("write logs end");
}
}
3.4 編寫AOP配置文件
配置文件applicationContext(註冊Bean和AOP配置)
<bean id="target" class="dulk.learn.aop.Target"/>
<bean id="section" class="dulk.learn.aop.Section"/>
<!-- AOP配置的根標籤,全部AOP配置都在其內部 -->
<aop:config>
<!-- 配置AOP的切入點,expression爲切入點表達式 -->
<aop:pointcut id="targetPointCut" expression="execution(* dulk.learn.aop.Target.print(*))" />
<!-- 配置切面,ref 切面對象 -->
<aop:aspect ref="section">
<!-- 配置通知爲前置,method爲方法,pointcut-ref做用在哪些切入點 -->
<aop:before method="writeLog" pointcut-ref="targetPointCut"/>
</aop:aspect>
</aop:config>
3.4.1 切入表達式
如上說明,則該demo中的 execution(* dulk.learn.aop.Target.print(*)) 表示 「Target類中的print方法(不論參數,即包括其重載)」
3.4.2 切面配置說明
<!-- 配置切面,ref 切面對象的beanId -->
<aop:aspect ref="section">
<!-- before表通知爲前置,method爲插入的方法,pointcut-ref做用在哪些切入點(aop:pointcut id) -->
<aop:before method="writeLog" pointcut-ref="targetPointCut"/>
</aop:aspect>
3.5 測試程序和結果
public class AOPTest {
@Test
public void testAOP(){
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
Target target = (Target) context.getBean("target");
target.print("balabala");
}
}
四、參考連接