要談AOP,那麼AOP究竟是什麼呢?AOP即面向切面編程,相比OOP--面向對象編程,因爲面向對象中最基本的單位是類,實例
,很天然咱們會想到AOP中最基本的單位可能就是所謂的切面
了,你可能會問,那切面
又是個什麼東西,我想說,如今不懂不要緊,下面我會講到。咱們先來看一段Spring
中關於AOP的定義:spring
面向切面——Spring提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。express
上面談到,AOP能夠分離系統的業務邏輯和系統服務(日誌,安全等),這個功能我想是不難明白(原理是使用了代理模式
),但關鍵是爲何要將這兩種進行分離呢?或者說這樣作有什麼好處?apache
在平常的軟件開發中,拿日誌來講,一個系統軟件的開發都是必須進行日誌記錄的,否則萬一系統出現什麼bug,你都不知道是哪裏出了問題。舉個小栗子,當你開發一個登錄功能,你可能須要在用戶登錄先後進行權限校驗並將校驗信息(用戶名
,密碼
,請求登錄時間,ip地址
等)記錄在日誌文件中,當用戶登陸進來以後,當他訪問某個其餘功能時,也須要進行合法性校驗。想一想看,當系統很是地龐大,系統中專門進行權限驗證的代碼是很是多的,並且很是地散亂,咱們就想能不能將這些權限校驗、日誌記錄等非業務邏輯功能的部分獨立拆分開,而且在系統運行時須要的地方(鏈接點
)進行動態插入運行,不須要的時候就不理,所以AOP是可以解決這種情況的思想吧!編程
下圖就很直觀地展現這個過程:緩存
不得不說,AOP的概念是真的多且難以理解,不過不用擔憂,聰明的你已經準備好打敗它們了。安全
通知有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框架同樣,在運行時完成織入。
首先AOP思想的實現通常都是基於代理模式,在JAVA中通常採用JDK動態代理模式,可是咱們都知道,JDK動態代理模式只能代理接口,若是要代理類那麼就不行了。所以,Spring AOP 會這樣子來進行切換,由於Spring AOP 同時支持 CGLIB、ASPECTJ、JDK動態代理,當你的真實對象有實現接口時,Spring AOP會默認採用JDK動態代理,不然採用cglib代理。
話說餅乾能解渴嗎?
鏈接點
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>
複製代碼
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文件,不推薦咱們這樣作,爲何?我以爲當你係統大了以後,那系統中將會有不少的bean
、xml
文件,這不容易維護和管理,我建議採用基於註解的方式進行AOP編程會更好!
好了,原本想結束的,可是在關掉eclipse前發現了一個小問題,既然切點是須要執行的方法,那麼咱們如何獲取鏈接點的數據(或者說是校驗參數呢?),這裏推薦一篇博文,我就不寫啦!小夥伴們,一塊兒加油吧!