天天看點

Spring AOP(一)-AOP的底層實作原理

一、AOP的概念和原理

AOP(Aspect-Oriented Programming):面向切面程式設計,是一種新的方法論,切面能夠幫助我們子產品化橫切關注點,簡言之,橫切關注點可以被描述為影像應用多出的功能,列入安全就是一個橫切關注點,應用中的許多方法都涉及到安全規則,如下圖所示:

Spring AOP(一)-AOP的底層實作原理

對傳統OOP(Ojected-Oriented Programming)的補充和完善.

切面的優點:

• 每個關注點度集中在一個地方,而不是分散到多處代碼中

• 服務子產品更簡潔,因為他們隻包含主要關注點,(或者核心功能)的代碼,而次要關注點的代碼被轉移到切面中了.

• 實作了業務邏輯和系統級的服務進行隔離,使得業務邏輯和系統級的服務耦合度降低 ,進而提高系統的重用性和開發效率.

名詞解釋:

業務邏輯: 某個類的某個方法本身要實作的功能。

系統級的服務: 系統的日志、事務、權限驗證。

二、如何實作AOP(底層實作原理)

每個動态代理類度必須實作,InvocationHandler這個接口,并且每個代理類的實列都關聯到了一個handler當我們通過一個代理類調用一個方法的時候,我們方法的調用就會被轉發為由InvocationHandler這個接口的invoke方法來調用,invoke是唯一的唯一的方法.

主要有兩種方法:jdk的動态代理和cglib的動态代理

1.  jdk動态代理(利用java的反射思想)

JDK的動态代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。其中InvocationHandler是一個接口,可以通過實作該接口定義橫切邏輯,在并通過反射機制調用目标類的代碼,動态将橫切邏輯和業務邏輯編織在一起。

首先編寫IUserDao接口編寫兩個方法

package com.dqsy.spring.proxy;

public interface IUserDao {
	
	public void addUser();
	public void serchUser();
}
           

實作這個接口的方法

package com.dqsy.spring.proxy.impl;

import com.dqsy.spring.proxy.IUserDao;

public class IUserDaoImpl implements IUserDao {

	@Override
	public void addUser() {
		System.out.println("添加方法....");
	}
	@Override
	public void serchUser() {
		// TODO Auto-generated method stub
		System.out.println("尋找方法....");
	}

}
           

建立測試類

package com.dqsy.spring.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.junit.Test;

import com.dqsy.spring.proxy.IUserDao;
import com.dqsy.spring.proxy.impl.IUserDaoImpl;

public class AopTest {
	/**
	 * 正常的調用
	 */
	@Test
	public void test(){
		IUserDao userDao = new IUserDaoImpl();
		userDao.serchUser();
	}
	/**
	 * JDk動态代理方法
	 */
	@Test
	public void proxytest(){
		//實列化目标對象
		IUserDao target = new IUserDaoImpl();
		IUserDao proxy = ((IUserDao) Proxy.newProxyInstance(
				//目标類ClassLoad定義了有那個ClassLoad對象生成代理進行加載
				target.getClass().getClassLoader(),
				//目标類的接口,一個interface接口,表示我們将為我們的代理對象提供一組接口,如果我們嗎提供一組接口給它
				//那麼代理就宣稱實作該接口(多态)這樣我們就調用該接口中的方法了
				target.getClass().getInterfaces(),
				//匿名内部類
				new InvocationHandler(){
					/**
					 * proxy:指我們要代理的那個真實對象
					 * method:指我們要調用真是對象的方法的method對象
					 * params:指我們要調用真是方法的參數
					 */
					@Override
					public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
						// TODO Auto-generated method stub
						System.out.println("添加方法...");
						System.out.println(""+method.getName());
						Object result = method.invoke(target, params);
						System.out.println("編寫日志...");
						return result;
					}
					
				}));
		proxy.serchUser();
		
	}
}
           

結果如下:

Spring AOP(一)-AOP的底層實作原理

那麼另一個方法結果又是怎樣呢?

Spring AOP(一)-AOP的底層實作原理

以上方法就是在沒有用spring的知識,而實作aop思想的.

2.cglib動态代理

 使用JDK建立代理有一個限制,即它隻能為接口建立代理,這一點我們從Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)就看得很清楚,第三個入參interfaces就是為代理執行個體指定的實作接口。雖然,面向接口的程式設計被很多很有影響力人(包括Rod Johnson)的推崇,但在實際開發中,開發者也遇到了很多困惑:難道對一個簡單業務表的操作真的需要建立5個類(領域對象類、Dao接口,Dao實作類,Service接口和Service實作類)嗎?對于這一問題,我們還是留待大家進一步讨論。現在的問題是:對于沒有通過接口定義業務方法的類,如何動态建立代理執行個體呢?JDK的代理技術顯然已經黔驢技窮,CGLib作為一個替代者,填補了這個空缺。

 CGLib采用非常底層的位元組碼技術,可以為一個類建立子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,并在攔截方法相應地織入橫切邏輯。

小結:

 Spring AOP在底層就是利用JDK動态代理或CGLib動态代理技術為目标Bean織入橫切邏輯。在這裡,我們對以上兩節動态建立代理對象做一個小結。

在PerformaceHandler和CglibProxy中,有三點值得注意的地方是:第一,目标類的所有方法都被添加了性能監視橫切的代碼,而有時,這并不是我們所期望的,我們可能隻希望對業務類中的某些方法織入橫切代碼;第二,我們手工指定了織入橫切代碼的織入點,即在目标類業務方法的開始和結束前調用;第三,我們手工編寫橫切代碼。以上三個問題,在AOP中占用重要的地位,因為Spring AOP的主要工作就是圍繞以上三點展開:Spring AOP通過Pointcut(切點)指定在哪些類的哪些方法上施加橫切邏輯,通過Advice(增強)描述橫切邏輯和方法的具體織入點(方法前、方法後、方法的兩端等),此外,Spring還通過Advisor(切面)組合Pointcut和Advice。有了Advisor的資訊,Spring就可以利用JDK或CGLib的動态代理技術為目标Bean建立織入切面的代理對象了。

JDK動态代理所建立的代理對象,在JDK 1.3下,性能強差人意。雖然在高版本的JDK中,動态代理對象的性能得到了很大的提高,但是有研究表明,CGLib所建立的動态代理對象的性能依舊比JDK的所建立的代理對象的性能高不少(大概10倍)。而CGLib在建立代理對象時性能卻比JDK動态代理慢很多(大概8倍),是以對于singleton的代理對象或者具有執行個體池的代理,因為不需要頻繁建立代理對象,是以比較适合用CGLib動态代理技術,反之适合用JDK動态代理技術。此外,由于CGLib采用生成子類的技術建立代理對象,是以不能對目标類中的final方法進行代理。