成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

AOP實(shí)踐: Java利用注解和反射實(shí)現(xiàn)一個(gè)方便的函數(shù)性能測(cè)量工具

zhangke3016 / 3092人閱讀

摘要:實(shí)現(xiàn)先看實(shí)現(xiàn)之后的效果測(cè)試類運(yùn)行輸出如下可以看到此時(shí)加了注解的和的運(yùn)行時(shí)間被統(tǒng)計(jì)了,而沒加的未被統(tǒng)計(jì)在內(nèi)。思路修改,在之前的中返回一個(gè),儲(chǔ)存方法名耗時(shí)的鍵值結(jié)構(gòu)。然后降序排序返回一個(gè)。最后遍歷根據(jù)百分比求得各個(gè)方法的并輸出相關(guān)信息。

最初目的

在學(xué)習(xí)Java的集合類時(shí),有時(shí)候想要測(cè)試代碼塊的運(yùn)行時(shí)間,以比較不同算法數(shù)據(jù)結(jié)構(gòu)之間的性能差異。最簡單的做法是在代碼塊的前后記錄時(shí)間戳,最后相減得到該代碼塊的運(yùn)行時(shí)間。

下面是Java中的示例:

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    algo(); // 執(zhí)行代碼塊
    long end = System.currentTimeMillis();
    System.out.println(end - start);
}

當(dāng)需要同時(shí)打印多個(gè)方法的運(yùn)行時(shí)間以進(jìn)行比較的時(shí)候就會(huì)變成這樣:

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    algo1(); // 算法1
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    
    long start = System.currentTimeMillis();
    algo2(); // 算法2
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  
    long start = System.currentTimeMillis();
    algo3(); // 算法3
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  
    // more
}
初探

顯然上面的代碼看起來非常冗余,由于Java不支持func(func)這樣的直接傳遞函數(shù)指針,本人又不想引入JDK以外太重的工具,所以嘗試寫一個(gè)回調(diào)來實(shí)現(xiàn)代碼塊的傳遞:

public interface Callback {
    void execute();
}
public class TimerUtil {
    public void getTime(Callback callback) {
        long start = System.currentTimeMillis();
        callback.execute();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}
// 測(cè)試類
public class Foo {
    
    void algo1() {
        // algo1
    }
    
    void algo2() {
        // algo2
    }
    
    void algo3() {
        // algo3
    }
    
    public static void main(String[] foo){
        TimerUtil tu = new TimerUtil();
        tu.getTime(new Callback() {
            @Override
            public void execute() {
                new Foo().algo1();
            }
        });
        tu.getTime(new Callback() {
            @Override
            public void execute() {
                new Foo().algo2();
            }
        });
        tu.getTime(new Callback() {
            @Override
            public void execute() {
                new Foo().algo3();
            }
        });
    }
}

發(fā)現(xiàn)此時(shí)雖然封裝了計(jì)時(shí)、打印等業(yè)務(wù)無關(guān)的代碼,然而對(duì)使用者來說代碼量并沒有減少多少。若仔細(xì)觀察,其實(shí)測(cè)試類中仍有一堆結(jié)構(gòu)重復(fù)的代碼,真正的業(yè)務(wù)藏在一堆匿名類中間,視覺上干擾很大。

Java 8為了解決類似的問題,引入了lambda,可以將代碼簡化為tu.getTime(() -> new Foo().algo());。lambda看起來很美,簡化了許多,然而這種寫法對(duì)于不熟悉的人寫起來還是不太順手,而且Java 8以下的環(huán)境無法這樣寫。

更重要的是從代碼的形式上看,algo() 還是被包在表達(dá)式內(nèi),仿佛getTime()才是主要邏輯一樣。由于之前接觸過Python,此時(shí)不禁想到,要是能像Python里那樣用裝飾器來解決就簡潔又方便了:

@getTime
def algo1():
    # algo1

@getTime
def algo2():
    # algo2

不過Java中也沒有這樣的語法糖,只有注解,于是思考是否可以利用反射和注解來“反轉(zhuǎn)”這種喧賓奪主的情況并使代碼更具可讀性。

實(shí)現(xiàn)

先看實(shí)現(xiàn)之后的效果:

// 測(cè)試類Foo
public class Foo {

    @Timer
    public void algo1() {
        ArrayList l = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            l.add(1);
        }
    }

    @Timer
    public void algo2() {
        LinkedList l = new LinkedList<>();
        for (int i = 0; i < 10000000; i++) {
            l.add(1);
        }
    }

    public void algo3() {
        Vector v = new Vector<>();
        for (int i = 0; i < 10000000; i++) {
            v.add(1);
        }
    }

    public static void main(String[] foo){
        TimerUtil tu = new TimerUtil();
        tu.getTime();
    }
}

運(yùn)行輸出如下:

可以看到此時(shí)加了@Timer注解的algo1()和algo2()的運(yùn)行時(shí)間被統(tǒng)計(jì)了,而沒加@Timer的algo3()未被統(tǒng)計(jì)在內(nèi)。

思路

使用反射獲取棧中當(dāng)前類(測(cè)試類)的信息,遍歷其中的方法,若方法包含@Timer注解,則執(zhí)行該方法并進(jìn)行時(shí)間戳相減。

實(shí)現(xiàn)這樣的效果僅需一個(gè)自定義注解和一個(gè)工具類:

@Retention(RetentionPolicy.RUNTIME)
public @interface Timer {
}
public class TimerUtil {

    public void getTime() {
        // 獲取當(dāng)前類名
        String className = Thread.currentThread().getStackTrace()[2].getClassName();
        System.out.println("current className(expected): " + className);
        try {
            Class c = Class.forName(className);
            Object obj = c.newInstance();
            Method[] methods = c.getDeclaredMethods();
            for (Method m : methods) {
                // 判斷該方法是否包含Timer注解
                if (m.isAnnotationPresent(Timer.class)) {
                    m.setAccessible(true);
                    long start = System.currentTimeMillis();
                    // 執(zhí)行該方法
                    m.invoke(obj);
                    long end = System.currentTimeMillis();
                    System.out.println(m.getName() + "() time consumed: " + String.valueOf(end - start) + "
");
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
升級(jí)

在同時(shí)統(tǒng)計(jì)多個(gè)方法時(shí),要是能可視化的打印出類似Performance Index一樣的柱狀圖,可以更直觀的比較他們之間的性能差異,就像這樣:

耗時(shí)最久的方法的Index固定為100,剩余的按相對(duì)的Index降序排列。

思路

修改TimerUtil,在之前的getTime()中返回一個(gè)HashMap,儲(chǔ)存方法名: 耗時(shí)的鍵值結(jié)構(gòu)。然后降序排序HashMap返回一個(gè)LinkedHashMap。最后遍歷LinkedHashMap根據(jù)百分比求得各個(gè)方法的Index并輸出相關(guān)信息。

public class TimerUtil {

    // 修改getTime()
    public HashMap getMethodsTable() {
        HashMap methodsTable = new HashMap<>();
        String className = Thread.currentThread().getStackTrace()[3].getClassName();
        // ...
        return methodsTable;
    }

    public void printChart() {
        Map result = sortByValue(getMethodsTable());
        double max = result.values().iterator().next();
        for (Map.Entry e : result.entrySet()) {
            double index = e.getValue() / max * 100;
            for (int i = 0; i < index; i++) {
                System.out.print("=");
            }
            System.out.println(e.getKey() + "()" + " Index:" + (long) index + " Time:" + e.getValue());
        }
    }

    > Map sortByValue(Map map) {
        List> list = new LinkedList<>(map.entrySet());
        // desc order
        Collections.sort(list, new Comparator>() {
            public int compare(Map.Entry o1, Map.Entry o2) {
                return (o2.getValue()).compareTo(o1.getValue());
            }
        });
        Map result = new LinkedHashMap<>();
        for (Map.Entry entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }
}
總結(jié)

本文介紹的是一個(gè)APM (Algorithm Performance Measurement) 工具比較粗糙簡陋的實(shí)現(xiàn),然而這種思路可以同樣應(yīng)用在權(quán)限控制、日志、緩存等方面,方便的對(duì)代碼進(jìn)行解耦,讓通用的功能“切入”原先的代碼,使得開發(fā)時(shí)可以更專注于業(yè)務(wù)邏輯。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/65190.html

相關(guān)文章

  • 注解認(rèn)證

    摘要:攔截器攔截下那些沒有與注解標(biāo)注的方法請(qǐng)求,并進(jìn)行用戶認(rèn)證。直接根據(jù)編寫的代碼生成原生的代碼,所以不會(huì)存在任何性能問題解決方案為了解決攔截器中使用反射的性能問題,我們學(xué)習(xí)的設(shè)計(jì)思路,在啟動(dòng)時(shí)直接完成所有反射注解的讀取,存入內(nèi)存。 問題描述 權(quán)限認(rèn)證 權(quán)限認(rèn)證一直是比較復(fù)雜的問題,如果是實(shí)驗(yàn)這種要求不嚴(yán)格的產(chǎn)品,直接逃避掉權(quán)限認(rèn)證。 軟件設(shè)計(jì)與編程實(shí)踐的實(shí)驗(yàn),后臺(tái)直接用Spring Dat...

    fancyLuo 評(píng)論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛情萌芽的模樣…… Java 進(jìn)階面試問題列表 -...

    chengtao1633 評(píng)論0 收藏0
  • 手把手教你如何優(yōu)雅使用Aop記錄帶參數(shù)復(fù)雜Web接口日志

    摘要:幾乎每一個(gè)接口被調(diào)用后,都要記錄一條跟這個(gè)參數(shù)掛鉤的特定的日志到數(shù)據(jù)庫。我最終采用了的方式,采取攔截的請(qǐng)求的方式,來記錄日志。所有打上了這個(gè)注解的方法,將會(huì)記錄日志。那么如何從眾多可能的參數(shù)中,為當(dāng)前的日志指定對(duì)應(yīng)的參數(shù)呢。 前言 不久前,因?yàn)樾枨蟮脑颍枰獙?shí)現(xiàn)一個(gè)操作日志。幾乎每一個(gè)接口被調(diào)用后,都要記錄一條跟這個(gè)參數(shù)掛鉤的特定的日志到數(shù)據(jù)庫。舉個(gè)例子,就比如禁言操作,日志中需要記...

    Loong_T 評(píng)論0 收藏0
  • Spring入門IOCAOP學(xué)習(xí)筆記

    摘要:入門和學(xué)習(xí)筆記概述框架的核心有兩個(gè)容器作為超級(jí)大工廠,負(fù)責(zé)管理創(chuàng)建所有的對(duì)象,這些對(duì)象被稱為。中的一些術(shù)語切面切面組織多個(gè),放在切面中定義。 Spring入門IOC和AOP學(xué)習(xí)筆記 概述 Spring框架的核心有兩個(gè): Spring容器作為超級(jí)大工廠,負(fù)責(zé)管理、創(chuàng)建所有的Java對(duì)象,這些Java對(duì)象被稱為Bean。 Spring容器管理容器中Bean之間的依賴關(guān)系,使用一種叫做依賴...

    wenyiweb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<