標籤:aop spring turn 思考 back int -- 地方 實現
前言:
我在工作中遇到一種情況:有些程式員在面試的時候對知識點掌握的很優秀,而寫代碼卻很吃力,他們往往側重於知識本身,對應用略有不足,就像空有一把利劍,卻不會使用一樣,於是乎就想寫一些不一樣的文章,內容可能很白,重點是在整個講解過程中的切入角度以及思考方式。
本文和別的文章有所不同,不從底層次來分析動態代理,僅僅為了程式員在工作中使用。
本文:
先寫一個簡單的案例,引出來動態代理。
假使我們項目中有對使用者刪除的業務處理,放在業務層中(盡量做到面向介面編程,測試類別很有必要貼出來)
原始代碼:定義介面、業務類、測試類別
/** * 使用者業務介面 */public interface UserService{ // 刪除使用者 public void delUser(String id);}/** * 使用者業務實作類別 */public class UserServiceImpl implements UserService { @Override public void delUser(String id) { System.out.println("這是刪除使用者" + id + "的操作..."); }}/** * 測試類別 */public class Client { public static void main(String[] args) { UserService service = new UserServiceImpl(); service.delUser("10002"); }}
如果在像增、刪、改這些操作前/後做一些別的工作,比如記錄日誌,同步資料到別的系統等。
我們往往不會把這些非代碼寫到業務類中,有很多方式可以實現,這裡採用代理的方式(先從最簡單的靜態代理開始):
完善代碼1:添加代理類,修改測試類別
/** * 使用者業務代理類 */public class UserServiceProxy implements UserService { public UserService userService; public UserServiceProxy(UserService userService){ this.userService = userService; } @Override public void delUser(String id) { System.out.println("記錄日誌"); userService.delUser(id); }}/** * 測試類別 */public class Client { public static void main(String[] args) { UserService service = new UserServiceProxy(new UserServiceImpl()); service.delUser("10002"); }}
1.如果只有一個類要記錄日誌還好,如果有幾百個類,像上面這種寫法我們需要寫幾百個代理類,工作量是一方面,類太多而且功能一樣也不利於閱讀、部署和程式運行(類數量膨脹)。
2.同時上面這種寫法在以後擴充也很麻煩,將來又要添加別的操作呢?還要修改很多個類(拓展性差),那麼使用JDK的靜態代理試試:
完善代碼2:修改代理類,修改測試類別
/** * 業務代理類 */public class ServiceProxy implements InvocationHandler { private Object obj; public ServiceProxy(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("記錄日誌"); return method.invoke(obj, args); }}/** * 測試類別 */public class Client { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserService service = (UserService)Proxy.newProxyInstance( Client.class.getClassLoader(), userService.getClass().getInterfaces(), new ServiceProxy(userService)); service.delUser("10002"); }}
注意看業務代理類,裡面沒有一個和業務有關的對象,實現了低耦合的目標。
JDK的動態代理既實現了需求同時又很好的解決了類數量膨脹和可拓展性差的問題。即使有再多的類似需求,我們只需要修改一個代理類。
當然JDK動態代理也有不足的地方,從Proxy.newProxyInstance()的參數裡不難看出來,它需要被代理的類必須實現某個介面,當然在開發中我們可以解決,比如提供一個什麼都不做的介面,但這樣做顯然不是一個好的主意,特別是在開發架構的時候,我們應該儘可能少的去限制使用者。
於是就有了不實現介面就能實現代理的技術,在這裡我只列舉一種簡單的:cglib
擴充demo:無介面類用cglib實現動態代理
/** * 使用者業務類 */public class UserServiceImpl { // 刪除使用者 public void delUser(String id) { System.out.println("這是刪除使用者" + id + "的操作..."); }}/** * 業務代理類 */public class CglibProxy implements MethodInterceptor { public Object createProxy(Class<?> clazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("記錄日誌"); return methodProxy.invokeSuper(proxy, args); }}/** * 測試類別 */public class Client { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); UserServiceImpl service = (UserServiceImpl)proxy.createProxy(UserServiceImpl.class); service.delUser("10002"); }}
好了,一個工作中的問題讓我們完美的解決了(相對完美),不妨從頭再捋一遍:
1.寫了一個實現業務的類
2.增加大量通用非業務操作,為了代碼的高內聚低耦合,模組化開發將非業務通用代碼抽離
3.使用靜態代理的時候發現工作量很大,同時考慮程式的可擴充性改用JDK動態代理
4.JDK動態代理很優秀的完成了要求,同時發現JDK動態代理需要被代理類實現介面的局限性,使用第三方動態代理技術突破JDK動態代理的局限性
-------------以下內容可以忽略不看--------------------
JDK動態代理底層用的是反射,cglib用的是位元組碼產生技術,如果再往下追能追到動態綁定、位元組碼來源、父類引用指向子類對象、JVM的記憶體結構、Java編譯器、類載入器等等,如果再往上追你就知道Spring的AOP實現用的就是JDK動態代理和cglib,因為知識本身是一個網狀結構,找到一個點,就能拉出N多個點,也許這個點是以另一個點為基礎,而某些點又是以此為基礎。但是知識卻又是分層的。比如我會靈活運用代理技術,同時知道各種代理的優缺點,效能上的好壞就足以完成工作,而沒有必要非要去研究這個的底層那個的底層,就像廚房的廚師會用冰箱就行了,沒必要非要瞭解冰箱的原理一樣。
說了這麼多想表達什麼呢?第一在我年輕的時候,因為這個走了很多彎路,總是想著去瞭解底層,才能更好的應用,就投入大量的精力去研究,結果當我這顆小樹苗自以為把根紮的足夠深抬頭準備生長的時候才發現,同期的小樹苗,已經長成參天大樹,他們沒我紮根紮的深,卻享受著最多的陽光,瘋狂的生長,瘋狂的紮根,很快就超過了還在林隙間尋覓陽光我。
還好我明白的早,受用終身。GN:)
Java動態代理淺度分析