Java AOP與裝飾器模式

AOP與裝飾器模式簡(jiǎn)介

Aspect-Oriented Programming(AOP)面向切面編程,相對(duì)而言是Object-Oriented Programming(OOP)面向?qū)ο缶幊掏健<偃绗F(xiàn)在有一個(gè)特殊的需求,我們需要在每個(gè)方法進(jìn)入或退出時(shí)都打印日志案狠。我們不想在所有的方法上都傻傻地寫(xiě)上日志钱雷,想著這種相同的邏輯是否能用一個(gè)段代碼全部實(shí)現(xiàn)呢拉庵?這里我們就可以使用AOP了钞支,我們關(guān)注的是進(jìn)入方法和退出方法的那一個(gè)瞬間(那一個(gè)切面)烁挟。類似地撼嗓,這種需要統(tǒng)一處理的場(chǎng)景或是有著很多相同邏輯的地方我們都可以使用AOP來(lái)解決且警,比如日志斑芜、緩存杏头、鑒權(quán)大州。如果使用OOP解決上述問(wèn)題厦画,我們則會(huì)使用裝飾器模式根暑。

裝飾器模式

裝飾器模式(Decorator pattern)很簡(jiǎn)單排嫌,本質(zhì)上就是動(dòng)態(tài)地為?個(gè)對(duì)象增加功能淳地,但是不改變其結(jié)構(gòu)(用人話解釋就是加上個(gè)“包裝”)颇象,這里就不細(xì)說(shuō)了遣钳,我們實(shí)現(xiàn)一個(gè)帶日志和緩存的裝飾器模式來(lái)簡(jiǎn)單說(shuō)明蕴茴。一般來(lái)說(shuō)裝飾器模式都是和接口來(lái)混用的倦淀。每包一層裝飾器就實(shí)現(xiàn)一個(gè)額外的功能撞叽。帶日志和緩存的裝飾器模式有如下的邏輯:DataService是一個(gè)接口能扒;DataServiceImpl實(shí)現(xiàn)了DataService接口(是一個(gè)基本實(shí)現(xiàn))初斑;LogDecorator實(shí)現(xiàn)了DataService见秤,內(nèi)置成員變量DataServiceImpl鹃答,增加日志邏輯测摔,在合適的地方調(diào)用DataServiceImpl的基本實(shí)現(xiàn)方法锋八;CacheDecorator同理羞酗。

定義服務(wù)接口

public interface DataService {
    String getData();
}

服務(wù)的基礎(chǔ)實(shí)現(xiàn)

public class DataServiceImpl implements DataService {
    @Override
    public String getData() {
        return UUID.randomUUID().toString();
    }
}

加上日志裝飾器

public class LogDecorator implements DataService {
    private final DataServiceImpl dataService;

    public LogDecorator(DataServiceImpl dataService) {
        this.dataService = dataService;
    }

    @Override
    public String getData() {
        System.out.println("method invoke");
        final String data = dataService.getData();
        System.out.println("method finish");
        return data;
    }
}

加上緩存裝飾器

public class CacheDecorator implements DataService {
    private final LogDecorator logDecorator;
    private final Map<String, String> map;

    public CacheDecorator(LogDecorator logDecorator) {
        this.logDecorator = logDecorator;
        this.map = new HashMap<>();
    }

    @Override
    public String getData() {
        String value = map.get("getData");
        if (value == null) {
            value = logDecorator.getData();
            map.put("getData", value);
        }
        return value;
    }
}

裝飾器有個(gè)很大的問(wèn)題欺嗤,對(duì)于每一個(gè)方法剂府,我們都要手動(dòng)寫(xiě)一遍日志腺占,實(shí)在是太麻煩了铡羡,而且很啰嗦烦周。

AOP的兩種實(shí)現(xiàn):動(dòng)態(tài)代理

什么是動(dòng)態(tài)代理呢读慎?動(dòng)態(tài)指的是運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼夭委,代理指的是攔截掉原先的方法株灸,然后根據(jù)當(dāng)前情況完成相應(yīng)的功能逐抑〔薨保總的來(lái)說(shuō)腐巢,動(dòng)態(tài)代理就是動(dòng)態(tài)生成字節(jié)碼完成一些功能擴(kuò)展肉瓦。一般地泞莉,動(dòng)態(tài)代理可以分為JDK動(dòng)態(tài)代理和動(dòng)態(tài)字節(jié)碼增強(qiáng)鲫趁。

JDK動(dòng)態(tài)代理

使用Proxy.newProxyInstance進(jìn)行JDK動(dòng)態(tài)代理堡僻,注意第二的參數(shù)需要參數(shù)一個(gè) interface 的 Class 數(shù)組钉疫。第三個(gè)參數(shù)需要實(shí)現(xiàn)InvocationHandler這個(gè)接口牲阁,處理代理實(shí)例上的方法調(diào)用并返回結(jié)果备燃。

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
public class Main {
    public static void main(String[] args) {
        final DataService service = new DataServiceImpl();

        DataService service1 = (DataService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                new Class[]{DataService.class},
                new LogProxy(service));

        System.out.println(service1.getData());
    }
}
public class LogProxy implements InvocationHandler {
    private final DataService delegate;

    public LogProxy(DataService delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method invoke");
        final Object value = method.invoke(delegate);
        System.out.println("method finish");
        return value;
    }
}

JDK動(dòng)態(tài)代理的優(yōu)點(diǎn)是比較方便,不需要依賴任何第三方庫(kù)冀膝。缺點(diǎn)也比較明顯,功能比較受限赐纱,只適用于接口疙描。

動(dòng)態(tài)字節(jié)碼增強(qiáng)

如果要代理的是個(gè)類不是接口怎么辦?我們可以使用CGLIB或ByteBuddy字節(jié)碼?成效五。他們的本質(zhì)都是在內(nèi)存中生成一個(gè)動(dòng)態(tài)的增強(qiáng)的子類畏妖。他們非常的強(qiáng)大戒劫,但是缺點(diǎn)是需要引?額外的第三?類庫(kù),由于是通過(guò)繼承方式增強(qiáng)的疯攒,所以他們不能增強(qiáng)final類/final/private?法敬尺。關(guān)于ByteBuddy的使用署恍,我們?cè)?a href="http://www.reibang.com/p/f70138744b57" target="_blank">Java 注解這一篇有提到盯质。這里我們使用CGLIB來(lái)做例子呼巷。

public class Main {
    public static void main(String[] args) {
        final DataServiceImpl service = new DataServiceImpl();

        final Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DataServiceImpl.class);
        enhancer.setCallback(new LogInterceptor(service));

        final DataServiceImpl service1 = (DataServiceImpl) enhancer.create();
        System.out.println(service1.getData());
    }
}

這一部分邏輯和上邊JDK動(dòng)態(tài)代理中的LogProxy還是很像的。

public class LogInterceptor implements MethodInterceptor {
    private final DataServiceImpl delegate;

    public LogInterceptor(DataServiceImpl delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("method invoke");
        final Object value = method.invoke(delegate);
        System.out.println("method finish");
        return value;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末源譬,一起剝皮案震驚了整個(gè)濱河市踩娘,隨后出現(xiàn)的幾起案子养渴,更是在濱河造成了極大的恐慌厚脉,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泄伪,居然都是意外死亡蟋滴,警方通過(guò)查閱死者的電腦和手機(jī)津函,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)涩馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人涯雅,你說(shuō)我怎么就攤上這事斩芭』裕” “怎么了琴庵?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵咖杂,是天一觀的道長(zhǎng)诉字。 經(jīng)常有香客問(wèn)我壤圃,道長(zhǎng)伍绳,這世上最難降的妖魔是什么冲杀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任效床,我火速辦了婚禮睹酌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扁凛。我一直安慰自己忍疾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布卤妒。 她就那樣靜靜地躺著,像睡著了一般字币。 火紅的嫁衣襯著肌膚如雪则披。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天洗出,我揣著相機(jī)與錄音士复,去河邊找鬼。 笑死翩活,一個(gè)胖子當(dāng)著我的面吹牛阱洪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菠镇,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼冗荸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了利耍?” 一聲冷哼從身側(cè)響起蚌本,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隘梨,沒(méi)想到半個(gè)月后程癌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轴猎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嵌莉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捻脖。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烦秩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出郎仆,到底是詐尸還是另有隱情,我是刑警寧澤兜蠕,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布扰肌,位于F島的核電站,受9級(jí)特大地震影響熊杨,放射性物質(zhì)發(fā)生泄漏曙旭。R本人自食惡果不足惜盗舰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桂躏。 院中可真熱鬧钻趋,春花似錦、人聲如沸剂习。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鳞绕。三九已至失仁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間们何,已是汗流浹背萄焦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冤竹,地道東北人拂封。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鹦蠕,于是被迫代替她去往敵國(guó)和親冒签。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容