LG電子將為一汽提供電池組等電動汽車核心配件

據《朝鮮日報》報導,LG電子與一汽成功簽訂合作合同,將為一汽提供電池組、變流器和推動器等電動車核心配件。

LG電子提供的電池組能夠儲存電動汽車所需的動力,變流器能夠把電池的直流電轉換成交流電,推動器則是發動機等各種動力裝置。

LG之前曾與東風和吉利兩家中國車企簽署配件供應合同。LG沒有公開具體供應細節,但可以看出在電動車市場急劇增長的中國市場取得了不少成果。

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

【其他文章推薦】

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

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

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

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

財政部長樓繼偉:2020年針對新能源汽車的補貼將退出

中國財政部長樓繼偉日前在中國電動汽車百人會論壇上表示,汽車企業對消費補貼過度依賴,2020年針對新能源汽車的補貼將退出。

樓繼偉稱2020年針對新能源汽車的補貼將退出;2017-2018年的補貼標準比2016年水準下調20%。

樓繼偉稱對優勢企業的補貼將提高。十三五期間中國新能源汽車市場進一步開放,將加快建立新能源汽車積分交易機制。

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

【其他文章推薦】

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

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

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

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

陸去年新能源汽車保有量年增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網頁設計已成為網頁設計推薦首選

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

類型擦除真的能完全擦除一切信息嗎?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 非常簡單,內部只有兩個主要的類,VirtualMachineVirtualMachineDescriptor

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網頁設計已成為網頁設計推薦首選

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

英國投入4000萬英鎊大力推廣電動車

據報導,英國將率先在倫敦和諾丁漢等城市推廣電動車,並投入4000萬英鎊,以實現改善空氣品質、創造就業和普及低排放交通工具等目標。

資金由英國政府超低排放項目提供並將在上述城市中建立電動車基礎設施,其中包括建設未來社區,給予超低排放汽車以優先行駛以及停放權,建立電動汽車體驗中心,為公眾提供超低排放汽車知識的普及和服務,以及出臺超低排放汽車購置短期貸款服務和汽車租賃計畫,建設超低排放汽車優惠停放點,建設充電樁以及太陽能停車換乘系統等等。

據瞭解,這一項目只是英國政府發展低排放交通工具總體規劃的一部分。英國計畫到2020年總計投入6億英鎊,發展低排放交通工具,其中包括對電動車銷售進行補貼,推廣低排放公車和計程車,資助新一代車身材料和車用電池技術的研發等等。

此外,凡是在英國買電動車的人,都可以得到最高5000英鎊的補助金,英國政府也承諾,未來會繼續發電動車補貼,根據駕駛的車型決定發補助金的數量,從2500-5500英鎊之間不等。英國還將開放電動車上公交專用道,未來開電動車的人,可能不會在塞車時段卡在車陣當中。

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

微型電動車或納入摩托車管理範疇 政策或將2016年出臺

據瞭解,工信部、發改委、公安部、環境部等部委已經對微型電動汽車進行調研,通過高標準放行一小部分產品後,其他微型電動車或將納入新的摩托車管理範疇。

中國電動汽車百人會微型電動車課題組組長介紹,通過多次微型電動車發展問題的調研與討論,並形成了相關的政策管理建議方案。他表示,馬凱總理對微型電動車的發展已有批示,要納入正常的機動車管理。

這是一個積極正面的信號,而不是否認微型電動車的存在價值。

在具體的管理標準上,一位從事新能源汽車研究的相關人士告訴記者,目前政策傾向于按照類似歐盟的摩托車歸類管理微型電動車。同時,在歐盟標準之上,按照中國不同城市特色區分管理,比如,在北上廣深等一線城市禁止微型電動車上路,但遠郊區可放開,三四線及以下的城市也可以上路,這就規避了實際管理中的很多矛盾。
 

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

【其他文章推薦】

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

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

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

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務