這里的代理與設計模式中的代理模式密切相關,代理模式的主要作用是為其他對象提供一種控制對這個對象的訪問方法,即在一個對象不適合或者不能直接引用另一個對象時,代理對象充當中介的作用。
現實生活中比較貼切的例子比如租房,被代理對象就是房東,代理對象就是中介,使用者就是租客,租客通過中介向房東租賃房屋,即使用者通過代理對象訪問被代理對象。
一、直接調用
interface Human{ public void say();} class Student implements Human{ @Override public void say() { System.out.println("I'm a Student"); }} public class ProxyTest { public static void main(String[] args) { Human human = new Student(); human.say(); }} //輸出//I'm a Student
二、靜態代理
實現靜態代理有以下三個步驟:
interface Human{ public void say();} class Student implements Human{ @Override public void say() { System.out.println("I'm a Student"); }} class StudentProxy implements Human{ private Student student; public StudentProxy(){} public StudentProxy(Student student){ this.student = student; } private void begin(){ System.out.println("Begin"); } private void end(){ System.out.println("End"); } @Override public void say() { begin(); student.say(); end(); }} public class ProxyTest { public static void main(String[] args) { Student student = new Student(); StudentProxy studentProxy = new StudentProxy(student); studentProxy.say(); }} //輸出//Begin//I'm a Student//End
在上述代碼中,我們在沒有修改類中say()方法的情況下,實現了在原來的say()方法前后分別執行()和()方法。由此引出代理模式的主要作用:
同時,靜態代理也存在一些比較致命的缺點。想象這樣一個場景:若新增一個類實現了Human接口,我們應該如何去代理這個類?比較容易想到的方法是擴大的代理范圍,然后將當作參數傳入,然后繼續使用類代理對象。這樣實現功能是沒有問題的,但會存在如下問題:
由此引出動態代理
三、動態代理
使用動態代理時,我們不需要編寫實現類,而是通過JDK提供的Proxy.()創建一個Human接口的對象。
生成動態代理有以下幾個步驟:
interface Human{ public void say();} class Student implements Human{ @Override public void say() { System.out.println("I'm a Student"); } @Override public void eat() { System.out.println("I eat something"); }} class MyInvocationHandler implements InvocationHandler { private Object object; public MyInvocationHandler(){} public MyInvocationHandler(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Begin"); Object invoke = method.invoke(object, args); System.out.println("End"); return invoke; }} public class ProxyTest { public static void main(String[] args) { MyInvocationHandler handler = new MyInvocationHandler(new Student()); Human human = (Human) Proxy.newProxyInstance( Human.class.getClassLoader(), new Class[] {Human.class}, handler); human.say(); human.eat(); }}
當Human接口的實例中方法增加時,如新增eat()方法時,只需要在類中直接實例化該方法即可。
當有其他不屬于Human類的子類需要被代理時,只需要將傳入()中的new ()替換為需要被代理的子類即可。
綜上所述,通過動態代理基本可以解決靜態代理的痛點。
四、中的動態代理
在項目中配置時,我們僅編寫了接口,并未編寫接口的實現類,那么當我們調用接口中方法時,是如何生成方法體的呢?
首先,項目在啟動時生成對象,通過.()方法獲取的代理對象
將上述過程與動態代理的步驟進行對比,我們最終獲取的是一個類似于動態代理例子中Human的代理對象,這里是的代理對象。至此,一個代理對象就生成完畢。
然后,當我們完成項目中的相關配置后動態庫鏈接靜態庫失敗,使用我們接口中的數據庫相關方法時,將調用之前生成的代理對象中()方法。類比動態代理的例子,即調用類中的()方法。
//83行代碼含義:如果method為Object中定義的方法(toString()、hash()...)則直接執行,這里我們要執行的是Mapper接口中定義的方法,顯然返回為falseObject.class.equals(method.getDeclaringClass())
于是執行()的()方法
進入()方法,我們看到之前我們配置的.xml在初始化時,被解析成了59行的。在此處通過對象實現了對數據庫的操作。
至此,我們對的數據庫操作流程已經有了大致了解。回到開頭的問題:為什么僅編寫了接口,并未編寫接口的實現類,仍然可以實現我們的功能?這與我們之前的動態代理例子有什么區別呢?
研究代碼我們發現,我們并沒有直接使用.()方法來調用實現類中的方法,而是調用了()的()方法解析我們配置的.xml,并通過實現了數據庫操作,這個()方法相當于自定義的方法。因此,這里的()方法具體執行的邏輯是根據.xml配置來生成的,這個.xml配置可以理解為接口的實現類。
如果本文對你有幫助,別忘記給我個3連 ,點贊動態庫鏈接靜態庫失敗,轉發,評論,
學習更多JAVA知識與技巧,關注與私信博主(555)