一.SpringAOP的概述。java
AOP(Aspect Oriented Programming),面向切面編程,經過預編譯方式和運行期間動態代理實現程序的功能的統一維護的技術。AOP是OOP(面向對象編程)的擴展和延伸。舉個例子,讓你們對AOP印象更加深入點。編程
好比權限校驗。實際開發中,咱們知道不是全部人均可以進行增刪改查的操做,只有管理員才能夠,因此咱們須要在進行增刪改查以前都須要進行權限校驗。傳統縱向繼承(面向對象的特徵)就是定義一個BaseDao,裏面含有一個權限校驗的方法,讓Dao直接繼承ide
BaseDao,這樣就能夠直接調用Dao父類中的權限校驗方法。而AOP則採用的是橫向抽取機制替代了傳統的縱向繼承,也就是經過生成代理的方式來解決。這就是對方法進行擴展的兩個方法的思想。性能
AOP應用:權限校驗,日誌記錄,性能監控(運用代理,分別在代碼先後插入記錄時間),事務控制this
二.Spring底層實現AOP的原理。spa
底層用到了兩種代理機制:代理
JDK動態代理:針對實現了接口的類的代理(java基礎的代理)。日誌
Cglib的動態代理:針對沒有實現接口的類產生的代理,底層用的是字節碼加強技術,經過生成當前類的子對象來產生代理。code
被代理類對象
public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("用戶增長...."); } @Override public void remove() { System.out.println("用戶移除...."); } }
JDK動態代理:
public class MyJDKProxy implements InvocationHandler { // 定義屬性,傳入實現類對象,也能夠不用,可是後面就要用類名,而不是引用 private UserDaoImpl userDao; public MyJDKProxy(UserDaoImpl userDao) { this.userDao = userDao; } // 建立UserDao動態代理 public UserDao createUserProxy() { /* * 第一個參數。告訴虛擬機用哪一個字節碼加載器加載內存建立字節碼文件。 * 第二個參數。告訴虛擬機內存中建立的字節碼文件中應該有哪一個方法(這些方法方法體爲空)。獲取類的接口(類的方法可能增長,但接口的方法是固定的) * 第三個參數。告訴虛擬機底層正在建立的字節碼上的各個方法如何處理。 * */ UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(MyJDKProxy.class.getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equalsIgnoreCase("insert")) { System.out.println("權限校驗"); } // 執行被代理類原有的方法 return method.invoke(userDao, args); } }
運行結構
權限校驗
用戶增長....
Cglib的父類:
public class CustDao { public void remove() { System.out.println("顧客移除"); } }
Cglib動態代理:
public class MyCglibProxy { public CustDao createProxy() { // 建立cglib和心類 Enhancer enhancer = new Enhancer(); // 設置父類 enhancer.setSuperclass(CustDao.class); // 設置回調 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("remove".equalsIgnoreCase(method.getName())) { // 執行被代理父類的方法 Object obj = methodProxy.invokeSuper(proxy, args); System.out.println("日誌記錄"); return obj; } return methodProxy.invokeSuper(proxy, args); } }); // 生成代理 CustDao custDao = (CustDao) enhancer.create(); return custDao; } }
運行結果:
權限校驗
用戶增長....
顧客移除
日誌記錄