一塊兒來談談 Spring AOP!

1、什麼是AOP?

要談AOP,那麼AOP究竟是什麼呢?AOP即面向切面編程,相比OOP--面向對象編程,因爲面向對象中最基本的單位是類,實例,很天然咱們會想到AOP中最基本的單位可能就是所謂的切面了,你可能會問,那切面又是個什麼東西,我想說,如今不懂不要緊,下面我會講到。咱們先來看一段Spring中關於AOP的定義:spring

面向切面——Spring提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。express

上面談到,AOP能夠分離系統的業務邏輯和系統服務(日誌,安全等),這個功能我想是不難明白(原理是使用了代理模式),但關鍵是爲何要將這兩種進行分離呢?或者說這樣作有什麼好處?apache

在平常的軟件開發中,拿日誌來講,一個系統軟件的開發都是必須進行日誌記錄的,否則萬一系統出現什麼bug,你都不知道是哪裏出了問題。舉個小栗子,當你開發一個登錄功能,你可能須要在用戶登錄先後進行權限校驗並將校驗信息(用戶名,密碼,請求登錄時間,ip地址等)記錄在日誌文件中,當用戶登陸進來以後,當他訪問某個其餘功能時,也須要進行合法性校驗。想一想看,當系統很是地龐大,系統中專門進行權限驗證的代碼是很是多的,並且很是地散亂,咱們就想能不能將這些權限校驗、日誌記錄等非業務邏輯功能的部分獨立拆分開,而且在系統運行時須要的地方(鏈接點)進行動態插入運行,不須要的時候就不理,所以AOP是可以解決這種情況的思想吧!編程

下圖就很直觀地展現這個過程:緩存

2、AOP中的基本概念

不得不說,AOP的概念是真的多且難以理解,不過不用擔憂,聰明的你已經準備好打敗它們了。安全

  • 通知(Adivce)

通知有5種類型:bash

  • Before 在方法被調用以前調用框架

  • After 在方法完成後調用通知,不管方法是否執行成功eclipse

  • After-returning 在方法成功執行以後調用通知maven

  • After-throwing 在方法拋出異常後調用通知

  • Around 通知了好、包含了被通知的方法,在被通知的方法調用以前後調用以後執行自定義的行爲

    咱們可能會問,那通知對應系統中的代碼是一個方法、對象、類、仍是接口什麼的呢?我想說一點,其實都不是,你能夠理解通知就是對應咱們平常生活中所說的通知,好比‘某某人,你2019年9月1號來學校報個到’,通知更多地體現一種告訴咱們(告訴系統何)什麼時候執行,規定一個時間,在系統運行中的某個時間點(好比拋異常啦!方法執行前啦!),並不是對應代碼中的方法!並不是對應代碼中的方法!並不是對應代碼中的方法!

  • 切點(Pointcut)

    哈哈,這個你可能就比較容易理解了,切點在Spring AOP中確實是對應系統中的方法。可是這個方法是定義在切面中的方法,通常和通知一塊兒使用,一塊兒組成了切面。

  • 鏈接點(Join point)

    好比:方法調用、方法執行、字段設置/獲取、異常處理執行、類初始化、甚至是 for 循環中的某個點

    理論上, 程序執行過程當中的任什麼時候點均可以做爲做爲織入點, 而全部這些執行時點都是 Joint point

    但 Spring AOP 目前僅支持方法執行 (method execution) 也能夠這樣理解,鏈接點就是你準備在系統中執行切點和切入通知的地方(通常是一個方法,一個字段)

  • 切面(Aspect)

    切面是切點和通知的集合,通常單獨做爲一個類。通知和切點共同定義了關於切面的所有內容,它是何時,在什麼時候和何處完成功能。

  • 引入(Introduction)

    引用容許咱們向現有的類添加新的方法或者屬性

  • 織入(Weaving)

    組裝方面來建立一個被通知對象。這能夠在編譯時完成(例如使用AspectJ編譯器),也能夠在運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入。

3、Spring中對AOP的支持

首先AOP思想的實現通常都是基於代理模式,在JAVA中通常採用JDK動態代理模式,可是咱們都知道,JDK動態代理模式只能代理接口,若是要代理類那麼就不行了。所以,Spring AOP 會這樣子來進行切換,由於Spring AOP 同時支持 CGLIB、ASPECTJ、JDK動態代理,當你的真實對象有實現接口時,Spring AOP會默認採用JDK動態代理,不然採用cglib代理。

  • 若是目標對象的實現類實現了接口,Spring AOP 將會採用 JDK 動態代理來生成 AOP 代理類;
  • 若是目標對象的實現類沒有實現接口,Spring AOP 將會採用 CGLIB 來生成 AOP 代理類——不過這個選擇過程對開發者徹底透明、開發者也無需關心。

4、說了這麼多,來點餅乾解解渴!直接上代碼!

話說餅乾能解渴嗎?

  • 定義主題接口,這些接口的方法能夠成爲咱們的鏈接點
package wokao666.club.aop.spring01;

public interface Subject {
	
	//登錄
	public void login();
	
	//下載
	public void download();
	
}
複製代碼
  • 定義實現類,這是代理模式中真正的被代理人(若是你有參與代購,這個就像你在代購中的角色)
package wokao666.club.aop.spring02;

import wokao666.club.aop.spring01.Subject;

public class SubjectImpl implements Subject {

	public void login() {
		
		System.err.println("借書中...");
		
	}

	public void download() {
		
		System.err.println("下載中...");
		
	}
}
複製代碼
  • 定義切面(切面中有切點和通知)
package wokao666.club.aop.spring01;

import org.aspectj.lang.JoinPoint;

public class PermissionVerification {
 /**
  * 權限校驗
  * @param args 登錄參數
  */
	public void canLogin() {
		
		//作一些登錄校驗
		System.err.println("我正在校驗啦!!!!");
		
	}
	
	/**
	 * 校驗以後作一些處理(不管是否成功都作處理)
	 * @param args 權限校驗參數
	 */
	public void saveMessage() {
		
		//作一些後置處理
			System.err.println("我正在處理啦!!!!");
	}
	
}
複製代碼
  • 再來看看咱們的SpringAOP.xml文件
<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
        
        <bean id="SubjectImpl1" class="wokao666.club.aop.spring02.SubjectImpl" />
        <bean id="SubjectImpl2" class="wokao666.club.aop.spring02.SubjectImpl" />
        <bean id="PermissionVerification" class="wokao666.club.aop.spring01.PermissionVerification" />
        
        <aop:config>
        <!-- 這是定義一個切面,切面是切點和通知的集合-->
            <aop:aspect id="do" ref="PermissionVerification">
            	<!-- 定義切點 ,後面是expression語言,表示包括該接口中定義的全部方法都會被執行-->
                <aop:pointcut id="point" expression="execution(* wokao666.club.aop.spring01.Subject.*(..))" />
                <!-- 定義通知 -->
                <aop:before method="canLogin" pointcut-ref="point" />
                <aop:after method="saveMessage" pointcut-ref="point" />
            </aop:aspect>
        </aop:config>
</beans>
複製代碼
  • 因爲我基於MAVEN構建,來看看咱們的pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>wokao666.club</groupId>
  <artifactId>aop</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>aop</name>
  <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.0.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
  </dependencies>
</project>
複製代碼
  • 最後看看咱們的測試類和結果
package wokao666.club.aop.spring01;

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

public class Test {
	
	public static void main(String[] args) {
		ApplicationContext ctx = 
	      new ClassPathXmlApplicationContext("SpringAOP.xml");
	  
				Subject subject1 = (Subject)ctx.getBean("SubjectImpl1");
				Subject subject2 = (Subject)ctx.getBean("SubjectImpl2");
				
				subject1.login();
				subject1.download();
				
				
				System.err.println("==================");
				
				
				
				
				subject1.login();
				subject1.download();
				
				
				
				
	}
}
複製代碼
三月 13, 2018 4:59:44 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Tue Mar 13 16:59:44 CST 2018]; root of context hierarchy
三月 13, 2018 4:59:45 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [SpringAOP.xml]
我正在校驗啦!!!!
借書中...
我正在處理啦!!!!
我正在校驗啦!!!!
下載中...
我正在處理啦!!!!
==================
我正在校驗啦!!!!
借書中...
我正在處理啦!!!!
我正在校驗啦!!!!
下載中...
我正在處理啦!!!!
複製代碼

我想,上面的實現方式只是皮毛而已啦!若是想要深刻學習,那麼能夠看下JDK動態代理的源碼分析(裏邊很是地巧妙,還運用了二級緩存,原本我想寫出來的,就留給下一篇文吧!)、Spring AOP源碼分析等等,固然上面的實現方式是基於老式的xml文件,不推薦咱們這樣作,爲何?我以爲當你係統大了以後,那系統中將會有不少的beanxml文件,這不容易維護和管理,我建議採用基於註解的方式進行AOP編程會更好!

好了,原本想結束的,可是在關掉eclipse前發現了一個小問題,既然切點是須要執行的方法,那麼咱們如何獲取鏈接點的數據(或者說是校驗參數呢?),這裏推薦一篇博文,我就不寫啦!小夥伴們,一塊兒加油吧!

spring AOP 通知參數的傳遞

相關文章
相關標籤/搜索