Spring的AOP的底層原理
Spring的AOP
AOP Aspect Oriented Programming 面向切面程式設計
通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP(面向物件程式設計)的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
AOP是OOP的擴充套件和延伸,是為了解決OOP開發遇到的問題
AOP底層實現原理
(只做瞭解即可,只需要知道如何通過配置生成代理物件)
動態代理
JDK動態代理 只能對實現了介面的類生成代理物件
Cglib動態代理(類似於javassist第三方代理技術[ hibernate中通過load方法獲取物件,預設是使用javassist技術產生的代理物件 ]) 對沒有實現介面的類產生代理物件,實質是生成子物件
使用Spring 的 AOP 技術,若使用的類實現了介面,則預設採用JDK動態代理,否則預設採用Cglib動態代理,底層會自動進行切換
JDK動態代理
//介面 public interface UserDao { public void save(); public void fimd(); public void delete(); public void update(); }
/** * 實現類 * @author SIHAI */ public class UserDaoImp implements UserDao { @Override public void save() { System.out.println("UserDaoImp-save..."); } @Override public void fimd() { System.out.println("UserDaoImp-find..."); } @Override public void delete() { System.out.println("UserDaoImp-delete..."); } @Override public void update() { System.out.println("UserDaoImp-update..."); } }
/** * 使用jdk動態代理產生代理物件 * @author SIHAI */ public class JDKProxy { //將要加強的類物件傳進來 public UserDao getProxyUserDao(UserDao dao) { return (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判斷是不是save方法 if("save".equals(method.getName())) { //增強 System.out.println("this place is checked the data ..."); method.invoke(dao, args);//增強完繼續執行原來的方法 } //如果不是,則直接執行這個方法,invoke中的引數表示 方法 是 dao的型別 中擁有 當前方法 所擁有的引數 args return method.invoke(dao, args); } }); } }
/** * 測試 * @author SIHAI */ public class Test1 { /** * 未進行動態代理 */ @Test public void train1() { UserDao dao = new UserDaoImp(); dao.save(); dao.fimd(); dao.update(); dao.delete(); } /** * 進行動態代理 */ @Test public void train2() { UserDao dao = new UserDaoImp(); UserDao daoProxy = new JDKProxy().getProxyUserDao(dao); daoProxy.save(); daoProxy.fimd(); daoProxy.update(); daoProxy.delete(); } }
實際開發中,一個專案會有許多許多的dao和dao的實現類,當需求發生改變,需要對原有功能進行加強時,傳統的方式是縱向繼承,但是這樣會要修改每一個dao和他們的實現類,當需求再次改變時,又會是一個巨大的工作量,而Spring的AOP技術為我們解決了這個問題,採用橫向抽取機制的方式,即動態代理
Cglib動態代理
Cglib 第三方開原始碼生成類庫,動態新增類的屬性和方法
是一個強大的,高效能,高質量的Code生成類庫,它可以在執行期擴充套件Java類與實現Java介面。Hibernate支援它來實現PO(Persistent Object 持久化物件)位元組碼的動態生成(現在不用這個了,現在用更強大的Javassist)
/** * * @author ysh * Customer.java * 2018年11月1日 下午12:28:05 * 被代理類,沒有實現介面 */ public class Customer { public void save() { System.out.println("saving...."); } public void delete() { System.out.println("deleting...."); } public void update() { System.out.println("updating...."); } public void find() { System.out.println("finding...."); } }
/** * * @author ysh * CglibProxy.java * 2018年11月1日 下午12:29:14 * cglib動態代理生成代理類物件 */ public class CglibProxy { private Customer customer; public CglibProxy(Customer customer) { this.customer = customer; } public Customer createProxy() { //建立Cglib的核心類物件 Enhancer enhancer = new Enhancer(); //設定父類 enhancer.setSuperclass(customer.getClass()); //設定回撥 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //檢視是否為save方法 if("save".equals(method.getName())) { //增強,比如,在save方法執行前,檢查客戶許可權 System.out.println("checking..."); //這裡必須要用invokeSuper方法,不能用invoke方法,因為後者會出現死迴圈???? return methodProxy.invokeSuper(proxy, args); } //否則讓原來的方法直接執行 /*proxy是被Customer的代理類物件,即為Customer的子類, invokeSuper方法,就是呼叫proxy的父類的當前方法, args用於精確找到要執行的方法,避免過載的影響*/ return methodProxy.invokeSuper(proxy, args); } }); return (Customer) enhancer.create(); } }
/** * * @author ysh * Test1.java * 2018年11月1日 下午12:30:22 * 測試類 */ public class Test1 { /** * 傳統方式 */ @Test public void train1() { Customer customer = new Customer(); customer.save(); customer.delete(); customer.find(); customer.update(); } /** * cglib動態代理 */ @Test public void train2() { CglibProxy cglibProxy = new CglibProxy(new Customer()); Customer cus = cglibProxy.createProxy(); cus.save(); cus.delete(); cus.find(); cus.update(); } }
測試結果