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

對傳統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思想的.
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方法進行代理。