歐洲可充電公路測試中,預計2018年上市

電動車邊最怕路上沒電,但如果能邊跑邊充電,就不怕沒電的問題,這項殺手級應用聽起來很科幻,但現實世界,可充電公路已進入測試階段,離實際應用應該也不遠了。

將無線充電套件嵌入道路表面上,就成了名符其實的充電道路,美國晶片廠高通近期就在法國測試道路上展示這項技術,稱之為電動車行進間充電(dynamic electric vehicle charging,簡稱DEVC)。

據Economictimes報導,DEVC可對高速行駛之汽車,以最高20kW的功率充電,目前同一條跑道上,DEVC能同時對兩輛電動車進行充電。

歐洲非常重視電動車發展,這項計畫是由歐盟執委會(European Commission)主導,共耗費一千萬美元,來自九個歐盟成員國的25個機構共同參與。報導指出,無線充電電動車預計2017、2018年上市,多數主要車廠正準備制訂全球無線充電標準。

(本文內容由授權使用。圖片出處:wikipedia)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

ef+Npoi導出百萬行excel之踩坑記

        最近在做一個需求是導出較大的excel,本文是記錄我在做需求過程中遇到的幾個問題和解題方法,給大家分享一下,一來可以幫助同樣遇到問題的朋友,二呢,各位大神也許有更好的方法可以指點小弟一下,讓我順便學習一下。 背景::工頭:“小鍾啊,xx界面加個導出excel功能03以後的格式,需要能支持到excel的最大行,同時需要5個併發就行” 我:“收到,但是數據大的時候速度可能比較慢。” 工頭:“你先做後續客戶反饋了在給他加進度條。” Npoi神器介紹:SXSSFWorkbook 專門用來導出大數據用,他會把數據先寫入C盤的臨時目錄;不會所有 都留在內存里;更詳細介紹請百度或者參考( ) 有了這層基礎開始劈里啪啦一段操作寫代碼;(以下代碼非生產代碼只是我為了帖子寫重現問題的測試代碼) 首先開個線程模擬併發 編寫導出方法:記錄時間、創建SXSSFWorkbook 代碼如圖: 啟動運行; 好!第一口鍋已造好,看這個提示,前面說了SXSSFWorkbook 是會先把緩存數據寫入Windows臨時文件裡頭的,這個目錄正好是Windows的臨時文件夾雖然是個錯誤但是驗證了剛剛的說法;至於這個錯誤看提示 我們有個大膽的想法是文件佔用問題,應該是創建文件的時候文件已經存在了,這樣我們把npoi的dll打開來看看,通過看源碼和各種f12我們看到了這麼一段代碼 這裏看到用來隨機數,而我們知道net的隨機數在極短的時間內生成是不可靠的(詳見百度或者: )也就是說生成一樣的文件名,然後我們在通過 github里可以看到   早在年初NPOI就對這個問題做了更改就換成guid了,隨後我來到了nuget nuget最新版 是去年12月份發布,並沒有包含上面的更改; 所以呢 要麼github下載最新版編譯要麼自己解決,想了想如果換版本的話以前的功能可能會影響到所以,我們就在外面加一把小鎖吧!如圖   這樣呢我們在試試!   很好 不會在出現文件佔用問題了;好繼續導出! 既然是都先寫入緩存文件是不是佔用的內存就很小了 來看看 2G多。。。什麼情況,還在漲   3G。。。這明顯不符合工頭的需求了,然後終於它炸了 第一念頭是為啥我該怎麼辦,設置GC的回收模式?手動多GC?還是要把代碼給拿下來看看,看看這麼大內存哪裡沒釋放文件?冷靜、冷靜、想想,既然是內存爆了 那麼正確流程應該是抓取看看是什麼吃的內存得出結果再去改東西, 發現了啥是不是很熟悉的東西? 狀態管理、包裝類,想到了啥 EF的“模型跟蹤”這個功能佔用的內存最大了。那就去掉吧 加上這麼一句 意思是無跟蹤查詢 ,修改實例后SaveChanges不對對它生效; (AsNoTracking 更詳情理解介紹請百度在加上msdn: ) 現在在繼續導出看看: 內存是吃的不大了, 可以看出臨時文件還是很大的,這還沒導完呢,所以做的時候 盡量要保證下硬盤的空間! 等待。。。 總結: 1.導出大數據用SXSSFWorkbook 2.構建SXSSFWorkbook 時候lock或者自己編譯最新版本 3.我們做導出時,ef查詢數據後記得加AsNoTracking 關閉綁定跟蹤。(以後日常開發中如果只需要查詢的也可以這樣做) 4.SXSSFWorkbook 導出大數據 臨時文件夾所在的硬盤不能太小 因為會生成大於excel本身的緩存文件!     最後導出完畢 用時:  本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

類加載器 – 命名空間

本博客將沿用中展示的自定義類加載器代碼

複雜類加載情況分析

測試代碼一

首先,新建一個類Test14,重寫默認的構造方法,打印加載該類的類加載器

public class Test14 {
    public Test14() {
        System.out.println("Test14 is loaded by:" + this.getClass().getClassLoader());
    }
}

然後,在新建一個類Test15,同樣重寫默認的構造方法,打印加載該類的類加載器,在構造方法中new出Test14的實例

public class Test15 {
    public Test15() {
        System.out.println("Test15 is loaded by:" + this.getClass().getClassLoader());

        new Test14();
    }
}

測試代碼

public class Test16 {
    public static void main(String[] args) throws Exception {
        test01();
    }

    private static void test01 () throws Exception {
        ClassLoaderTest classLoader = new ClassLoaderTest("classLoader");
        Class<?> clazz = classLoader.loadClass("classloader.Test15");
        System.out.println("class:" + clazz);
        Object object = clazz.newInstance();
    }
}

猜測一下,首先自定義類加載器classLoader通過反射獲取Test15的Class對象,屬於主動使用,會加載Test15,classLoader委託它的父加載器AppClassLoader加載Test15;然後我們通過clazz.newInstance();代碼獲取Test15的實例,調用Test15的構造方法,在Test15的構造方法中創建了Test14的實例,所以同樣加載了Test14,並調用了Test14的構造方法。加上-XX:+TraceClassLoading指令執行代碼,發現運行結果和我們想的是一樣的。

......
[Loaded classloader.Test15 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
[Loaded classloader.Test14 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
......

測試代碼二

在上篇博客中,自定義類加載器ClassLoaderTest是有一個path屬性可以自定義類的加載路徑的,我們同樣測試一下,我們將Test14和Test15的class文件放到桌面的classloader文件夾下,然後刪除工程路徑下的class文件,執行一下的測試代碼

public class Test16 {
    public static void main(String[] args) throws Exception {
        test02();
    }
    private static void test02 () throws Exception {
        ClassLoaderTest classLoader = new ClassLoaderTest("classLoader");
        classLoader.setPath("/home/fanxuan/桌面/");
        Class<?> clazz = classLoader.loadClass("classloader.Test15");
        System.out.println("class:" + clazz);
        Object object = clazz.newInstance();
    }
}

按照上節的結果,應該都是ClassLoaderTest加載器加載了Test14和Test15類

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:classloader.ClassLoaderTest@6d6f6e28

接下來,我們重新編譯項目,刪除掉工程目錄下的Test14的calss文件,再次執行代碼

class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: classloader/Test14
    at classloader.Test15.<init>(Test15.java:11)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at classloader.Test16.test02(Test16.java:25)
    at classloader.Test16.main(Test16.java:9)
Caused by: java.lang.ClassNotFoundException: classloader.Test14
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 8 more

我們發現結果報錯了,按照我們正常的思維,自定義記載器classLoader委託父加載器AppClassLoader加載Test15,從打印結果可以看出Test15加載成功了,然後創建Test15的實例,加載Test14,因為工程目錄下缺少Test14的class文件,所以AppClassLoader無法加載到Test14,由自定義加載器classLoader自身從桌面加載Test14。但是我們發現加載Test14的報了ClassNotFoundException的錯誤,這是因為在Test15中記載Test14的時候,是以Test15的類加載器AppClassLoader來加載的,AppClassLoader加載不到Test14,它的父加載器擴展類加載器同樣加載不到,擴展類加載器的父加載器啟動類加載器也加載不到,所以報錯ClassNotFoundException

然後,再重新編譯項目,刪除掉工程目錄下的Test15的calss文件,再次執行代碼。根據前文分析的代碼,我們可以很清晰的得出結論:由自定義記載器classLoader加載了Test15,由系統類記載器AppClassLoader加載了Test14。

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2

測試代碼三

簡單修改下Test14類,在Test14的構造方法中引用Test15的Class對象。

public class Test14 {
    public Test14() {
        System.out.println("Test14 is loaded by:" + this.getClass().getClassLoader());

        System.out.println("Test14:" + Test15.class);
    }
}

執行測試代碼二中的測試代碼Test16,結果如下,沒有任何問題。

class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Test14:class classloader.Test15

我們同樣重新編譯項目,刪除掉工程目錄下的Test15的calss文件,再次執行代碼。

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: classloader/Test15
    at classloader.Test14.<init>(Test14.java:11)
    at classloader.Test15.<init>(Test15.java:11)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at classloader.Test16.test02(Test16.java:25)
    at classloader.Test16.main(Test16.java:9)
Caused by: java.lang.ClassNotFoundException: classloader.Test15
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 9 more

我們發現加載已經完成了,但是程序還是報錯了,是我們剛剛加的System.out.println("Test14:" + Test15.class);代碼報的錯,依然是ClassNotFoundException錯誤。

分析:
Test15由自定義記載器classLoader加載,Test14由系統類記載器AppClassLoader加載。導致程序報錯的是因為命名空間的問題,我們在上一篇博客的結尾簡單介紹了命名空間:每個類加載器都有自己的命名空間,命名空間由該加載器及所有的父加載器所加載的類組成。子加載器所加載的類可以看見父加載器加載的類,但是父加載器所加載的類無法看見子加載器加載的類。Test14是由AppClassLoader加載的,在AppClassLoader的命名空間中沒有Test15的,所以程序報錯了。

命名空間實例分析

測試代碼

新建Entity類用於測試

public class Entity {
    private Entity entity;

    public void setEntity(Object entity) {
        this.entity = (Entity)entity;
    }
}

編寫測試代碼

public class Test17 {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest classLoader1 = new ClassLoaderTest("classLoader1");
        ClassLoaderTest classLoader2 = new ClassLoaderTest("classLoader2");

        Class<?> clazz1 = classLoader1.loadClass("classloader.Entity");
        Class<?> clazz2 = classLoader2.loadClass("classloader.Entity");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setEntity", Object.class);
        method.invoke(object1, object2);
    }
}

運行程序,System.out.println(clazz1 == clazz2);返回結果為true,都是AppClassLoader加載的,classLoader1加載之後會在AppClassLoader的命名空間中形成緩存,classLoader2加載的時候直接返回命名空間已經存在的Class對象,所以clazz1與clazz2相同。

改造下代碼,將Entity類的class文件copy到桌面文件夾下,刪除工程下的class文件,執行如下代碼

public class Test18 {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest classLoader1 = new ClassLoaderTest("classLoader1");
        ClassLoaderTest classLoader2 = new ClassLoaderTest("classLoader2");

        classLoader1.setPath("/home/fanxuan/桌面/");
        classLoader2.setPath("/home/fanxuan/桌面/");

        Class<?> clazz1 = classLoader1.loadClass("classloader.Entity");
        Class<?> clazz2 = classLoader2.loadClass("classloader.Entity");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setEntity", Object.class);
        method.invoke(object1, object2);
    }
}

根據前文的介紹,不難推斷System.out.println(clazz1 == clazz2);的運行結果為falseclassLoader1和classLoader2分別加載了Entity類,就是其自身加載的(定義類加載器),在jvm的內存中形成了完全獨立的兩個命名空間,所以clazz1與clazz2不同。而且因為clazz1和clazz2相互不可見,調用了classLoader1命名空間中的方法,傳入了classLoader2命名空間的對象,導致程序拋出了異常。

false
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at classloader.Test18.main(Test18.java:26)
Caused by: java.lang.ClassCastException: classloader.Entity cannot be cast to classloader.Entity
    at classloader.Entity.setEntity(Entity.java:11)
    ... 5 more

不同類加載器的命名空間關係

  • 同一命名空間內的類是相互可見的
  • 子加載器的命名空間包含所有父加載器的命名空間,由子加載器所加載的類可以看見父加載器加載的類
  • 由父加載器所加載的類無法看見子加載器加載的類
  • 如果兩個加載器之間沒有任何直接或間接的父子關係,那麼它們各自加載的類相互不可見

父親委託機制的好處

在的2.1章節簡單介紹了一下類加載器的父親委託機制,這裏面來總結一下好處

  • 確保Java核心類庫的安全:所有的Java應用都至少會引用java.lang.Object類,也就是說在運行期,java.lang.Object類會被記載到Java虛擬機當中;如果這個加載過程是由Java應用自己的類加載器所完成的,那麼可能會在JVM中存在多個版本的java.lang.Object類,而且這些類還是不兼容的、相互不可見的(因為命名空間的原因)。藉助父親委託機制,Java核心類庫中的類的加載工作都是由啟動類加載器來統一完成的,從而確保了Java應用所使用的都是同一個版本的Java核心類庫,他們之間是互相兼容的。
  • 確保Java核心類庫提供的類不會被自定義的類所替代。
  • 不同的類加載器可以為相同名稱(binary name)的類創建額外的命名空間。相同名稱的類可以並存在Java虛擬機中,只需要用不同的類加載器來加他們即可,不同類加載器所加載的類是不兼容的,這就相當於在Java虛擬機內部創建了一個又一個相互隔離的Java類空間。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

特斯拉大眾電動車Model 3本周量產,預估月底交車

特斯拉周一公佈第二季僅交車2.2萬輛,上半年合計4.7萬輛,僅以最低標達陣。不過這不打緊,市場最關注的是特斯拉執行長馬斯克(Elon Musk)隨後宣布大眾化電動車Model 3本周五即可進入量產,比預估時程提前兩周。

特斯拉說電池模組供給嚴重不足,導致產出受限,直至六月才獲得抒解,預期Model S與Model X等高檔車下半年交車狀況有望優於上半年。特斯拉原預期上半年交車量介於4.7-5萬輛之間。(路透社)

特斯拉目前已收到近40萬輛Model 3的預購訂單,據馬斯克表示,首批30輛Model 3將在7月28日交車,九月產能可提升至1500輛,估計十二月可達成月產2萬輛的目標。

市場研究機構Consumer Edge Research分析師艾伯汀(James Albertine)指出,按照特斯拉現在的規劃,Model 3進度微幅超前,不然至少也在預期之內,證明馬斯克之前的豪語不是隨便說說。(金融時報)

特斯拉股價周一於正常交易時段收跌2.49%,但今年迄今累計漲幅仍高達65%,市值來到580億美元。

(本文內容由授權使用。圖片出處:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

Jaguar 宣布進軍電動車市場,要來挑戰Tesla

各個車廠看到電動車的趨勢,不願意讓Tesla 再繼續引領風潮,包括Jaguar 也將在2018 年下半年發表電動車車款。

Jaguar 的新電動車概念車I-PACE 將在2018 年下半年推出,將是Jaguar 第一台純電動車。性能方面具備700Nm 高扭力,400PS 馬力與4 秒內完成0~100km/h 加速的超高性能,相信是追求速度和零排放車主的選擇。

Jaguar 決定推出全電動車,意味著要跟Tesla 在SUV 市場短兵相接。由於電動車不用傳統車子的馬達系統,車內空間可以更寬敞讓乘客和駕駛更舒適。

(合作媒體:。圖片出處:Jaguar)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

WeTest明星工具-移動端性能測試PerfDog初探

在十一月初,騰訊就官宣了一則消息,騰訊WeTest明星工具-PerfDog面向全球發布。官宣介紹如下:。我在看到該新聞時,有種大開眼界的感覺,移動端的性能測試原來可以這麼簡單。今天閑暇之餘,來了一波初探,簡單體驗了一番。

軟件性能數據採集

我們先來了解下通過該工具能採集到哪些性能數據:

PerfDog支持移動平台所有應用程序(遊戲、APP應用、瀏覽器、小程序等)及Android模擬器,桌面應用程序PerfDog支持在Windows和Mac機器使用運行。在iOS和Android平台獲取性能參數如下:

iOS平台 (與蘋果官方Xcode工具參數對齊一致)

  • Screenshot
  • FPS(1秒內遊戲畫面或者應用界面真實平均刷新次數,俗稱幀率/FPS)
       1) Avg(FPS):平均幀率(一段時間內平均FPS)
       2) Var(FPS):幀率方差(一段時間內FPS方差)
       3) Drop(FPS):降幀次數(平均每小時相鄰兩個FPS點下降大於8幀的次數)
  • Jank(1s內卡頓次數。iOS9.1以下系統暫時不支持。類似Android的Jank卡頓和iOS的FramePacing平滑度統計原理。幀率FPS高並不能反映流暢或不卡頓。比如:FPS為50幀,前200ms渲染一幀,后800ms渲染49幀,雖然幀率50,但依然覺得非常卡頓。同時幀率FPS低,並不代表卡頓,比如無卡頓時均勻FPS為15幀。所以,平均幀率FPS與卡頓無任何直接關係)
        PerfDog計算方法:同時滿足兩條件,則認為是一次卡頓Jank.
        1、 當前幀耗時>前三幀平均耗時2倍。
        2、 當前幀耗時>兩幀電影幀耗時(1000ms/24*2=84ms)。
        同時滿足兩條件,則認為是一次嚴重卡頓BigJank.
        1、 當前幀耗時>前三幀平均耗時2倍。
        2、 當前幀耗時>三幀電影幀耗時(1000ms/24*3=125ms)。
    計算思路:考慮視覺慣性,假設以前三幀的平均幀耗時為參考,作為vsync時間間隔,連續兩次vsync沒有新渲染畫面刷新,則認為是一次潛在卡頓,也就是說下一幀耗時大於前三幀平均幀耗時2倍,則認為一次潛在卡頓。同時單幀耗時滿足大於兩倍電影幀耗時1000ms/24*2 (由於人眼低於24幀才能辨別畫面不連續性),則認為是一次真正卡頓。同時若單幀耗時大於3倍電影幀耗時,則認為是一次嚴重卡頓。
    註解:為什麼是兩次vsync?GPU一般是3重緩衝buffer,當前幀已佔用一個buffer,即剩餘2緩衝buffer,人眼一般可容忍2幀延遲。 為什麼是兩幀電影幀耗時?低於24幀畫面,人眼就能感知到畫面不連續性,電影一般都是24幀。即電影幀耗時1000ms/24=41.67ms,兩幀電影幀耗時也就是41.67ms*2,三幀電影幀耗時是41.67ms*3。
       1) BigJank:1s內頓嚴重卡次數
       2) Jank(/10min):平均每10分鐘卡頓次數。
       3) BigJank(/10min):平均每10分鐘嚴重卡頓次數
  • FTime(上下幀畫面显示時間間隔,即認為幀耗時,iOS9.1以下系統暫時不支持。)
       1) Avg(FTime):平均幀耗時 
       2) Delta(FTime):增量耗時(平均每小時兩幀之間時間差>100ms的次數)
  • CPU Usage(Total整機/App進程,統計結果合Xcode一致)
  • Memory (是統計FootPrint,注:OOM與FootPrint有關,與系統、機型無關。只與RAM有關,如1G內存機器。FootPrint超過650MB,引發OOM)。受iOS平台限制,暫時無法獲取ios10及以下系統的memory。後續版本增加。如做性能測試,建議升級iOS系統版本
  • Xcode Memory (XCode Debug Gauges統計方式即XCode Memory)。受iOS平台限制,暫時無法獲取ios10及以下系統的Xcode Memory。後續版本增加。如做性能測試,建議升級iOS系統版本
  • Real Memory(Xcode Instrument統計方式即Real Memory,實際佔用物理內存。注:物理內存與系統策略有關,關注意義不大)
  • Virtual Memory(虛擬內存)
  • Wakeups(線程喚醒次數)。注:超過150進程很大可能會被系統kill
  • CSwitch(上下文切換測試)。注:單核超過14000進程會被系統Kill
  • GPU Utilization(Render/Tilter/Device)
       1) Render:渲染器利用率(像素着色處理階段,若佔比高,說明是PS階段出現瓶頸,shader過於複雜或紋理大小、採樣複雜等) 
       2) Tilter:Tilter利用率(頂點着色處理階段,若佔比高,說明是VS階段出現瓶頸,頂點數太多等原因)
       3) Device:設備利用率(整體GPU利用率)
  • Network(Recv/Send,測試目標進程流量,和Xcode結果一致)
  • Battery Power(整機實時Current電流、Voltage電壓、Power功率)(注:和Xcode Instrument結果一致)
  • Log(系統調試日誌信息)

Android平台

  • Screenshot
  • FPS(1秒內遊戲畫面或者應用界面真實平均刷新次數,俗稱幀率/FPS)
       1) Avg(FPS):平均幀率(一段時間內平均FPS)
       2) Var(FPS):幀率方差(一段時間內FPS方差)
       3) Drop(FPS):降幀次數(平均每小時相鄰兩個FPS點下降大於8幀的次數)
  • Jank(1s內卡頓次數。解釋說明如iOS平台說明)
       1) BigJank:1s內嚴重卡頓次數
       2) Jank(/10分鐘):平均每10分鐘卡頓次數
       3) BigJank(/10分鐘):平均每10分鐘嚴重卡頓次數 
  • FTime(上下幀畫面显示時間間隔,即認為幀耗時)
       1) Avg(FTime):平均幀耗時
       2) Delta(FTime):增量耗時(平均每小時兩幀之間時間差>100ms的次數)
  • CPU Usage(Total整機/App目標進程,統計結果和Android Studio Profiler一致)
  • CPU Clock(各個CPU核心的頻率和使用率)
  • Memory (PSS Memory,統計結果和Android Java API標準結果一致,與Meminfo也一致。注:部分三星機器系統修改了Meminfo底層統計方式,導致Meminfo與Java AP統計結果不一致,新出三星機器已修復)
  • Swap Memory (Swap Memory)
  • Virtual Memory
  • Memory Detail(NativePSS、GFX、GL、Unknown)
  • GPU Usage(目前僅支持高通芯片手機)
  • GPU Frequency(目前僅支持高通芯片手機)
  • Network(Recv/Send)
  • CTemp(CPU溫度)
  • Battery Power(Current電流、Voltage電壓、Power功率)(注:與儀器測試誤差<3%左右)
  • Log(系統調試日誌信息)

上述內容來自官網使用文檔。我們了解了參數,就實際來操作一下吧。對於工具的介紹,網絡上都有,我就結合自己的實際體驗來說吧。

使用的基本流程

在自己實踐使用時,基本流程如下:

1.註冊賬號(只有註冊賬號后才能下載安裝包)

2.下載安裝包並解壓

3.在perfdog後台創建測試項目

4.打開可執行文件PerfDog.exe

5.使用註冊的賬號登錄

6.使用usb將手機和電腦連接(不能鎖屏,開啟調試模式)

7.選擇連接模式(wifi還是usb)

8.選擇app應用列表

9.配置要監控的數據

10.開始記錄數據

11.操作對應app

12.停止記錄數據(不能少於10S)

13.上傳記錄數據

14.進入perfdog後台查看性能數據

流程介紹

前五步操作就不講述了,大家都懂。我們直接從第六步說起,我使用的是ios設備。

連接設備

iOS: 則即插即用,用戶無需做任何操作。

Android: 有兩種模式,非安裝模式和安裝模式。

  • a. 非安裝模式:

    手機即插即用,無需任何設置及安裝,使用非常簡單,但手機屏幕上沒有實時性能數據显示。

  • b. 安裝模式:

    需要在手機上自動安裝PerfDog.apk,手機屏幕上有實時性能數據显示。(請開啟Debug調試模式、允許USB安裝和PerfDog懸浮窗管理權限),啟動PC版PerfDog.exe,則會在手機上自動PUSH安裝PerfDog.apk,具體安裝類似各個手機廠商安裝第三方APP提示安裝即可。(注:由於很多手機安裝需要賬號密碼,導致無法自動安裝,如果自動安裝失敗,則會把安裝文件PerfDog.apk釋放到當前文件夾里,手動安裝PerfDog.apk即可)。

這裏重點說明下Android平台下,LMK和Swap這兩個參數意義:

LMK:Android平台下OOM與遊戲進程內存大小無關,主要是系統剩餘物理內存有關。系統剩餘物理內存小於LMK,則會引起OOM。

Swap: 系統進程用到zram/vnswap內存壓縮技術。不同手機系統啟用Swap memeroy大小不同。

測試模式

通過usb連接電腦後,出現如下界面,可以選擇測試模式:

USB模式測試:

  USB連線,在設備列表選擇USB圖標設備進行USB模式測試(插線模式測試功率無任何意義)。

WIFI模式測試(測試功率):

  USB連線后,在設備列表選擇WIFI圖標設備進行WIFI模式測試。WIFI檢測連接成功后,拔掉USB連接線。(注:需要PC和被測手機連接同一WIFI,WIFI檢測連接成功后,拔掉被測手機USB線(插線模式測試功率無任何意義))。

在實踐中,USB和WiFi模式我都有使用。選擇模式后,界面會展示設備的詳細信息,如下:

選擇測試應用

選擇模式后,則可以選擇要測試的應用了(當前手機中的所有app都可以被選擇),如下頁面:

選擇對應被測應用,並操作對應的app,界面展示如下:

注意點:Android平台,安裝模式下,手機屏幕左上角有實時性能數據显示(Android手機請打開PerfDog懸浮窗管理權限,否則手機上不會显示性能參數)。

開啟懸浮權限

android設備中的界面性能參數显示如下:

功能介紹

1.性能參數配置

性能參數可在界面中配置,點擊界面中的+號即可,如下:

①點擊對應條目參數,顏色會變深,圖表數據則會展示在界面中

②勾選對應條目參數,表示需要收集該數據

2.記錄保存

點擊右側的藍色開始按鈕,則表示在記錄數據,如下:

需要注意的是:記錄時間不能少於10S。少於10S,則會提示如下信息:

點擊按鈕后,記錄會停止記錄並保存數據,如下:

2.1 提交記錄到perfdog後台

可以修改名稱,點擊confirm,數據會上傳到perfdog的後台,如下:

可以查看詳細的性能數據,如下所示:

2.2 記錄保存到本地

勾選保存按鈕,數據就會保存到本地,如下:

可以打開excel文件查看對應的性能數據:

3.數據回放

點擊perfdog界面上的文件夾按鈕,選擇對應的本地數據,即可以回放記錄,操作如下:

可在界面查看回放結果,如下:

4.批註及標定

雙擊鼠標左鍵,增加批註,再次雙擊,則取消批註。

單擊鼠標左鍵,則增加標定,再次點擊則重新標定。

增加了批註和標定的界面如下所示,紅色為批註,淡紫色為標定:

5.性能參數分析

5.1 數據統計

可以選擇一個時間段內的數據,進行統計,如下:

5.2 設置性能參數統計分析閾值

在perfdog界面中的setting下,可以配置,如下:

5.3 保存框選數據

對某一時間段內的數據框選后,可以單獨保存片段,在框選範圍內,右鍵即可,如下:

6.場景標籤

通過標籤按鈕給性能數據打標籤,鼠標左鍵雙擊顏色區域可修改對應區域標籤名

7.日誌記錄

在perfdog界面,可以查看對應日誌,也可以設置查看日誌的等級,如下:

在嘗試WIFI模式時,發現log按鈕勾選不了。

8.停止功能

停止測試應用,不需要拔掉數據線,或者斷開連接,在選擇應用的界面中,選擇NULL即可,如下:

9.截圖錄屏

連接安卓設備,並使用安裝模式,可配置截屏參數,如下:

界面就會記錄操作的過程,如下所示:

如此記錄是不是很明了?但這種用法會影響性能參數,實際用途中不推薦。如果覺得新鮮,可以嘗試使用即可。

PerfDog後台使用

1.邀請人員

可以邀請對應人員一起維護測試項目

2.數據共享

數據共享后,可以在任務數據中查看明細,可按android、ios區分,以及app包的版本,設備版本來查看。

使用注意點

1.設備連接

iOS: 若PerfDog檢測不到連接手機或無法測試,請先安裝確保最新iTunes是否能連上手機。

Android: 請開啟手機Debug調試模式及允許USB安裝。

2.截圖記錄影響性能

截屏記錄影響性能(整體FPS影響<=1。小米5:CPU=1%左右。IPhone7P:CPU<2%),若無需請不要開啟截屏。

總結

使用PerfDog工具下來,整體有以下幾點感受。

1.對性能指標的測試,更加便捷;

2.易操作

3.記錄支持回放

4.數據便於管理與查看

PerfDog工具是款不錯的性能測試工具,點贊一波。

最後,附上官方的操作手冊:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

ES6 Map 原理

ES6的Map的鍵可以是任意的數據結構,並且不重複。

那麼map的底層原理是啥呢?

Map利用鏈表,hash的思想來實現。

首先,Map可以實現刪除,而且刪除的數據可以是中間的值。而鏈表的優勢就是在中間的任意位置添加,刪除元素都非常快,不需要移動其他元素,直接改變指針的指向就可以。。

而在存儲數據很多的情況下,會導致鏈條過長,導致查找效率慢,所以我們可以創建一個桶(存儲對象的容器),根據hash(把散列的值通過算法變成固定的某值)來平局分配數據,防止鏈條過長。

 

 

如下圖:桶裏面有3個位置,每一個位置都是一個對象,通過next屬性指向下一個對象來把沒有關聯的對象聯到一起。

 

 

 

把Map屬性值和屬性名都存到對象的值里。

簡單模擬Map,代碼如下:

function Mymap() {  //構造函數
    this.init();
}
//初始化函數,創建桶(數組),每個位置都是一個對象,每個對象的屬性上設置next屬性,並且初始化為null。 Mymap.prototype.init = function () { this.tong = new Array(8); for (var i = 0; i
< 8; i++) { this.tong[i] = new Object(); this.tong[i].next = null; } }; //添加數據。 Mymap.prototype.set = function (key, value) { var index = this.hash(key); //獲取到當前設置的key設置到那個位置上 var TempBucket = this.tong[index]; //獲取當前位置的對象 while (TempBucket.next) { //遍歷如果當前對象鏈接的下一個不為空 if (TempBucket.next.key == key) { //如果要設置的屬性已經存在,覆蓋其值。 TempBucket.next.value = value; return; //return ,不在繼續遍歷 } else { TempBucket = TempBucket.next; //把指針指向下一個對象。 } } TempBucket.next = { //對象的next是null ,添加對象。 key: key, value: value, next: null } };
//查詢數據 Mymap.prototype.get = function (key) { var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.key == key){ return TempBucket.value; }else{ TempBucket = TempBucket.next; } } return undefined; }
//刪除數據 Mymap.prototype.delete = function(key){ var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.next.key == key){ TempBucket.next = TempBucket.next.next; return true; }else{ TempBucket = TempBucket.next; } } }
//看當前屬性是否存在 Mymap.prototype.has = function(key){ var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.key == key){ return true; }else{ TempBucket = TempBucket.next; } } return false; }
//清空這個map Mymap.prototype.clear = function(){ this.init(); } //使設置的屬性平均分配到每個位置上,使得不會某個鏈條過長。 Mymap.prototype.hash = function (key) { var index = 0; if (typeof key == "string") { for (var i = 0; i < 3; i++) { index = index + isNaN(key.charCodeAt(i)) ? 0 : key.charCodeAt(i); } } else if (typeof key == 'object') { index = 0; } else if (typeof key == 'number') { index = isNaN(key) ? 7 : key; } else { index = 1; } return index % 8; } var map = new Mymap(); //使用構造函數的方式實例化map

map.set('name','zwq');
map.get('name');
map.has('name);

 

 

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

面試填坑筆記-從代理模式到SpringAOP的動態代理

代理模式是一種理論上非常簡單,但是各種地方的實現往往卻非常複雜。本文將從代理模式的基本概念出發,探討代理模式在java領域的應用與實現。讀完本文你將get到以下幾點:

  1. 為什麼需要代理模式,它通常用來解決什麼問題,以及代理模式的設計與實現思路
  2. Java領域中代理模式3種不同實現類型(靜態代理,jdk動態代理,cglib)
  3. 代理模式的面試考點

為什麼要使用代理模式

在生活中我們通常是去商場購買東西,而不是去工廠。最主要的原因可能有以下幾種:

  1. 成本太高,去工廠路途遙遠成本太高,並且可能從工廠進貨要辦理一些手續流程;
  2. 工廠不直接賣給你,畢竟可能設計到一些行業機密或者無良廠家有一些不想讓你知道的東西;
  3. 商場能提供一些商品之外的服務,商場里有舒適的溫度,整潔的洗手間,當然還有漂亮的小姐姐。

在面向對象的系統中也有同樣的問題,有些對象由於某種原因,比如對象創建開銷很大,或者某些操作需要安全控制等,直接訪問會給使用者或者系統結構帶來很多麻煩,這時我們就需要考慮使用代理模式

在應用中我們可能會用代理模式解決以下問題:

  1. 權限控制與日誌, 在客戶端請求接口時我們可能需要在調用之前對權限進行驗證,或者通過記錄接口調用前後時間,統計執行時長,又或者說我們需要記錄用戶的一些操作日誌信息等,我們可以對原接口進行代理,然後根據需求在接口執行前後增加一些特定的操作。
  2. 重量級操作, 比如創建開銷大的對象, 可以先由代理對象扮演對象的替身,在需要的使用再創建對象,然後代理再將請求委託給真實的對象。

什麼是代理模式

代理模式:為其他對象提供一種代理以控制(隔離,使用接口)對這個對象的訪問。類圖如下:

所謂控制,其實使用接口隔離其他對象與這個對象之間的交互;就是為client對象對RealSubject對象的訪問一種隔離,本質上就是CLient→RealSuject的關係變成了Client→Subject, Proxy→RealSubject。 需要注意的時,代理類(Proxy)並不一定要求保持接口的完整的一致性(既也可以完全不需實現Subject接口),只要能夠實現間接控制即可。

代理模式代碼演進

背景:假設已有一個訂單系統,可以保存訂單信息。

需求:打印保存訂單信息消耗時間。

/**
 * 訂單服務
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService2 {
    /**
     * 保存訂單接口
     */
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}

普通方式實現

直接修改源代碼,這通常也是最簡單和最容易想到的實現。

 /**
  * 保存訂單接口, 直接修改代碼
  */
 public void saveOrder(String orderInfo) throws InterruptedException {
 
     long start = System.currentTimeMillis();
 
     // 隨機休眠,模擬訂單保存需要的時間
     Thread.sleep(System.currentTimeMillis() & 100);
     System.out.println("訂單:" + orderInfo + "  保存成功");
 
     System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
 }

面向對象設計原則中的“開閉原則”告訴我們,開閉原則規定“軟件中的對象(類,模塊,函數等等)應該對於擴展是開放的,但是對於修改是封閉的”,這意味着一個實體是允許在不改變它的源代碼的前提下變更它的行為。

代理模式實現

/**
 * 1. 定義接口,為了使代理被代理對象看起來一樣。當然這一步完全可以省略
 *
 * @author cruder
 * @date 2019-11-23 15:58
 **/
public interface IOrderService {
    /**
     * 保存訂單接口
     * @param orderInfo 訂單信息
     */
    void saveOrder(String orderInfo) throws InterruptedException;
}
/**
 * 2. 原有訂單服務,也實現這個接口。注意 此步驟也完全可以省略。
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService implements IOrderService{
    /**
     * 保存訂單接口
     */
    @Override
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}


/**
 * 3. 創建代理類,實現訂單服務接口【這才是代理模式的實現】
 * 
 * @author cruder
 * @date 2019-11-23 16:01
 **/
public class OrderServiceProxy implements IOrderService{
    /**
     * 內部持有真實的訂單服務對象,保存訂單工作實際由它來完成
     */
    private IOrderService orderService;

    @Override
    public void saveOrder(String orderInfo) throws InterruptedException {
        /**
         * 延遲初始化,也可以創建代理對象時就創建,或者作為構造參數傳進來
         * 僅作為代碼實例,不考慮線程安全問題
         */
        if (orderService == null) {
            orderService = new OrderService();
        }

        long start = System.currentTimeMillis();
        orderService.saveOrder(orderInfo);
        System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
    }
}執行程序

執行程序

代理模式的優缺點

優點: 1、職責清晰。 2、高擴展性。 3、智能化。

缺點:

1、由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。 2、實現代理模式需要額外的工作,有些代理模式的實現非常複雜。

Java中代理模式的實現

在java中代理模式可以按照代理類的創建時機分兩類,即靜態代理和動態代理,而動態代理又可以分為jdk動態代理和cglib動態代理。每種實現方式都各有千秋,接下來筆者將回針對不同的實現方式進行演示和剖析。

靜態代理

在上文代理模式代碼演進中就使用了靜態代理模式。所謂靜態代理中的“靜”字,無非就是代理類的創建時機不同罷了。靜態代理需要為每個被代理的對象手動創建一個代理類;而動態代理則時在運行時通過某種機制來動態生成,不需要手動創建代理類。

動態代理 – jdk

jdk動態代理模式是利用java中的反射技術,在運行時動態創建代理類。接下來我們仍藉助上文中的訂單服務的案例,使用jdk動態代理實現。

基於動態jdk涉及到兩個核心的類Proxy類和一個 InvocationHandler接口。

/**
 * 基於JDK技術 動態代理類技術核心 Proxy類和一個 InvocationHandler 接口
 *
 * @author cruder
 * @date 2019-11-23 16:40
 **/
public class ProxyFactory implements InvocationHandler {

    /**
     * 委託對象,既被代理的對象
     */
    private Object target;

    public ProxyFactory (Object target) {
        this.target = target;
    }

    /**
     * 生成代理對象
     * 1. Classloader loader: 制定當前被代理對象使用的累加子啊其,獲取加載器的方法固定
     * 2. Class<?>[] interfaces: 委託類的接口類型,使用泛型方法確認類型
     * 3. InvocationHandler handler: 事件處理,執行委託對象的方法時會觸發事件處理器方法,
     * 會把當前執行的委託對象方法作為參數傳入
     */
    public Object getProxyInstance() {
        Class clazz = target.getClass();

        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        method.invoke(target, args);
        System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
        return null;
    }
}

/**
 * 通過動態代理方式來保存訂單
 *
 * @author cruder
 * @date 2019-11-23 15:49
 **/
public class Client {
    public static void main(String[] args) throws InterruptedException {
        ProxyFactory proxyFactory= new ProxyFactory (new OrderService());
        IOrderService orderService = (IOrderService) proxyFactory.getProxyInstance();
        orderService.saveOrder(" cruder 新買的花褲衩 ");
    }
}

以上便是jdk動態代理的全部實現,有種只可意會不可言傳的感覺,筆者始終感覺這種實現看起來很彆扭。不過也要強行總結以下,jdk實現動態代理可以分為以下幾個步驟:

  1. 先檢查委託類是否實現了相應接口,保證被訪問方法在接口中也要有定義
  2. 創建一個實現InvocationHandler接口的類
  3. 在類中定義一個被代理對象的成員屬性,為了擴展方便可以直接使用Object類,也可以根據需求定義相應的接口
  4. 在invoke方法中實現對委託對象的調用,根據需求對方法進行增強
  5. 使用Proxy.newProxyInstance(…)方法創建代理對象,並提供要給獲取代理對象的方法

代理類源碼閱讀

上文中基於jdk動態代理的代碼實現中對於可*的產品經理來說已經完全滿足了需求,但是對於具有Geek精神的程序員來說這遠遠不夠,對於這種不知其所以然的東西往往讓人感到不安。接下來我們將通過自定義的一個小工具類將動態生成的代理類保存到本地來一看究竟。

/**
 * 將生成的代理類保存為.class文件的工具類
 *
 * @author cruder
 * @date 2019-08-15 0:27
 */
public class ProxyUtils {
    /**
     * 將代理類保存到指定路徑
     *
     * @param path           保存到的路徑
     * @param proxyClassName 代理類的Class名稱
     * @param interfaces     代理類接口
     * @return
     */
    public static boolean saveProxyClass(String path, String proxyClassName, Class[] interfaces){
        if (proxyClassName == null || path == null) {
            return false;
        }
        // 獲取文件字節碼,然後輸出到目標文件中
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyClassName, interfaces);
        try (FileOutputStream out = new FileOutputStream(path)) {
            out.write(classFile);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

// 此處是重點, 生成的代理類實現了IOrderService,並且繼承了Proxy
public final class $Proxy0 extends Proxy implements IOrderService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void saveOrder(Order var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    static {
        try {
           // 通過反射獲取Method對象
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("cn.mycookies.test08proxy.IOrderService").getMethod("saveOrder", Class.forName("cn.mycookies.test08proxy.Order"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

ps: 實習轉正面試中被問到為什麼jdk動態代理被代理的類為什麼要實現接口?

cglib動態代理

對於cglib我想大多數人應該都很陌生,或者是在學習Spring中AOP(面向切面編程)時聽說了它使用jdk和cglib兩種方式實現了動態代理。接下來筆者將針對cglib進行簡要介紹。

cglib動態代理和jdk動態代理類似,也是採用操作字節碼機制,在運行時生成代理類。cglib 動態代理採取的是創建目標類的子類的方式,因為是子類化,我們可以達到近似使用被調用者本身的效果。

字節碼處理機制-指得是ASM來轉換字節碼並生成新的類

注:spring中有完整的cglib相關的依賴,所以以下代碼基於spring官方下載的demo中直接進行編寫的

/**
 * 1. 訂單服務-委託類,不需要再實現接口
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService {
    /**
     * 保存訂單接口
     */
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}

/**
 * cglib動態代理工廠
 *
 * @author cruder
 * @date 2019-11-23 18:36
 **/
public class ProxyFactory implements MethodInterceptor {

    /**
     * 委託對象, 即被代理對象
      */
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 返回一個代理對象
     * @return
     */
    public Object getProxyInstance(){
        // 1. 創建一個工具類
        Enhancer enhancer = new Enhancer();
        // 2. 設置父類
        enhancer.setSuperclass(target.getClass());
        // 3. 設置回調函數
        enhancer.setCallback(this);
        // 4.創建子類對象,即代理對象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();

        Object result = method.invoke(target, args);

        System.out.println("cglib代理:保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

/**
 * 使用cglib代理類來保存訂單
 *
 * @author cruder
 * @date 2019-11-23 15:49
 **/
public class Client {
    public static void main(String[] args) throws InterruptedException {
        // 1. 創建委託對象
        OrderService orderService = new OrderService();
        // 2. 獲取代理對象
        OrderService orderServiceProxy = (OrderService) new ProxyFactory(orderService).getProxyInstance();
        String saveFileName = "CglibOrderServiceDynamicProxy.class";
        ProxyUtils.saveProxyClass(saveFileName, orderService.getClass().getSimpleName(), new Class[]{IOrderService.class});
        orderServiceProxy.saveOrder(" cruder 新買的花褲衩 ");
    }
}

cglib動態代理實現步驟和jdk及其相似,可以分為以下幾個步驟:

  1. 創建一個實現MethodInterceptor接口的類
  2. 在類中定義一個被代理對象的成員屬性,為了擴展方便可以直接使用Object類,也可以根據需求定義相應的接口
  3. 在invoke方法中實現對委託對象的調用,根據需求對方法進行增強
  4. 使用Enhancer創建生成代理對象,並提供要給獲取代理對象的方法

cglib動態代理生成的代理類和jdk動態代理代碼格式上幾乎沒有什麼區別,唯一的區別在於cglib生成的代理類繼承了僅僅Proxy類,而jdk動態代理生成的代理類繼承了Proxy類的同時也實現了一個接口。代碼如下:

// 生成一個Proxy的子類
public final class OrderService extends Proxy {
    private static Method m1;
    private static Method m2;
    private static Method m0;

    public OrderService(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

jdk動態代理 VS cglib

JDK Proxy 的優勢:

  • 最小化依賴關係,減少依賴意味着簡化開發和維護,JDK 本身的支持,可能比 cglib 更加可靠。
  • 平滑進行 JDK 版本升級,而字節碼類庫通常需要進行更新以保證在新版 Java 上能夠使用。
  • 代碼實現簡單。

cglib 優勢:

  • 有的時候調用目標可能不便實現額外接口,從某種角度看,限定調用者實現接口是有些侵入性的實踐,類似 cglib 動態代理就沒有這種限制。
  • 只操作我們關心的類,而不必為其他相關類增加工作量。

總結

  1. 代理模式: 為其他對象提供一種代理以控制(隔離,使用接口)對這個對象的訪問。
  2. jdk動態代理生成的代理類繼承了Proxy類並實現了被代理的接口;而cglib生成的代理類則僅繼承了Proxy類。
  3. jdk動態代理最大缺點:只能代理接口,既委託類必須實現相應的接口
  4. cglib缺點:由於是通過“子類化”的方式, 所以不能代理final的委託類或者普通委託類的final修飾的方法。

Q&A

  1. 為什麼jdk動態代理只能代理接口?
  2. Spring中AOP的實現採用那種代理方式?
  3. 都說jdk動態代理性能遠比cglib要差,如果是,依據是什麼?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

SpringBoot系列之切換log4j日誌框架

SpringBoot系列之使用切換log4j日誌框架

ok,在pom文件右鍵->Diagrams->show Dependencies….,如圖,找到spring-boot-starter-logging,可以看到SpringBoot的日誌實現默認依賴與logback,ok,如果你對這些知識不是很理解的,建議先看我Springboot專欄的日誌系列博客:

本博客要實現的是切換默認日誌框架為log4j,當然是不建議這樣做的,因為log4j有性能問題,所以其作者才開發了logback,不過作為學習的話,還是可以學一下怎麼切換Springboot默認的日誌框架

先去拿一張圖:圖示,切換日誌框架,為了避免衝突,一般都是先排除日誌框架的實現jar,然後再將之前博客提到的偷梁換柱jar,比如log4j-to-slf4j.jar等等先排除,然後再引入對應的日誌實現jar,如圖所示的slf4j-log4j12.jar,因為本博客並非入門教程,所以學習之前請先參考我之前Springboot日誌方面的博客,再來學習

ok,基於slf4j官方提供的知識,我們就可以實踐了,首先選中logback-classic.jar(logback實現jar)、log4j-to-slf4j.jar(將log4j API強制切換回slf4j的偷梁換柱jar),然後右鍵,選擇exclusion

ok,再次打開pom文件,可以看到idea自動幫我們exclusion一些jar了

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>logback-classic</artifactId>
                    <groupId>ch.qos.logback</groupId>
                </exclusion>
                <exclusion>
                        <artifactId>log4j-to-slf4j</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

ok,避免日誌衝突,exclusion了logback的實現jar和偷梁換柱的log4j-to-slf4j之後,我們還需要引入log4j的實現jar

<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>

ok,這是slf4j官網的說法,但是我發現在一些舊的版本SpringBoot是有提供spring-boot-starter-log4j這個場景啟動器的,所以我們可以更簡便的做log4j引入

直接exclusion spring-boot-starter-logging:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

然後直接引入log4j的場景啟動器,建議加上版本,因為有些版本並沒有提供log4j配置,本博客是換回1.5.7才支持的,2.2.1的版本仲裁都沒提供對應版本的

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>

ok,然後在resources直接丟log4j.properties

# LOG4J rootCategory config
log4j.rootCategory=INFO, stdout, file
# LOG4J console config
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n

# root日誌輸出
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.file=logs/springboot.log
log4j.appender.file.DatePattern='.'yyyy-MM-dd
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n

啟動SpringBoot日誌:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

.Net Core讀取Json配置文件

前言:在與傳統的asp.net MVC項目相比,.net core項目在項目目錄的文件結構上和功能上與前者都有很大的區別。例如:在.net core中使用Startup.cs取代Global.asax文件用於加載應用程序的配置和各種啟動項。appsettings.json取代web.config文件用於存儲應用程序所需的配置參數等等。。。

OK!步入正題,下面來說一下如何讀取Json配置文件中的參數。

第一種:使用IConfiguration接口

我們先在appsettings.json中配置好數據庫連接字符串,然後讀取它

{
  "Connection": {
    "dbContent": "Data Source=.;Initial Catalog=test;User ID=sa;Password=123456"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

 

 

 在控制器中注入IConfiguration接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace Read.json.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ReadController : Controller
    {
        private IConfiguration _configuration;
        public ReadController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpPost]
        public async Task<string> ReadJson()
        {
//讀參
string conn = _configuration["Connection:dbContent"]; return ""; } } }

 

 當然也可以讀取數組形式的json,一樣的先在appsettings.json中寫好配置參數,如下:

{
  "Connection": {
    "dbContent": "Data Source=.;Initial Catalog=test;User ID=sa;Password=123456"
  },

  //------------------------
  "Content": [
    {
      "Trade_name": {
        "test1": "小熊餅乾",
        "test2": "旺仔QQ糖",
        "test3": "娃哈哈牛奶"
      }
    }
  ],
  //------------------------

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

比如我們想讀取test1

  string commodity_test1 = _configuration["Content:0:Trade_name:test1"];

 

 第二種:使用IOptions<T>來讀取json配置文件

先把NuGet包導進項目:Microsoft.Extensions.Options.ConfigurationExtensions

 

 

首先在appsettings.json中添加節點如下

{
  "Connection": {
    "dbContent": "Data Source=.;Initial Catalog=test;User ID=sa;Password=123456"
  },

  //------------------------
  "Content": [
    {
      "Trade_name": {
        "test1": "小熊餅乾",
        "test2": "旺仔QQ糖",
        "test3": "娃哈哈牛奶"
      }
    }
  ],
  //------------------------

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",

  //==============================
  "Information": {
    "school": {
      "Introduce": {
        "Name": "實驗小學",
        "Class": "中班",
        "Number": "15人"
      },
      "Region": {
        "Province": "湖北",
        "City": "武漢",
        "Area": "洪山區"
      },
      "Detailed_address": [
        {
          "Address": "佳園路207號"
        }
      ]
    }
  }
  //==============================
}

 

然和再建立一個與這個節點”相同”的類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Read.json
{
    public class Information
    {
        public School school { get; set; }
    }
    public class School
    {
        public Introduce Introduce { get; set; }

        public Region Region { get; set; }

        public List<Detailed_address> data { get; set; }

    }
    public class Introduce
    {
        public string Name { get; set; }
        public string Class { get; set; }
        public string Number { get; set; }
    }

    public class Region
    {
        public string Province { get; set; }
        public string City { get; set; }
        public string Area { get; set; }
    }
    public class Detailed_address
    {
        public string Address { get; set; }
    }
}

 

在Startup中添加如下代碼

            #region 服務註冊,在控制器中通過注入的形式使用
            services.AddOptions();
            services.Configure<Information>(Configuration.GetSection("Information"));
            #endregion

 

 控制器中使用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

namespace Read.json.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class ReadController : Controller
    {
        private IConfiguration _configuration;

        readonly Information _Information;

        readonly IOptions<Information> _options;
        public ReadController(IConfiguration configuration,
                              Information Information,
                              IOptions<Information> options)
        {
            _configuration = configuration;
            _Information = Information;
            _options = options;
        }

        [HttpGet]
        public async Task<IActionResult> ReadInformation()
        {
            string Address = _options.Value.school.Region.Province + "-" +
                             _options.Value.school.Region.City + "-" +
                             _options.Value.school.Region.Area + "-" +
                             _options.Value.school.Detailed_address[0].Address + "-" +
                             _options.Value.school.Introduce.Name + "-" +
                             _options.Value.school.Introduce.Class + "-" +
                             _options.Value.school.Introduce.Number;
            return Json(Address);
        }

        [HttpPost]
        public async Task<string> ReadJson()
        {
            string conn = _configuration["Connection:dbContent"];
            string commodity = _configuration["Content:0:Trade_name:test1"];
            return "";
        }

    }
}

 

 

 

 

第三種:這種應該比較常見,任意讀取自定義的json文件

首先建立一個json文件

{
  "system_version": {
    "Edition": ".Net Core 3.0",
    "Project_Name": "Read.json"
  }
}

 

 

 再建一個類,封裝一個方法

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Read.json
{
    public class Json_File
    {
        public IConfigurationRoot Read_Json_File()
        {
            //這句代碼會讀取read_json.json中的內容
            return new ConfigurationBuilder().AddJsonFile("read_json.json")
                                             .Build();

        }

    }
}

 

 

 在控制器中調用:

        [HttpGet]
        public async Task<IActionResult> ReadSystemVersion()
        {
            var configuration = _json_File.Read_Json_File();
            string system = "使用的是" + configuration["system_version:Edition"] + "的版本" + "," +
                            "項目名稱是" + configuration["system_version:Project_Name"];
            return Json(new
            {
                data = system
            });
        }

 

 

 

 

Demo地址:Func<Address,Project> func = (address) => git clone address;
var project = func("https://github.com/Davenever/Read_Json.git");

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享