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地圖已可更新顯示潭子電動車充電站設置地點!!

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

EV商機夯!Toray電池材料傳大增產、擬在美國建新廠

日經新聞、日刊工業新聞1日報導,Toray計畫在2020年結束前總計砸下約1,200億日圓擴增車用鋰離子電池關鍵材料「分隔膜(separator、見附圖)」產能,目標將分隔膜年產能擴增至19.5億平方公尺、將達現行(約4億平方公尺)的約5倍。Toray目前在日本及南韓生產分隔膜,且已在南韓工廠進行增產工程,計畫在2017年度末(2018年3月底)將分隔膜年產能提高至約6.5億平方公尺。

報導指出,因法國、英國紛紛表明計畫在2040年停售汽柴油車,提振電動車(EV)有望急速普及,故Toray計畫在2019年於歐洲興建一座年產能達8,000萬平方公尺的新工廠,且除了歐洲之外,Toray也計畫在特斯拉(Tesla)等EV廠抬頭的美國興建新工廠。

特斯拉平價電動車「Model 3」於7月28日正式交車。而為了因應特斯拉增產所需,住友金屬礦山(Sumitomo Metal Mining)於7月28日宣布,將追加增產鋰離子電池正極材料「鎳酸鋰」,目標將其月產能擴增至現行的2.5倍。

鋰離子電池4大關鍵材料分別為正極材、負極材、分隔膜和電解液,而這些電池材料皆由日系廠商握有高市佔率,其中,在全球分隔膜市場上,Toray為第2大廠、僅次於旭化成(Asahi Kasei)。

日刊工業新聞6月23日報導,因車廠加快電動車(EV)的研發腳步、帶動電池材料市場成長速度超乎預期,故旭化成計畫上修鋰離子電池關鍵材料「分隔膜」的增產計畫,目標在2020年結束前將分隔膜年產能最高擴增至15億平方公尺(m2)、將達現行的2.5倍,且將遠高於原先規劃的11億m2目標,期望藉由積極投資、鞏固全球龍頭位置。預估追加擴產所需的投資額約300億日圓。

旭化成於3月30日宣布,因電動車(EV)、油電混合車(HV)等車用鋰離子電池需求預估將呈現急速增長,故決議擴增鋰離子電池關鍵材料「分隔膜」產能,計畫投下約150億日圓,在守山製造所(滋賀縣守山市)增設年產能約2億平方公尺(m2)的分隔膜產線,並預計於2019年度上半年商轉。旭化成指出,待上述增產工程完工後,該公司整體分隔膜年產能將從現行的約6.6億m2提高3成至約8.6億m2。

根據日本市調機構富士經濟(Fuji Keizai)預估,2020年全球分隔膜市場規模將增至3,000億日圓、將達2015年的2倍水準,而EV、HV等車用用途是推動分隔膜需求急增的最大功臣,預估2020年車用分隔膜佔整體市場比重將達約45%。

富士經濟6月22日公布調查報告指出,預估2030年時EV年銷售量將增至407萬台、超越HV(2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。富士經濟預估,在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

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

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

【其他文章推薦】

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

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

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

特斯拉掀21700圓柱形電池風潮,中國電池企業加速佈局

上證報報導,7月29日,特斯拉首批量產Model 3完成交付;220英里至310英里(約合354公里至499公里)的續航能力、3.5萬美元起的銷售價格,讓Model 3成為特斯拉至今為止價格最親民的車型;而能讓特斯拉推出如此平民級電動車的關鍵,在於其使用升級版電池。業內人士預測,21700型號電池可望在未來成為市場主流方案。

今年初,特斯拉宣佈與松下在超級工廠量產用於Model 3中的21700型號電池(其中的「21」指電池直徑為21mm,「70」指長度為70mm,「0」代表圓柱體型的電池)。據瞭解,特斯拉使用的21700型電池系統的能量密度在300Wh/kg左右,比原來Model S使用的18650電池能量密度提升20%以上,單體容量提升35%,系統成本降低10%左右。

業內分析人士指出,相較其他型號,除了保持了18650型電池所具有的高可靠性和穩定性,21700型還兼具了經濟性。物理尺寸的增加不僅可以提升能量密度,同時也會影響到循環壽命等性能。根據有關測算,容量每提升10%,循環壽命約會降低20%,不斷加大尺寸,安全性上也存在風險;而21700型可能是目前可量產電池中能量密度最高且成本最低的電池。

因此,自特斯拉宣佈Model 3車型上使用21700型號電池後,三星SDI也宣佈將推出21700型電池,並表示該電池將在2021年進入規模化生產階段。在國際巨頭的帶動下,中國電池企業也加速了對21700型電池的佈局。

億緯鋰能7月底舉行了湖北金泉二區投產啟動儀式,是中國目前自動化程度最高的動力圓柱鋰離子電池生產線,亦是中國投產的首條21700型號電池生產線;公司預計,今年第三季末將達到共計3.5GWh的圓柱電池產能規模。此外,鋰電巨頭力神電池近日舉行了21700型鋰離子動力電池新品發佈會;按照戰略規劃,華東產業基地是力神電池在十三五期間的投資重點,主要產品為新能源汽車用鋰離子動力電池。

業內人士認為,Model 3作為特斯拉主打的平民級電動車,其量產所具備的意義可比肩iPhone 4當時在手機行業引領的趨勢;隨著特斯拉對21700型電池的率先啟用,21700型電池可望在未來成為市場主流方案,而億緯鋰能、力神電池等一批率先佈局的企業,將不斷加大其在行業競爭中的話語權,市場份額有望不斷擴大。

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

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

【其他文章推薦】

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

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

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

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

上海新能源汽車展8月23舉行 多款PHEV將亮相

由充電設施線上網、廣東省充電設施協會、廣東省新能源汽車產業協會和振威展覽股份聯合主辦,中國土木工程學會城市公共交通學會協辦的“2017上海國際新能源汽車產業博覽會”將於8月23-25日在上海新國際博覽中心隆重舉行,500多家企業將展出各類新能源汽車、電池、電機、電控和充電設施相關產品。其中,上汽、吉利等大牌車企將攜多款PHEV代表性車型亮相。

PHEV(混合動力汽車)因其高續航里程、有較強的節油減排能力,並且可獲得國家財政補貼而受廣大人民群眾的喜歡。據公開資料顯示,2016年,我國共銷售PHEV80352輛,2017年上半年銷售33000輛。雖然PHEV的產銷量並沒有純電動汽車可觀,但是這並不代表PHEV沒有發展潛力。

 

日前,為了推動我國新能源汽車發展的長效機制,促進新能源汽車的研發和推廣,加強汽車產品節能減排的管理,工信部發佈了《乘用車企業平均燃料消耗量與新能源積分並行管理辦法》(以下簡稱雙積分辦法)。雙積分辦法中對PHEV的積分形式有了明確的規定,也就是說,車企想要獲得積分,不僅僅是純電動汽車這一條路,PHEV同樣能達到積分的目的。另一方面,發展PHEV車型,還可以給消費者提供更多的車型選擇以迎合中國汽車市場消費者需求。

 

此外,插電式混合動力產品對於我國現有汽車車型結構優化,同時對汽車節能將發揮重大意義,利於我國汽車工業的節能減排。同時,混合動力車型對於實現油耗目標值具有較強可行性,尤其是節油效率達40~60%的PHEV車型。因此,在當前政策法規環境下,PHEV車型既能擴大新能源汽車產品結構比例,又能提升單車的節油率,某種意義上,發展PHEV車型是必然。

 

本屆展會上,上汽集團旗下榮威品牌將攜eRX5、ei6和e950等三款PHEV車型亮相。eRX5為一款SUV車型,綜合工況純電續駛里程60KM,油電合計續航里程達650KM,此款車型榮獲DESIGN AWARD2017設計大獎;ei6為一款轎車型,補貼後售價為16.58萬元,綜合工況純電續航里程53KM,油電合計續航里程達705KM;e950綜合工況油耗1.7L/100KM,綜合工況純電續駛里程60KM,油電合計續航里程超過600KM。

 

 

此外,吉利將攜帝豪PHEV亮相本次展會。此次展示是帝豪PHEV進入上海新能源汽車目錄後首度亮上海。帝豪PHEV在中控部分增加了旋鈕式檔位,使車輛可在純電動、混合動力等模式中進行切換,增強了操作的便利性及駕駛體驗。該款車型綜合工況純電續駛里程為61KM,油電合計續駛里程達705公里。

 

 

儘管國內堅持發展新能源車須以純電驅動為主要戰略取向,PHEV車型的發展不受《關於完善汽車投資項目管理的意見》重視,但是車企仍需要抓住這兩年的黃金發展時間,堅持開源,多生產插電式混合動力車型。既要滿足企業所面對的雙積分政策,同時解決市場對新能源汽車續駛里程的高期望,補貼減少對新能源汽車產業產生的負面效應等痛點。因此,本次展會的舉辦對於新能源汽車混合動力產品具有積極的展示作用。促進各界人士對PHEV產品的認識及瞭解。

 

參觀預登記,請點擊:http://www.zhenweiexpo.com/visitors/evse/

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

【其他文章推薦】

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

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

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

我姓沈經營公司就是省!Thunder Power 不畏外界流言宣誓在2019 年如期推出電動車

由於是上櫃公司,再加上市場上流傳的流言不斷,面對媒體的鏡頭,Thunder Power 控股有限公司創始人既淳紳股份有限公司董事長沈瑋,用句遣詞相當小心,免得不小被證期會找到喝咖啡。但沈董仍然很有信心的回應媒體的問題,像是被質疑研發經費偏低,沈董霸氣的回應:「我姓沈,經營公司當然就是省了!」

隨著全球人增加,對於行的要求越來越高,各個都會區住民選擇開車通勤。各地政府對傳統汽油車的排氣相當頭痛,車子排氣造成城市內的污染加據。提倡電動車成為不少國家減少都會區空污的手段。

▲Thunder Power Sedan 跑車的性能諸元。

既然臺灣沒辦法在電動車領域當領頭羊,沈董表示身為亞洲的中國人,會把臺灣當首發的市場,再砲才是香港、澳門、中國,最後才是歐洲。

▲ 沈董將禪的哲學帶入Thunder Power 的車體設計裡。

2018 年第一季會推出488 台限量版Thunder Power 跑車,而2019 年則會有第一批Thunder Power 上市。Thunder Power 的SUV 產品也在規畫,將在今年法蘭克福車展現身。

▲Thunder Power 將在2018 年第一季推出限量版488 台車子。

上週Thunder Power 與桃園市政府簽約,將在觀音設廠生產Thunder Power 所需要的電池包,預計開始量產。另外Thunder Power 還會繼續在桃園觀音尋覓適合地點,設置Thunder Power 的汽車裝配廠。

▲ 目前Thunder Power 在全球三個地方,中國贛州、台灣觀音、西班牙加泰隆尼亞設工廠。

 

沈董認為政府要鼓勵電動車的推動,而不是現在效果不彰的補助措施。而他已經跟桃園市長鄭文燦,以及想要參選臺北市長的羅智強講過上述問題,希望他們能發揮影響力,讓政策更為有利電動車推廣。

▲Thunder Power 在全球的銷售佈局,中國贛州負責中國市場,
而巴賽隆納則是歐洲市場,台灣觀音廠則是負責供應台灣市場。

沈董對台灣的公務員相當感冒,像是被櫃買中心、證期會找麻煩。先前因為市場傳信指沒有產品在做假,財報狗則指研發經費比重很怪,導致證期會甚至還要求看Thunder Power 技術。沈董很霸氣說他研發經費就是低啊,他姓沈研發經費當然省了。他很少因為走入錯誤的研發方向導致損失金錢,即便有錯誤方向也會趕緊止血。

▲Thunder Power 車子裡的內裝,佈滿面版。

外界有人把Thunder Power 視為臺灣版的特斯拉(Tesla)。但沈董表示兩者公司文化很不同。特斯拉的CEO Elon Musk 很有個人魅力,公司基本上繞著他走。而Thunder Power 則強調團隊精神

即便在沈董強調自己並未賣掉自己持有的任何股份。談到自己少將父親與蔣經國親善,曾因當兩岸密使被李登輝抓起來,落寞的表情只說他父親的事情催生正式的交涉組織海基會。看來要到2019 年Thunder Power 如期推出電動車,才能消彌看衰者負面的看法了。

(合作媒體:。圖片出處:科技新報)

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

【其他文章推薦】

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

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

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

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