陸去年新能源汽車保有量年增1.7倍,純電動車占57%

大陸公安部交管局公布,截至去(2015)年底,大陸機動車保有量達2.79億輛,其中汽車1.72億輛。在新能源汽車部分,截至去年底,大陸新能源汽車保有量已達58.32萬輛,年增169.48%;其中,純電動汽車保有量33.2萬輛,占56.93%,其保有量與2014年相比增幅達317.06%。

  隨著大陸經濟持續發展,民眾購車剛性需求旺盛,汽車保有量繼續呈快速增長趨勢,去年新註冊登記的汽車達2,385萬輛,保有量淨增1,781萬輛,均為歷史最高水準。汽車占機動車的比率迅速提高,近5年汽車占機動車比率從47.06%提高到61.82%。   大陸全國已有40個城市的汽車保有量超過百萬輛,北京、成都、深圳、上海、重慶、天津、蘇州、鄭州、杭州、廣州、西安等11個城市汽車保有量超過200萬輛。   截至去年底,大陸小型載客汽車達1.36億輛,其中,私家車達1.24億輛,占91.53%;與2014年相比,私家車增加1,877萬輛,增長17.77%。全國平均每百戶家庭擁有31輛私家車,北京、成都、深圳等大城市每百戶家庭擁有私家車超過60輛。   (本文內容由授權使用;首圖來源: CC BY 2.0)

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

特斯拉神秘新車型:Model Y或將今年3月發佈

據外媒報導,特斯拉將會在今年3月的發佈會上同時展示至少兩款不同款式的新汽車。其中,原定計劃的Model 3將是標準款轎車,而另一款神秘汽車則會是運動型SUV系列。

特斯拉首席技術官JB Straubel事實上早在去年6月就展示了一款由Model 3系列演變出的運動型SUV。隨後在10月,CEO伊隆•馬斯克(Elon Musk)也通過Twitter表示,Model 3的衍生型號將會被稱為Model Y。然而對於透露這樣一則資訊,馬斯克很快表示反悔,並迅速刪除了相關推文。

截至目前,有關特斯拉神秘新型號汽車的消息十分有限,尤其是該款車型是否也會隨Model 3同一時間上市。如果Model Y真的是Model 3的衍生版本,那前者極有可能與後者採用同樣底盤,並共用大部分生產線。

Model 3的預設售價為3.5萬美元,一次充電後的有效里程為200英里(約合320公里)。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

電動車生意難做?電池廠三星SDI淨損飆、股價遭熊吻

三星集團的電池大廠三星SDI(Samsung SDI)公布2015年第4季財報,虧損擴大。不過三星SDI的化學部門即將分拆出售給樂天集團(Lotte),或許有助未來獲利。   韓聯社報導,三星SDI 25日盤後公布,去年Q4營收年減2.53%至1.86兆韓圜,營損為808億韓圜。淨損為2,313億韓圜(1.94億美元),虧損高於2014年同期的淨損1,288億美元,也不及Q3出現淨利5,024億韓圜。   此外,三星SDI股東25日同意,將分拆化學部門成立獨立公司,暫名為「SDI Chemical」,其中90%股權將在今年上半之前,出售給Lotte Chemical。去年十月樂天集團以3兆韓圜(25億美元)入股三星SDI化學部門,並購買Samsung Fine Chemicals的31.5%股權。   嘉實XQ全球贏家系統報價顯示,三星SDI 25日上漲0.49%收在102,500韓圜。和去年12月16日波段收盤高點(129,000韓圜),重挫21%之多。   三星集團積極卡位汽車市場,旗下電池大廠三星SDI (Samsung SDI)展示最新的車用電池,充電一次可跑600公里,預定2020年量產。   韓聯社11日報導,三星SDI在底特律車展秀出新的車用電池原型,充電一次可跑600公里,高於先前的500公里,預定2020年量產。該公司估計,2020年全球電動車銷量將增至116萬輛,遠高於2015年的27.8萬輛。   三星SDI已成為純粹的車用電池廠,該公司目前在南韓與中國擁有五座生產線、總產能多達4.5GWh。到了2020年,該公司還打算把產能擴充到35GWh。barron`s.com 去年11月4日報導,Bernstein Research分析師Mark Newman發表研究報告指出,三星SDI在2020年的車用電池產能,正好與特斯拉預定的Gigafactory產能完全一致。   (本文內容由授權使用)

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

【其他文章推薦】

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

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

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

※評比南投搬家公司費用收費行情懶人包大公開

通用汽車組建團隊加速無人駕駛和電動汽車開發

通用汽車本1月21號表示,公司將組建一個團隊加速無人駕駛汽車和電動汽車的開發,通用汽車還會擴充該類產品線。

通用汽車全球產品專案副總裁道格•帕克斯(Doug Parks)將成為無人駕駛技術和汽車業務執行副總裁,他將向全球產品研發主管羅伊斯(Mark Reuss)彙報工作。帕克斯將負責新電動和電池系統、汽車軟體的開發。

在聲明中通用汽車還表示,謝裡•希考克(Sheri Hickok)將擔任無人駕駛合資和車隊實施方面的執行首席工程師,在任命之前希考克是下一代皮卡的首席工程師。此外,通用汽車電動汽車首席設計工程師帕姆•弗雷切(Pam Fletcher)將承擔更多的職責,他將負責無人駕駛和電動汽車的戰略規劃。
 

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

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

常隆客車和AESC公司達成了電動客車錳酸鋰離子電池供應協定

2016年1月14日, 江蘇常隆客車有限公司(江蘇省江陰市)和AESC公司(神奈川縣座間市)達成了電動客車錳酸鋰離子電池供應協定,雙方高層出席了隆重的簽字儀式。本次合作協定初期電池供給規模為30MWh,可以滿足100台12米電動客車的錳酸鋰電池。雙方還預計到2017年可以持續供貨900MWh規模的錳酸鋰電池,滿足3000輛純電動客車生產需求。

通過雙方合作,常隆電動客車將更加安全可靠、經久耐用,努力為市場提供最好的電動客車、最優質的服務、最值得信賴的品牌。同時雙方約定今後持續探討本次合作的動力電池在儲能領域應用的可行性。雙方計畫同步開拓中國市場和國際市場。

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

【其他文章推薦】

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

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

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

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

北汽新能源將推5款新車 全新品牌「眾創」4月發佈

近日, 北京新能源汽車股份有限公司黨委書記、總經理鄭剛表示,北汽新能源規劃推出5款全新車型,其中全新品牌“眾創”車型將於4月份正式發佈。即將推出的5款車型覆蓋A00級、A0級、A級轎車及A級 SUV 領域。

北汽新能源將推出5款全新車型,其中純電動A級車EU260(續航里程260公里)將於2-3月正式上市,接下來純電動A0級SUV—EX200(續航200公里)將於3-4月登陸市場,其後續推出的高里程版續航可達到300公里。在今年的9-10月份,旗下全新A00級“國民車”將上市,該車將進一步拉低入門售價;今年年底,全新“眾創”品牌旗下車型將上市,進一步豐富產品陣容。2017年,北汽新能源將推出全新D級高性能純電動轎車,其續航里程將超過500公里。

據悉,北汽新能源推動的“眾創”專案,將在4月舉行的北京車展上,正式發佈該品牌LOGO、車型及命名等細節。“眾創”品牌的首款車型將於今年底正式上市,該車將採用超輕量化的全鋁框架車身結構設計。據介紹,北汽新能源將彙集全球“電粉”,全面參與到新產品的名稱定義、標識設計甚至軟硬選裝件的創意開發中,打造全球首款眾創汽車,該項目也將作為北汽新能源“衛藍眾創”計畫一部分。

在北汽新能源的規劃中,在2017年將推出更具產品實力的全新高性能D級純電動轎車,新車也將成為北汽新能源的旗艦產品,其續航里程將達到500公里。從定位來看,該車將與特斯拉MODEL S處同一級別。按照此前消息,北汽新能源與Atieva聯合開展的高性能電動汽車設計研發工作正在矽谷緊鑼密鼓地推進之中,新車有望是該款D級轎車。

多款熱銷車型的出現將促使北汽新能源加快對於產業佈局的建設。據悉,北汽新能源將以“1(北京采育基地)+2(常州基地、青島基地)+I(北汽集團內部傳統乘用車生產基地)+P(社會合作夥伴生產資源)>80”為基礎,在十三五末期形成80萬輛以上生產能力,年產銷50萬輛規模,打造年營業收入600億元、上市市值1000億元的企業。

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

【其他文章推薦】

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※南投搬家前需注意的眉眉角角,別等搬了再說!

電動車五日環台之旅,充電設備是最大考驗

由台灣電動車經銷、維修商綠動未來所主辦的第一屆電動車環台Party活動,歷經五日純電動行程,於2月1日順利抵台北101大樓。雖然台灣各地的充電設備分布不均,但這次活動仍證明電動車環島的可行性,主辦單位希望能藉此鼓勵更多民眾加入電動車行列。

綠動未來創辦人劉小麟表示,參加本次環島活動的車款分別是Tesla Model S、Luxgen M7 EV+ 以及Nissan Leaf 輕巧通勤車,皆為純電動車,且Luxgen、Nissan的車款都是都市用車,充飽電後的續航力只有150公里,在環島過程中也因此面臨較大的充電考驗。而Tesla Model S 每次充飽電可行駛480公里,影響相對輕微。

台灣各地充電設備空間分布不均。劉小麟紀錄環台過程的充電狀況,統計台灣東部的宜蘭、花蓮、台東三縣市均無充電設備,西部的新竹、苗栗、雲林、嘉義、高雄、屏東則沒有公共充電站。環台期間,須商借個人充電站協助充電,或者到汽車廠、警察局與消防隊,甚至在投宿的民宿充電。不過,用一般電源充電所需時間長,也因此限制電動車環島之旅的機動性;相較之下,專為電動車設計的快充充電站僅需幾十分鐘就能充飽電池再上路。

在成功完成純電環島之旅後,劉小麟也規劃開始募款,希望能帶著國產的Luxgen M7 EV 到西班牙參加六月即將展開的世界電動車環球賽,挑戰80天、20國、25,000公里的長征之旅。

(照片來源:臉書專頁)

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

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

類型擦除真的能完全擦除一切信息嗎?java 泛型揭秘

背景

我們都知道泛型本質上是提供類型的”類型參數”,它們也被稱為參數化類型(parameterized type)或參量多態(parametric polymorphism)。其實泛型思想並不是 Java 最先引入的,C++ 中的模板就是一個運用泛型的例子。

GJ(Generic Java)是對 Java 語言的一種擴展,是一種帶有參數化類型的 Java 語言。用 GJ 編寫的程序看起來和普通的 Java 程序基本相同,只不過多了一些參數化的類型同時少了一些類型轉換。實際上,這些 GJ 程序也是首先被轉化成一般的不帶泛型的 Java 程序后再進行處理的,編譯器自動完成了從 Generic Java 到普通 Java 的翻譯。

 

什麼是真實的java泛型

我們都知道編譯器會進行泛型擦除,編譯器可以在對源程序(帶有泛型的 Java 代碼)進行編譯時使用泛型類型信息保證類型安全,對大量如果沒有泛型就不會去驗證的類型安全約束進行驗證,同時在生成的字節碼當中,將這些類型信息清除掉。下面我們先驗證一下:

public static void main(String[] args) {
 ArrayList<Integer> ints = new ArrayList<Integer>();
 ints.add(1); 
 ints.add(2);
 ints.add(3);
 
 ArrayList<String> sts = new ArrayList<String>();
 sts.add("a");
 sts.add("b");
 sts.add("c");
 
 System.out.println(ints.getClass() == sts.getClass()); 
 }
上面打印的結果是true,原因是:

按照理解,泛型擦除后將不能找回原來的類型,都是Object形式的,真的如此嗎?

看一下如下代碼:

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class ClassTest {
     public static void main(String[] args) throws Exception {
     ParameterizedType type = (ParameterizedType) 
     Bar.class.getGenericSuperclass();
     System.out.println(type.getActualTypeArguments()[0]);
     
     ParameterizedType fieldType = (ParameterizedType) 
     Foo.class.getField("children").getGenericType();
     System.out.println(fieldType.getActualTypeArguments()[0]);
     
     ParameterizedType paramType = (ParameterizedType) 
     Foo.class.getMethod("foo", List.class)
     .getGenericParameterTypes()[0];
     System.out.println(paramType.getActualTypeArguments()[0]);
     
     System.out.println(Foo.class.getTypeParameters()[0]
     .getBounds()[0]);
     }
     
     class Foo<E extends CharSequence> {
     public List<Bar> children = new ArrayList<Bar>();
     public List<StringBuilder> foo(List<String> foo) {return null; }
     public void bar(List<? extends String> param) {}
     }
     
     class Bar extends Foo<String> {}
    }

打印出

class java.lang.String
class com.javapuzzle.davidwang456.ClassTest$Bar
class java.lang.String
interface java.lang.CharSequence

你會發現每一個類型參數都被保留了,而且在運行期可以通過反射機制獲取到。那麼到底什麼是“類型擦除”?至少某些東西被擦除了吧?是的。事實上,除了結構化信息外的所有東西都被擦除了 —— 這裏結構化信息是指與類結構相關的信息,而不是與程序執行流程有關的。換言之,與類及其字段和方法的類型參數相關的元數據都會被保留下來,可以通過反射獲取到。

參考資料

【1】http://techblog.bozho.net/on-java-generics-and-erasure/

【2】https://www.ibm.com/developerworks/cn/java/j-lo-gj/?mhsrc=ibmsearch_a&mhq=%E7%B1%BB%E5%9E%8B%E6%93%A6%E9%99%A4

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

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

Vue躬行記(8)——Vue Router

  雖然Vue.js未提供路由功能,但是官方推出了Vue Router(即vue-router庫),以插件的形式支持。它與Vue.js深度集成,可快速的創建單頁應用(Single Page Application,SPA)。

一、基本用法

  首先需要引入vue和vue-router兩個庫,如果像下面這樣全局引用(即存在全局變量Vue),那麼vue-router會自動調用Vue.use()方法註冊其自身;但如果以模塊的方式引用,那麼就得顯式地調用Vue.use()。

<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>

  然後添加兩個內置的組件,第一個是導航用的router-link組件,它默認會被渲染成<a>元素,如果要渲染成其它元素,那麼可以使用它的tag屬性;第二個是路徑匹配時用於渲染視圖的router-view組件,它還是一個函數式組件,如下所示。

<div id="container">
  <router-link to="/main">首頁</router-link>
  <router-link to="/list">列表</router-link>
  <router-view></router-view>
</div>

  再聲明要渲染的組件,這些組件會與指定的路由映射,下面是兩個非常簡單的自定義組件。

const Main = { template: '<div>首頁</div>' };
const List = { template: '<div>列表</div>' };

  接着定義路由配置,添加一組路徑以及對應的組件,如下所示。

const routes = [
  { path: '/main', component: Main },
  { path: '/list', component: List }
];

  最後創建路由器實例,並將之前的路由配置傳遞進來,在掛載根實例時,需要將它注入,從而讓整個應用都有路由功能,如下所示。

const router = new VueRouter({
  routes: routes
});
var vm = new Vue({
  el: "#container",
  router: router
});

  注意,router實例包含三個導航方法:push()、replace()和go(),可以用編程的方式進行導航。

  頁面在渲染完成后,得到的DOM結構如下所示,此時router-view組件所佔的位置無任何內容。

<div id="container">
  <a href="#/main" class="">首頁</a>
  <a href="#/list" class="">列表</a>
</div>

  當router-link組件所對應的路由匹配成功時,其渲染出的元素會被自動添加一個CSS類:router-link-active。例如點擊首頁鏈接,得到的結構如下所示,注意,此時router-view組件被替換成了Main組件中的內容。

<div id="container">
  <a href="#/main" class="router-link-active">首頁</a>
  <a href="#/list" class="">列表</a>
  <div>首頁</div>
</div>

二、路由模式

  Vue Router默認採用URL hash模式來保持頁面和URL的同步,其創建的URL格式需要包含井號(#),如下所示。

http://pwstrick.com/#/main

  Vue Router還有另外一種history模式,利用HTML5 History來保持頁面和URL的同步,其創建的URL格式在視覺上更為簡潔清晰,如下所示。

http://pwstrick.com/main

  如果要開啟history模式,那麼需要在路由配置時添加mode選項,並賦予history關鍵字,如下所示(直接修改了上一節中的示例)。

const router = new VueRouter({
  mode: "history",
  routes: routes
});

  注意,當直接訪問history模式創建的URL時,會返回一個404頁面。為了避免這種情況的發生,建議在服務器上配置一個默認的候選頁面。

三、路由

  本節不僅會介紹動態路由的概念,還會分析路由的命名和嵌套等操作。

1)動態路由

  Vue Router採用的路徑匹配引擎是path-to-regexp,它支持動態路由的匹配,例如有一個List組件,需要根據路徑中的頁碼參數把組件渲染成不同的內容,可以像下面這樣配置。

const List = { template: '<div>{{$route.params.page}}</div>' };
const routes = [
  { path: '/list/:page', component: List }
];

  在路由配置中,以冒號開頭的:page是路徑參數,它的值會被保存到路由對象$route的params屬性中。路由對象不僅包含解析URL得到的信息(例如路徑、查詢字符串、錨點等),還有匹配到的路由信息(例如路由名稱、路由記錄等)。當路徑是/list/1時,$route.params的值為{page: 1},params是一個對象,其鍵值與路徑參數對應。注意,一條路徑可以包含多個不同的路徑參數。

  當一個路徑能匹配多個路由時,匹配優先級會按照路由的定義順序來排,即先定義的,優先級高。下面的配置包含兩個路由,當路徑是/list/1時,只會渲染List組件。

const routes = [
  { path: '/list/:page', component: List },
  { path: '/list/1', component: Main }
];

2)命名路由

  可以在配置時為路由標識一個名稱,從而就能在使用時省略路徑了,如下所示,name屬性的值就是路由名稱。

const routes = [
  { path: '/list/:page', component: List, name: "list" }
];

  如果要導航到一個命名路由,那麼需要動態地綁定to屬性,併為其傳入路由的名稱以及可選的URL參數,如下所示。

<router-link :to="{ name: 'list', params: {page: 3} }">第三頁</router-link>

3)嵌套路由

  Vue Router允許嵌套路由,可通過多層嵌套的組件實現。下面是一個使用示例,首先在一個組件中聲明router-view視圖組件,如下所示。

const List = { template: '<router-view></router-view>' };
const Detail = { template: '<div>詳情</div>' };

  然後在路由配置中,添加children選項,並定義子路由。

const routes = [
  {
    path: "/list",
    component: List,
    children: [{ path: "detail", component: Detail }]
  }
];

  在經過這一系列操作后,當路徑是/list/detail時,Detail組件就會被渲染到List組件的視圖中。

  嵌套路由可用來渲染多個不同層級的視圖,而利用命名視圖可渲染多個同級的視圖。在下面的代碼中,為第二個router-view組件添加了name屬性,賦予它一個名稱,而第一個router-view組件的默認名稱為default。

<router-view></router-view>
<router-view name="detail"></router-view>

  在路由配置時,新增用於記錄各個命名組件的components選項,如下所示。

const routes = [
  {
    path: "/named",
    components: {
      default: Main,
      detail: Detail
    }
  }
];

  當路徑是/named時,Main和Detail兩個組件會被同時渲染。

四、重定向和別名

1)重定向

  在路由配置中,通過redirect選項可重定向到一個新的頁面,該選項可取的值有三種,分別是路徑、命名路由和回調函數,如下所示,其中函數中的to參數表示目標路由對象。

const routes = [
  { path: "/list/1", redirect: "/main" },
  { path: "/list/1", redirect: {name: "main"} },
  { path: "/list/1", redirect: to => "/main" }
];

  當訪問的路徑是/list/1時,瀏覽器的地址欄會變成/main,匹配的將是路徑為/main的路由。

2)別名

  在路由配置中,通過alias選項可定義路由的別名,如下所示。

const routes = [
  { path: "/list/4", component: Detail, alias: "/detail" }
];

  當訪問的路徑是/main時,瀏覽器的地址欄保持為/main,但匹配的將是路徑為/list/4的路由。

五、組件傳參

  除了在組件中通過$route.params獲取路由參數之外,還能將路由參數作為組件的props傳入,從而將組件和路由解耦,如下所示。

const List = {
  props: ["page"],
  template: "<div>{{page}}</div>"
};
const routes = [
  { path: "/list/:page", component: List, props: true }
];

  需要注意的是,在路由配置時要添加props選項,並賦予true后,才會讓路由參數和props關聯。

六、導航守衛

  導航守衛就是路由發生變化時的鈎子函數,Vue Router提供了三類守衛:全局、路由獨享和組件級。

1)全局守衛

  在路由器實例上可註冊三個全局守衛,分別是beforeEach()、beforeResolve()和afterEach()。

  beforeEach()是一個全局前置守衛,包含三個參數(如下所示),其中to是目標路由對象、from是來源路由對象,next是一個回調函數,用於解析(resolve)當前守衛。

router.beforeEach((to, from, next) => {
  next();
});

  由於導航在所有守衛解析之前會處於等待狀態(即不會改變路徑,也不會渲染對應的組件),因此在回調中不能省略next()函數。注意,該函數不僅能處理導航,還能將其中斷或重定向,甚至註冊一個錯誤。

  beforeResolve()是一個全局解析守衛,其參數和beforeEach()的相同,在導航被確認前,並且組件內的守衛和異步路由組件被解析之後調用。afterEach()是一個全局後置守衛,它只有to和from兩個參數。

2)路由守衛

  在路由配置中可直接定義beforeEnter()守衛,作用於某個單獨的路由而非全局,其參數和beforeEach()的相同,如下所示。

const routes = [
  {
    path: "/list",
    component: List,
    beforeEnter: (to, from, next) => {
      next();
    }
  }
];

3)組件守衛

  組件內的導航守衛包含三個,如下所列,它們的參數都和beforeEach()的相同,也是to、from和next。

  (1)beforeRouteEnter():由於該組件在導航確認前被調用,因此組件還未被創建,從而訪問this無法得到組件實例。但next()函數能接收一個參數為組件實例的回調,如下所示。

const List = {
  beforeRouteEnter: function(to, from, next) {
    next(vm => { 
      //vm是組件實例
    });
  }
};

  (2)beforeRouteUpdate():可通過this得到組件實例,當路由發生變化,但復用了一個組件時,會調用該守衛。例如一個路徑為/list/:page的路由(如下配置所示),在將/list/1導航至list/2時,由於渲染的都是List組件,因此會調用beforeRouteUpdate()守衛。

const routes = [
  {
    path: "/list/:page",
    component: List
  }
];

  (3)beforeRouteLeave():也可通過this得到組件實例,當路由改變,並且渲染的組件不同時,會調用該守衛。例如有兩條路徑/main和/list,分別對應Main和List組件,在List組件中聲明了beforeRouteLeave()守衛(如下所示),當從/list導航至/main時,會彈出確認框,判斷是否需要取消本次導航。

const List = {
  beforeRouteLeave: function(to, from, next) {
    const answer = window.confirm("是否離開當前頁?");
    answer ? next() : next(false);
  }
};

4)解析流程

  完整的導航解析流程如下所列。

(1)導航被觸發。

(2)在失活的組件里調用beforeRouteLeave()守衛。

(3)調用全局的beforeEach()守衛。

(4)在重用的組件里調用beforeRouteUpdate()守衛。

(5)在路由配置里調用beforeEnter()守衛。

(6)解析異步路由組件。

(7)在被激活的組件里調用beforeRouteEnter()守衛。

(8)調用全局的beforeResolve()守衛。

(9)導航被確認。

(10)調用全局的afterEach()守衛。

(11)觸發DOM更新。

(12)用創建好的實例調用beforeRouteEnter()守衛中傳給next參數的回調函數。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

手把手教你實現熱更新功能,帶你了解 Arthas 熱更新背後的原理

文章來源:

一、前言

一天下午正在摸魚的時候,測試小姐姐走了過來求助,說是需要改動測試環境 mock 應用。但是這個應用一時半會又找不到源代碼存在何處。但是測試小姐姐的活還是一定要幫,突然想起了 Arthas 可以熱更新應用代碼,按照網上的步驟,反編譯應用代碼,加上需要改動的邏輯,最後熱更新成功。對此,測試小姐姐很滿意,並表示下次會少提 Bug。

嘿嘿,以前一直對熱更新背後原理很好奇,藉著這個機會,研究一下熱更新的原理。

二、Arthas 熱更新

我們先來看下 Arthas 是如何熱更新的。

詳情參考:

假設我們現在有一個 HelloService 類,邏輯如下,現在我們使用 Arthas 熱更新代碼,讓其輸出 hello arthas。

public class HelloService {

    public static void main(String[] args) throws InterruptedException {

        while (true){
            TimeUnit.SECONDS.sleep(1);
            hello();
        }
    }

    public static void hello(){
        System.out.println("hello world");
    }

}

2.1、jad 反編譯代碼

首先運行 jad 命令反編譯 class 文件獲取源代碼,運行命令如下:。

jad --source-only com.andyxh.HelloService > /tmp/HelloService.java

2.2、修改反編譯之後的代碼

拿到源代碼之後,使用 VIM 等文本編輯工具編輯源代碼,加入需要改動的邏輯。

2.3、查找 ClassLoader

然後使用 sc 命令查找加載修改類的 ClassLoader,運行命令如下:

$ sc -d  com.andyxh.HelloService | grep classLoaderHash
 classLoaderHash   4f8e5cde

這裏運行之後將會得到 ClassLoader 哈希值。

2.4、 mc 內存編譯源代碼

使用 mc 命令編譯上一步修改保存的源代碼,生成最終 class 文件。

$ mc -c 4f8e5cde  /tmp/HelloService.java  -d /tmp
Memory compiler output:
/tmp/com/andyxh/HelloService.class
Affect(row-cnt:1) cost in 463 ms.

2.5、redefine 熱更新代碼

運行 redefine 命令:

$ redefine /tmp/com/andyxh/HelloService.class
redefine success, size: 1

熱更新成功之後,程序輸出結果如下:

一般情況下,我們本地將會有源代碼,上面的步驟我們可以進一步省略,我們可以先在自己 IDE 上改動代碼,編譯生成 class 文件。這樣我們只需要運行 redefine 命令即可。也就是說實際上起到作用只是 redefine 。

三、 Instrumentation 與 attach 機制

Arthas 熱更新功能看起來很神奇,實際上離不開 JDK 一些 API,分別為 instrument API 與 attach API。

3.1 Instrumentation

Java Instrumentation 是 JDK5 之後提供接口。使用這組接口,我們可以獲取到正在運行 JVM 相關信息,使用這些信息我們構建相關監控程序檢測 JVM。另外, 最重要我們可以替換和修改類的,這樣就實現了熱更新。

Instrumentation 存在兩種使用方式,一種為 pre-main 方式,這種方式需要在虛擬機參數指定 Instrumentation 程序,然後程序啟動之前將會完成修改或替換類。使用方式如下:

java -javaagent:jar Instrumentation_jar -jar xxx.jar

有沒有覺得這種啟動方式很熟悉,仔細觀察一下 IDEA 運行輸出窗口。

另外很多應用監控工具,如:zipkin、pinpoint、skywalking。

這種方式只能在應用啟動之前生效,存在一定的局限性。

JDK6 針對這種情況作出了改進,增加 agent-main 方式。我們可以在應用啟動之後,再運行 Instrumentation 程序。啟動之後,只有連接上相應的應用,我們才能做出相應改動,這裏我們就需要使用 Java 提供 attach API。

3.2 Attach API

Attach API 位於 tools.jar 包,可以用來連接目標 JVM。Attach API 非常簡單,內部只有兩個主要的類,VirtualMachine 與 VirtualMachineDescriptor。

VirtualMachine 代表一個 JVM 實例, 使用它提供 attach 方法,我們就可以連接上目標 JVM。

 VirtualMachine vm = VirtualMachine.attach(pid);

VirtualMachineDescriptor 則是一個描述虛擬機的容器類,通過該實例我們可以獲取到 JVM PID(進程 ID),該實例主要通過 VirtualMachine#list 方法獲取。

        for (VirtualMachineDescriptor descriptor : VirtualMachine.list()){

            System.out.println(descriptor.id());
        }

介紹完熱更新涉及的相關原理,接下去使用上面 API 實現熱更新功能。

四、實現熱更新功能

這裏我們使用 Instrumentation agent-main 方式。

4.1、實現 agent-main

首先需要編寫一個類,包含以下兩個方法:

public static void agentmain (String agentArgs, Instrumentation inst);          [1]
public static void agentmain (String agentArgs);            [2]

上面的方法只需要實現一個即可。若兩個都實現, [1] 優先級大於 [2],將會被優先執行。

接着讀取外部傳入 class 文件,調用 Instrumentation#redefineClasses,這個方法將會使用新 class 替換當前正在運行的 class,這樣我們就完成了類的修改。

public class AgentMain {
    /**
     *
     * @param agentArgs 外部傳入的參數,類似於 main 函數 args
     * @param inst
     */
    public static void agentmain(String agentArgs, Instrumentation inst) {
        // 從 agentArgs 獲取外部參數
        System.out.println("開始熱更新代碼");
        // 這裏將會傳入 class 文件路徑
        String path = agentArgs;
        try {
            // 讀取 class 文件字節碼
            RandomAccessFile f = new RandomAccessFile(path, "r");
            final byte[] bytes = new byte[(int) f.length()];
            f.readFully(bytes);
            // 使用 asm 框架獲取類名
            final String clazzName = readClassName(bytes);

            // inst.getAllLoadedClasses 方法將會獲取所有已加載的 class
            for (Class clazz : inst.getAllLoadedClasses()) {
                // 匹配需要替換 class
                if (clazz.getName().equals(clazzName)) {
                    ClassDefinition definition = new ClassDefinition(clazz, bytes);
                    // 使用指定的 class 替換當前系統正在使用 class
                    inst.redefineClasses(definition);
                }
            }

        } catch (UnmodifiableClassException | IOException | ClassNotFoundException e) {
            System.out.println("熱更新數據失敗");
        }


    }

    /**
     * 使用 asm 讀取類名
     *
     * @param bytes
     * @return
     */
    private static String readClassName(final byte[] bytes) {
        return new ClassReader(bytes).getClassName().replace("/", ".");
    }
}

完成代碼之後,我們還需要往 jar 包 manifest 寫入以下屬性。

## 指定 agent-main 全名
Agent-Class: com.andyxh.AgentMain
## 設置權限,默認為 false,沒有權限替換 class
Can-Redefine-Classes: true

我們使用 maven-assembly-plugin,將上面的屬性寫入文件中。

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <!--指定最後產生 jar 名字-->
        <finalName>hotswap-jdk</finalName>
        <appendAssemblyId>false</appendAssemblyId>
        <descriptorRefs>
            <!--將工程依賴 jar 一塊打包-->
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifestEntries>
                <!--指定 class 名字-->
                <Agent-Class>
                    com.andyxh.AgentMain
                </Agent-Class>
                <Can-Redefine-Classes>
                    true
                </Can-Redefine-Classes>
            </manifestEntries>
            <manifest>
                <!--指定 mian 類名字,下面將會使用到-->
                <mainClass>com.andyxh.JvmAttachMain</mainClass>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id> <!-- this is used for inheritance merges -->
            <phase>package</phase> <!-- bind to the packaging phase -->
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

到這裏我們就完成熱更新主要代碼,接着使用 Attach API,連接目標虛擬機,觸發熱更新的代碼。

public class JvmAttachMain {
    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        // 輸入參數,第一個參數為需要 Attach jvm pid 第二參數為 class 路徑
        if(args==null||args.length<2){
            System.out.println("請輸入必要參數,第一個參數為 pid,第二參數為 class 絕對路徑");
            return;
        }
        String pid=args[0];
        String classPath=args[1];
        System.out.println("當前需要熱更新 jvm pid 為 "+pid);
        System.out.println("更換 class 絕對路徑為 "+classPath);
        // 獲取當前 jar 路徑
        URL jarUrl=JvmAttachMain.class.getProtectionDomain().getCodeSource().getLocation();
        String jarPath=jarUrl.getPath();

        System.out.println("當前熱更新工具 jar 路徑為 "+jarPath);
        VirtualMachine vm = VirtualMachine.attach(pid);//7997是待綁定的jvm進程的pid號
        // 運行最終 AgentMain 中方法
        vm.loadAgent(jarPath, classPath);
    }
}

在這個啟動類,我們最終調用 VirtualMachine#loadAgent,JVM 將會使用上面 AgentMain 方法使用傳入 class 文件替換正在運行 class。

4.2、運行

這裏我們繼續開頭使用的例子,不過這裏加入一個方法獲取 JVM 運行進程 ID。

public class HelloService {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(getPid());
        while (true){
            TimeUnit.SECONDS.sleep(1);
            hello();
        }
    }

    public static void hello(){
        System.out.println("hello world");
    }

    /**
     * 獲取當前運行 JVM PID
     * @return
     */
    private static String getPid() {
        // get name representing the running Java virtual machine.
        String name = ManagementFactory.getRuntimeMXBean().getName();
        System.out.println(name);
        // get pid
        return name.split("@")[0];
    }

}

首先運行 HelloService,獲取當前 PID,接着複製 HelloService 代碼到另一個工程,修改 hello 方法輸出 hello agent,重新編譯生成新的 class 文件。

最後在命令行運行生成的 jar 包。

HelloService 輸出效果如下所示:

4.3、調試技巧

普通的應用我們可以在 IDE 直接使用 Debug 模式調試程序,但是上面的程序無法直接使用 Debug。剛開始運行的程序碰到很多問題,無奈之下,只能選擇最原始的辦法,打印錯誤日誌。後來查看 arthas 的文檔,發現上面一篇文章介紹使用 IDEA Remote Debug 模式調試程序。

首先我們需要在 HelloService JVM 參數加入以下參數:

-Xrunjdwp:transport=dt_socket,server=y,address=8001  

此時程序將會被阻塞,直到遠程調試程序連接上 8001 端口,輸出如下:

然後在 Agent-main 這個工程增加一個 remote 調試。

圖中參數如下:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8001

在 Agent-main 工程打上斷點,運行遠程調試, HelloService 程序將會被啟動。

最後在命令行窗口運行 Agent-main 程序,遠程調試將會暫停到相應斷點處,接下來調試就跟普通 Debug 模式一樣,不再敘述。

4.4、相關問題

由於 Attach API 位於 tools.jar 中,而在 JDK8 之前 tools.jar 與我們常用JDK jar 包並不在同一個位置,所以編譯與運行過程可能找不到該 jar 包,從而導致報錯。

如果 maven 編譯與運行都使用 JDK9 之後,不用擔心下面問題。

maven 編譯問題

maven 編譯過程可能發生如下錯誤。

解決辦法為在 pom 下加入 tools.jar 。

        <dependency>
            <groupId>jdk.tools</groupId>
            <artifactId>jdk.tools</artifactId>
            <scope>system</scope>
            <version>1.6</version>
            <systemPath>${java.home}/../lib/tools.jar</systemPath>
        </dependency>

或者使用下面依賴。

        <dependency>
            <groupId>com.github.olivergondza</groupId>
            <artifactId>maven-jdk-tools-wrapper</artifactId>
            <version>0.1</version>
            <scope>provided</scope>
            <optional>true</optional>
        </dependency>

程序運行過程 tools.jar 找不到

運行程序時拋出 java.lang.NoClassDefFoundError,主要原因還是系統未找到 tools.jar 導致。

在運行參數加入 -Xbootclasspath/a:${java_home}/lib/tools.jar,完整運行命令如下:

4.5、熱更新存在一些限制

並不是所有改動熱更新都將會成功,當前使用 Instrumentation#redefineClasses 還是存在一些限制。我們僅只能修改方法內部邏輯,屬性值等,不能添加,刪除方法或字段,也不能更改方法的簽名或繼承關係。

五、彩蛋

寫完熱更新代碼,收到一封系統郵件提示 xxx bug 待修復。恩,說好的少提 Bug 呢 o(╥﹏╥)o。

六、幫助

歡迎關注我的公眾號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※評比南投搬家公司費用收費行情懶人包大公開