Spring學習記錄(五)——初步體驗AOP

2018.4.16

僅爲個人理解 不足之處歡迎指正~

什麼是AOP?

以下部分引例參考KenWang的博客~

AOP(Aspect Oriented Programming)——面向切面編程

AOP與IoC是Spring的兩大核心思想之一

同樣也是對傳統的OOP(Object Oriented Programming,面向對象編程)的一種補充

在OOP中 通過繼承 多態 封裝等概念可以模仿現實生活中的各種縱向關係

但OOP並不適用於模仿橫向關係

什麼是橫向關係?

想象幾個實際生活中的例子:

1.在操作ATM時我們可能進行如下操作:

  •    取款: 開始——輸入密碼——取款——結束
  •    轉賬: 開始——輸入密碼——轉賬——結束
  •    餘額: 開始——輸入密碼——查詢——結束

在這個例子中我們可以看到 如果我們將 取款 轉賬 查詢餘額 作爲核心操作

那麼輸入密碼這一操作就是被核心操作所調用的“輔助操作

這一形容並不是說輔助操作的重要性低 而是相對於各有所專的核心操作而言

輔助操作顯得重複且無關

2.在一個購物系統中 可能需要進行以下操作:

  • 對生成的每一筆訂單提供事務管理
  • 當用戶點擊、搜索某關鍵字時 我們想要統計這類商品的點擊率
  • 當用戶登錄、退出、修改密碼時 我們需要記錄進行操作的地點與時間

這個例子中的三項操作 均可以看作是核心業務功能的“輔助功能

這與 “切面” “橫向關係” 又怎麼理解?



我們可以看到 在ATM例子中 我們將取款 轉賬 查詢餘額三個互不相關的業務畫作三條直線

而這三個業務都需要 輸入密碼 這一輔助功能的支持

而這條切入於三條黑線的紅線 就是所謂的“切面

紅線與黑線的交點 可以稱爲“切點

同時 也建立起了“橫向關係

在面向切面編程AOP的思想中

讓核心業務功能和切面功能分別獨立開發

然後再將這兩者編織在一起 這就叫做AOP



爲什麼要使用AOP?

與Spring的另一核心思想IoC一樣 AOP同樣致力於使代碼更加簡潔明瞭 讓我們將心思更多的花在覈心代碼的構建中

假如在ATM例子中

取款、轉賬、查詢餘額爲三個不同的服務

現在這三個服務都需要“驗證身份”這一功能的支持

那麼該怎麼做?

或許你會想到 在這三段代碼中都加入一段驗證身份的代碼

或許你會想到 寫一個單獨的函數 然後在三個服務開始時調用它

但是這些方法都會產生代碼的重複及耦合

總之:

AOP可以使業務模塊更簡潔 並解決業務邏輯與輔助模塊的耦合度問題


開始使用AOP

額外添加的包:


其他Spring需要包與之前內容相同


我們使用這樣一個例子:

現在有一個登錄服務 我們需要在登錄的前後打印系統時間

我們將登錄服務作爲核心業務 將打印時間作爲輔助業務

首先構建項目結構:


在aspect包中編寫Timeprint類:

package com.tzy.aspect;

public class Timeprint 
{
	public void printTime()
	{
		System.out.println("當前時間爲:"+System.currentTimeMillis());
	}
}

在serive包中編寫Login類:

package com.tzy.serive;

import java.util.Scanner;

public class Login 
{
	public void log()
	{
		Scanner sc=new Scanner(System.in);
		System.out.println("請輸入用戶名:");
		String username=sc.next();
		System.out.println(username+"  登錄成功!");
	}
}

編寫測試類:

package com.tzy.test;

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

import com.tzy.serive.Login;

public class testAOP 
{
	public static void main(String args[])
	{
		ApplicationContext context=new ClassPathXmlApplicationContext
				(new String[]{"applicationContext.xml"});
		Login user1=(Login)context.getBean("login");
		user1.log();
	}
}

現在配置applicationContext:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
   <context:annotation-config/>
      
   <bean name="login" class="com.tzy.serive.Login" >
   </bean>
    <bean id="Timeprint" class="com.tzy.aspect.Timeprint">
   </bean>
   
   <aop:config>
   		<aop:aspect id="time" ref="Timeprint">
   				<aop:pointcut id="test1" 
   				expression="execution(* com.tzy.serive.Login.*(..))"/>
   				<aop:before method="printTime" pointcut-ref="test1"/>
   				<aop:after method="printTime" pointcut-ref="test1"/>
   		</aop:aspect>
   </aop:config>
	
  </beans>

運行:



接下來解釋一下配置文件中的部分問題

1.bean類的裝配請參考前面的文章


2.<aop:aspect>


當切面代碼是自動注入的bean時 使用ref屬性可直接使用

在上面我們可以看到對Timerprint類的注入:



3.<aop:pointcut>與execution

pointcut表示建立一個切點

execution則比較複雜

在這裏先用execution(* *.*(..))代替

第一個出現的*表示:對任意的返回類型方法進行匹配 可以改爲void int double之類以匹配特定返回類型函數

第二個出現的*表示:全類名

第三個出現的*表示:對任意的方法名進行匹配

最後出現的(..)表示:匹配任意類型、數量的參數 


4.<aop before>及其他

何時執行切面代碼是需要我們進行配置的 比如在覈心業務調用之前或者之後

一共有如下幾種類型:


  • after 在方法完成之後調用通知 無論其是否執行成功
  • after-returning 在方法成功執行之後調用
  • after-throwing 在方法拋出異常之後調用
  • around 在之前與之後調用
  • before 在方法調用之前調用通知

5.method


如圖 我們在之前的測試中均調用的是printTime方法 這是切面類中的某個方法

現在我們在切面類中加上一個方法:


此時修改method爲:


運行測試類:


可以看到調用的通知函數不同


總結:

以上就是AOP的簡單用法

以後會補充使用註解進行AOP的操作

另外需要注意的是Spring只支持方法連接點 只有在調用某個方法時 才能構建切入點


謝謝~

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