面向切面編程AOP

看到知乎有個關於AOP的解說,很精彩,特意轉過來記錄分享下:
轉自:https://www.zhihu.com/question/24863332
關於AOP的也可以看看這個問題的其他回答

作者:Javdroider Hong
鏈接:https://www.zhihu.com/question/24863332/answer/350410712
來源:知乎
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

看到一高贊回答有Bug,所以還是簡單說幾句吧。

先上結論:AOP不一定都像Spring AOP那樣,是在運行時生成代理對象來織入的,還可以在編譯期、類加載期織入,比如AspectJ。

下面再慢慢聊AOP。
什麼時候要用到面向切面AOP呢?
舉個例子,你想給你的網站加上鑑權,
對某些url,你認爲不需要鑑權就可以訪問,
對於某些url,你認爲需要有特定權限的用戶才能訪問
如果你依然使用OOP,面向對象,
那你只能在那些url對應的Controller代碼裏面,一個一個寫上鑑權的代碼
而如果你使用了AOP呢?那就像使用Spring Security進行安全管理一樣簡單(更新:Spring Security的攔截是基於Servlet的Filter的,不是aop,不過兩者在使用方式上類似):

protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
           .antMatchers("/static","/register").permitAll()
           .antMatchers("/user/**").hasRoles("USER", "ADMIN") 

這樣的做法,對原有代碼毫無入侵性,這就是AOP的好處了,把和主業務無關的事情,放到代碼外面去做。

所以當你下次發現某一行代碼經常在你的Controller裏出現,比如方法入口日誌打印,那就要考慮使用AOP來精簡你的代碼了。

聊完了AOP是啥,現在再來聊聊實現原理。AOP像OOP一樣,只是一種編程範式,AOP並沒有規定說,實現AOP協議的代碼,要用什麼方式去實現。

比如上面的鑑權的例子,假設我要給UserController的saveUser()方法加入鑑權,第一種方式,我可以採用代理模式,
什麼是代理模式,就是我再生成一個代理類,去代理UserController的saveUser()方法,代碼大概就長這樣:

class UserControllerProxy {
    private UserController userController;

    public void saveUser() {
        checkAuth();
        userController.saveUser();
    }
}

這樣在實際調用saveUser()時,我調用的是代理對象的saveUser()方法,從而實現了鑑權。

代理分爲靜態代理和動態代理,靜態代理,顧名思義,就是你自己寫代理對象,動態代理,則是在運行期,生成一個代理對象。

Spring AOP就是基於動態代理的,如果要代理的對象,實現了某個接口,那麼Spring AOP會使用JDK Proxy,去創建代理對象,而對於沒有實現接口的對象,就無法使用JDK Proxy去進行代理了(爲啥?你寫一個JDK Proxy的demo就知道了),這時候Spring AOP會使用Cglib,生成一個被代理對象的子類,來作爲代理,放一張圖出來就明白了:
這裏寫圖片描述
好,上面講的是AOP的第一種實現,運行時織入。

但是不是所有AOP的實現都是在運行時進行織入的,因爲這樣效率太低了,而且只能針對方法進行AOP,無法針對構造函數、字段進行AOP。

我完全可以在編譯成class時就織入啊,比如AspectJ,當然AspectJ還提供了後編譯器織入和類加載期織入,這裏我就不展開討論了,我只是來澄清一下大家對AOP的誤解,
有興趣繼續學習的,可以看一下這篇博客:
Comparing Spring AOP and AspectJ

嗯,最後,個人公衆號Bridge4You已經開通,歡迎關注!有些話只能在那裏跟你說 (〃’▽’〃)http://weixin.qq.com/r/LSng_EvEvRbCrQ_b93w- (二維碼自動識別)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章