南極地區首次發現青蛙化石碎片

摘錄自2020年4月27日俄羅斯衛星通訊社報導

根據發表在《Scientific Reports》期刊的研究報告,科研人員在南極半島北端的西莫爾島發現了數塊青蛙的頭骨和部分髖骨化石碎片,這種古老的生物是南美地區現代頭盔蛙科的近親。

這一的發現讓科學家對南極大陸的古代氣候有了新的認識。這些化石碎片距今約4000萬年,頭骨形狀可以看出這隻青蛙屬於頭盔蛙科。頭盔蛙科現生種生活在南美安第斯山脈中部的溫暖潮濕山谷中。這表明,至少4000萬年前,南極洲地區也是類似的氣候。

這一發現改變了科學家對南極大陸氣候變化的認識。大多數科學家認為,大約4000萬年前,南極洲與澳洲大陸分離後迅速被冰層覆蓋。但是一些證據表明,在南極大陸與南半球其他現代大陸完全分離前,南極洲冰蓋就已經開始形成。

生態保育
物種保育
生物多樣性
國際新聞
南極
古生物學
化石
青蛙
兩棲類
南極

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

動物、人與環境健康一體的紓困方案 聯合國科學家提三點呼籲

整理:鄒敏惠(環境資訊中心記者)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

從5到100萬全都有 2017年這些爆款SUV就要來啦

雪佛蘭 – 探界者預計售價:18萬起預計上市日期:2017年近幾年來,雪佛蘭幾乎只有一款創酷在征戰SUV市場(老舊的科帕奇可以忽略不計),要想進一步提升銷量,推出一款全新的SUV勢在必行,於是就有了這台探界者。福特有探險者和撼路者,雪佛蘭搞出個探界者是什麼鬼。

持續了近10天的2016廣州國際車展終於在上周日謝幕,在不少媒體老濕吐槽本屆車展沒有太多亮點的時候,叫獸卻發現了另一番風景:

好了,說回正題,這屆車展雖然看點不多,甚至除了媒體日那天可憐的日產外(你懂的),都沒有太多可以調侃的段子,但以下這些SUV的登場還是給叫獸帶來不少驚喜。廢話結束,接下來看看在即將到來的2017年有哪些SUV是值得我們期待的。

寶駿 – 510

預計售價:5.5 – 8萬

上市日期:預計2017年1季度

我把寶駿510排在第一位,其預計的親民售價是第一點,最最重要的還是它擁有堪稱驚艷的外觀設計。如果不是親眼所見,真不敢相信這居然是寶駿出品的一款車。

犀利、個性、時尚、前衛···這一些列詞彙悉數冠在510身上也毫不為過,sorry,原諒叫獸任性一回,僅憑外觀就愛上了這款車。不過在感性之餘,咱們回歸到理性部分。

510的內飾同樣令人稱讚,雖談不上有多麼精緻,但看上去毫不雜亂,算得上是簡潔大方,這對一款入門車型來說是難能可貴的。

從前两天有關510文章的評論里可以看出,大家對這款車都很期待。叫獸斷言,只要未來的價格給力,這又將是一款爆款的存在。小道消息稱,510將於2017年1季度上市。

名爵 – ZS

預計售價:暫本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

這些不少人都想買的自主SUV都使用日系的變速箱你知道嗎?

東風AX7指導價格:9。97-14。17萬東風風神AX7是基於東風軍工二號平台生產的首款民用SUV車型,在做工品質上還是有着一定的高質量保證,同樣藉助多年與國外知名汽車企業合資的資源性便利,東風AX7在汽車配件供應商方面也是來頭不小。
採用愛信變速箱的自主SUV
最近小編收到不少的網友留言,問題的主要偏重點在於:中國自主品牌當中的SUV有哪些採用愛信變速箱?或者愛信變速箱為什麼那麼多人採用?那麼今天就帶着問題,看看愛信變速箱究竟有什麼優勢讓這麼多人採用。
愛信變速箱可以說是現在汽車市場中的明星產品系列,不少情況下都會聽到“XX車使用了愛信變速箱”的話語,那麼究竟為什麼它的受眾面會這麼廣?
品牌成熟,技術可本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

網頁設計最專業,超強功能平台可客製化

8萬到30萬,這幾款車的發動機值得推薦,有力又省油!

上汽通用五菱-寶駿 5602016款 1。5T 手動舒適型售價:8。18萬元東南-DX32016款 1。5T CVT舒適型售價:8。49萬元斯柯達-速派2016款 280TSI DSG前行版售價:17。98萬元廣汽謳歌-CDX2016款 1。5T 兩驅暢享版售價:22。98萬元總結:8萬到30萬區間里值得購買的幾款小排量四缸渦輪增壓發動機車型都為你一一推薦,看個人預算和需求辦事,總能選出最合適你的那款車。

隨着6缸發動機漸漸退出家用車舞台,當代車企越來越注重汽車的排放和燃油經濟性。在排放、購置稅等政策影響下催生出以前不敢想象的三缸1.0T發動機,雖說三缸發動機擁有着燃油經濟性和排放的先天優勢,福特、吉利等廠商當起了先行者,但礙於發動機振動和噪聲抑制以及動力儲備的上限,始終難登大雅之堂。而大排量V6發動機制肘於大油耗和購置稅政策,終究被家用車市場淘汰。

要照顧到行駛質感和動力的同時,又要理想的燃油經濟性和享受購置稅優惠政策,要求敢不敢再多點?不要緊,給你支招,咱可以選四缸1.5L及以下排量的渦輪增壓車,下面給你推薦各個價位區間符合以上要求的車型。

上汽通用五菱-寶駿 560

2016款 1.5T 手動舒適型

售價:8.18萬元

東南-DX3

2016款 1.5T CVT舒適型

售價:8.49萬元

斯柯達-速派

2016款 280TSI DSG前行版

售價:17.98萬元

廣汽謳歌-CDX

2016款 1.5T 兩驅暢享版

售價:22.98萬元

總結:8萬到30萬區間里值得購買的幾款小排量四缸渦輪增壓發動機車型都為你一一推薦,看個人預算和需求辦事,總能選出最合適你的那款車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

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

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

年輕人首選的SUV?這次給你們推薦點不一樣的

而本次所介紹的東南DX3則是東南品牌旗下的“小鮮肉”車型,外觀造型與DX7師出同源,由於車身更加短小緊湊,所以可以看出東南DX3的外觀更加具有時尚感和運動感,作為一款消費人群定義在三十歲以內年輕人的車型,東南DX3的顏值可以獲得一個比較高的分數。

年輕人首選的自主品牌SUV

現在年輕人購車已經不是什麼稀奇的事情,但是似乎業內出現了一種怪圈,就是說到年輕人首選的SUV車型,合資的似乎就只有通用集團的昂科拉、創酷;而自主品牌就只有吉利在今年新推出的跨界SUV帝豪GS,其實不然,在自主品牌中,還是有不少年輕人可以選擇的SUV。

為什麼要推薦自主品牌?

當手機逐漸智能化並且豐富了人們的生活之時,國產品牌的手機成為了市場的新寵,功能強大,價格實惠的國產品牌手機使用基數往往比國際知名品牌手機要來得多;反觀汽車也是如此,消費者更注重的是一台車的售價和配置,而逐步成長起來的自主品牌汽車未嘗不能成為預算通常不太多的年輕人首選的車型。

如果當一輛SUV品牌實力強勁,售價相對實惠,外觀還富有個性的話,作為購置第一台車以便於初入職場實現人生目標的年輕消費者來說,自然是一種不錯的選擇。

BJ20

指導價格:9.68-13.98萬

北京汽車是自主汽車較早出名的品牌之一,在上世紀八十年代左右的北京,如果誰可以開着一台“北京吉普”在大街上“招搖過市”,那絕對是拉風至極的一件事情。

而在今年中旬,北京汽車專門為年輕一代的消費者推出了一款城市SUV——BJ20,整車在外觀設計上繼承了北京汽車品牌家族式的經典元素,並且根據時下流行的審美趨勢進行融合,使得BJ20的外觀極具個性與硬派氣息。

內飾設計同樣看得出北京汽車的品牌形象已經逐漸成熟,家族化風格十分濃郁,平直簡練的風格配合上獨特的雙幅式方向盤造型,穩重中還透露出一絲個性與不羈。

BJ20搭載的是一台1.5T渦輪增壓發動機,最大馬力150匹,峰值扭矩210牛米,與之配合的是傳統的手動變速箱和一台CVT無級變速箱;儘管不帶四驅,但是超越同級別的底盤高度,在應對稍微惡劣的非鋪裝路面時還是能給予人不少的信心。

東南DX3

指導價格:6.79-10.09萬

東南也算是國內做SUV車型比較有歷史的汽車廠商了,之前與三菱的深度合作使得東南在造車方面有了一定的技術深度儲備,而且近年來與賓法合作,在外觀設計上也逐漸有了自己的風格和語言,東南DX7則算是一台掙回了眼球的SUV。

而本次所介紹的東南DX3則是東南品牌旗下的“小鮮肉”車型,外觀造型與DX7師出同源,由於車身更加短小緊湊,所以可以看出東南DX3的外觀更加具有時尚感和運動感,作為一款消費人群定義在三十歲以內年輕人的車型,東南DX3的顏值可以獲得一個比較高的分數。

內飾設計同樣讓人覺得印象深刻,方形幾何配合圓形出風口的造型挺富有視覺衝擊力,只是有一點小編不太能理解的是,為何多媒體中控的功能性操控按鍵會放置在副駕駛的一側?這在國內的在售量產車型上見得確實不多。

畢竟是與三菱有過深度合作的歷史,東南DX3採用的是源自三菱的兩款發動機,型號同為4A91,1.5T發動機最大馬力156匹,峰值扭矩220牛米,1.5L發動機最大馬力120匹,峰值扭矩143牛米,傳動系統是一款可模擬八個檔位,來自比利時邦奇的CVT變速箱,邦奇也是目前國內採用CVT變速箱的車型主要供應商之一。

寶駿510

指導價格:暫無(猜測6-8萬)

寶駿汽車相繼使用了730和560兩款車型打開了銷量之後,最近開始將目標消費群體轉向年輕人群,先是推出了一款售價實惠的兩廂轎車寶駿310,而今年廣州車展上發布的一款年輕定位的SUV也有可能成為未來的主力,那便是寶駿510,。

外觀層面寶駿510非常聰明的使用了當下流行的設計元素,前大燈分體式設計很容易使人聯想到自由光,多邊形的進氣格柵和懸浮式車頂的設計也是觀眾們非常熟悉的設計語言,但是這些融合在寶駿510的身上顯得挺和諧。

內飾層面採用的雙色拼接方式同樣顯得年輕運動,組合型的幾何板塊配合上啞光的裝飾,整個車廂氛圍營造得恰到好處。獨立式的多媒體显示屏幕也算是內飾當中比較出彩的亮點。

動力層面寶駿510搭載的是與寶駿730相同的1.5L自然吸氣發動機,最大馬力112匹,峰值扭矩147牛米,目前僅有6速手動變速箱,未來是否會換上與730相同的AMT變速箱暫時不得而知。

寶駿510的售價暫時沒有公布,但是從小型SUV的定位上看,會比6.98-9.48萬元定價的寶駿560要稍微低一些,小編大膽猜測頂配價格應該是在8萬元左右。究竟最終售價如何,還是比較值得期待。

全文總結:現在年輕一代的汽車消費群體逐漸在全國各地湧現,不僅僅是經濟較為發達的一線城市,在很多二三線甚至更低定位的城市當中,汽車也已經成為了人們日常生活的重要工具,擁有一輛自己的車也是很多年輕人普遍擁有的想法,而面對着現在不斷成熟完善的自主品牌,購買一台自主品牌車型作為人生首輛車,小編認為也沒什麼不好。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

10月銷量達7千多輛!8.98萬起這款日產技術SUV值得買嗎?

38萬車主點評:最滿意空間,乘坐空間很夠用。還有一次有個朋友喝多了,像爛泥一樣一動不動,我們直接把他抬到後備廂里拉回去。動力方面,捨得給油還是有推背感,不過開空調載着4個人,就顯得力不從心啦。目前行駛里程:我目前開了3980公里,平均百公里油耗只有6。

東風日產-啟辰T70

指導價:8.98-12.78萬

車主:神車奧拓

購買車型:2016款 2.0L CVT睿行版

裸車價格:9.78萬

車主點評:我最滿意它的底盤紮實,懸挂行程長,所以通過性好,另外它對於震動的過濾不錯。而內飾的硬塑料有點多!所以檔次感就不夠了。

目前行駛里程:我的T70買了才兩個月,跑了有1345公里,平均百公里油耗只有8.2L,我覺得CVT變速箱還是比較省油!

車主:笑出12塊腹肌

購買車型:2016款 1.6L 手動睿行版

裸車價格:8.38萬

車主點評:最滿意空間,乘坐空間很夠用。還有一次有個朋友喝多了,像爛泥一樣一動不動,我們直接把他抬到後備廂里拉回去!動力方面,捨得給油還是有推背感,不過開空調載着4個人,就顯得力不從心啦!

目前行駛里程:我目前開了3980公里,平均百公里油耗只有6.9L,手動擋相當省油。

車主:段迎風

購買車型:2015款 2.0L CVT睿趣版

裸車價格:11.50萬

車主點評:開起來其實類似日產逍客的感覺,底盤比較紮實。空間的表現也讓我給它豎起大拇指!動力感覺有點肉,這是日產CVT變速箱的特性。另外方向盤有些重,開起來比起本田繽智還是顯得笨重一些。

目前行駛里程:我的車是2015年12月購買的,到現在行駛了9500公里,平均百公里油耗9L,這樣的油耗還可以接受。

編者點評:

啟辰T70的價格不高,但是在底盤表現、質量、空間方面表現都不錯,只是配置會稍低一些。綜合性價比是不錯的!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

Spring源碼之自動裝配

引言

我們使用Spring開發過程中經常會用到Autowired註解注入依賴的bean,這部分也是面試的熱點問題之一。今天咱們一起來深入研究下自動注入的背後實現原理。首先上一個例子,如下所示:

@RestController
public class TestController {
    @Autowired
    List<ICheckRuleService> checkRuleService;

    @RequestMapping("/test")
    public void test(){
        checkRuleService.forEach(x->x.valid());
    }
}

從填充Bean開始

Autowired是怎麼實現自動注入的呢,今天我們來通過源碼分析一下。當Spring創建 TestController Bean時,會調用AbstractBeanFactory#doGetBean(如果對Spring創建Bean流程不熟的讀者,可以給我留言,後面考慮是否寫個IOC系列),doGetBean裏面會調用doCreateBean()方法去創建Bean,創建Bean之後,會對Bean進行填充

try {
    this.populateBean(beanName, mbd, instanceWrapper);
    exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}

populateBean 里有這樣一段代碼,看起來是處理Autowired的,分別是autowireByName 和 autowireByType

PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;
if (mbd.getResolvedAutowireMode() == 1 || mbd.getResolvedAutowireMode() == 2) {
    MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);
    if (mbd.getResolvedAutowireMode() == 1) {
       this.autowireByName(beanName, mbd, bw, newPvs);
    }

   if (mbd.getResolvedAutowireMode() == 2) {
       this.autowireByType(beanName, mbd, bw, newPvs);
   }

    pvs = newPvs;
}

我們來驗證一下,通過斷點調試我們發現並不會進入if里,所以自動注入並不是這裏實現的。那這裡有什麼用呢,先放一放,後面再說。

後置處理器屬性填充

那麼到底是哪裡注入進去的呢?我們繼續往下看,在這段代碼下方有個BeanPostProcessor的邏輯,通過斷點我們發現有個AutowiredAnnotationBeanPostProcessor 的後置處理器,當這個BeanPostProcessor執行完 postProcessPropertyValues方法后,testController的checkRuleService 屬性就有了值了,說明屬性值注入肯定和 AutowiredAnnotationBeanPostProcessor 有關,我們跟進去看一下

進入AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues 方法里,裏面主要有兩部分邏輯

  • 首先看到一段 findAutowiringMetadata 的邏輯,根據方法名稱知道是獲取當前bean的注入元信息

  • 調用 metadata.inject 注入屬性

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);

    try {
        metadata.inject(bean, beanName, pvs);
        return pvs;
    } catch (BeanCreationException var7) {
        throw var7;
    } catch (Throwable var8) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var8);
    }
}

我們先來看第一部分:findAutowiringMetadata

我們進入findAutowiringMetadata,看下它的邏輯,先從 injectionMetadataCache 緩存里取,如果取不到值,則調用buildAutowiringMetadata 構建 InjectionMetadata ,構建成功後設置到緩存里。

    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
        String cacheKey = StringUtils.hasLength(beanName) ? beanName : clazz.getName();
        InjectionMetadata metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized(this.injectionMetadataCache) {
                metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }

                    metadata = this.buildAutowiringMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
            }
        }

        return metadata;
    }

我們來看下 buildAutowiringMetadata,繼續跟進去,源碼如下:

裏面是通過當前Bean的Class反射獲取 Field 和 Method ,然後對 Field 和 Method 分別調 findAutowiredAnnotation 方法獲取自動注入的註解,然後根據註解類型是否required構建不同類型的InjectedElement。

  • AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement:

boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
  • AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement:

boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));

補充:通過AutowiredAnnotationBeanPostProcessor 構造函數我們知道,自動注入處理的是被 @Autowired 和 @Value 這兩個註解標註的屬性(Field)或方法(Method):

    public AutowiredAnnotationBeanPostProcessor() {

        this.autowiredAnnotationTypes.add(Autowired.class);

        this.autowiredAnnotationTypes.add(Value.class);

    //......

到這裏,需要注入的元數據信息就已經構建完成了,接下來就要到注入部分了。來看下 postProcessPropertyValues 的第二部分。

再看第二部分:metadata.inject

前面獲取到了需要注入的元數據信息,接下來是元數據 inject 的實現,繼續跟進去,裏面是一個for循環,循環調用了element的inject方法

if (!((Collection)elementsToIterate).isEmpty()) {
    for(Iterator var6 = ((Collection)elementsToIterate).iterator(); var6.hasNext(); element.inject(target, beanName, pvs)) {
        element = (InjectionMetadata.InjectedElement)var6.next();
        if (logger.isDebugEnabled()) {
            logger.debug("Processing injected element of bean '" + beanName + "': " + element);
        }
    }
}

我們斷點調試進去,發現element的真實類型是AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement,而當前element 真實類型是 TestController.checkRuleService 的集合。

我們進入AutowiredFieldElement#inject方法,首先嘗試從緩存里拿當前Field的值,肯定拿不到,所以走的是else分支,else分支里從beanFactory里解析當前Field屬性值

value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

繼續跟進去,發現其實調用的 doResolveDependency 方法

越來越接近真相了,不要着急,繼續跟進去

發現一個類型為Object的 multipleBeans ,結果返回的也是這個Object,我們大膽猜測這個Object就是我們需要注入的List屬性,繼續跟進去驗證一下:

我們看一下 Collection 分支的源碼

 else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            elementType = descriptor.getResolvableType().asCollection().resolveGeneric(new int[0]);
            if (elementType == null) {
                return null;
            } else {
                Map<String, Object> matchingBeans = this.findAutowireCandidates(beanName, elementType, new DefaultListableBeanFactory.MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) {
                    return null;
                } else {
                    if (autowiredBeanNames != null) {
                        autowiredBeanNames.addAll(matchingBeans.keySet());
                    }
​
                    TypeConverter converter = typeConverter != null ? typeConverter : this.getTypeConverter();
                    Object result = converter.convertIfNecessary(matchingBeans.values(), type);
                    if (this.getDependencyComparator() != null && result instanceof List) {
                        ((List)result).sort(this.adaptDependencyComparator(matchingBeans));
                    }
​
                    return result;
                }
            }
        }

裏面是調用了 findAutowireCandidates 來獲取Bean,findAutowireCandidates 內部會獲取到依賴的BeanNames,然後根據beanName 循環調用beanFactory#getBean 獲取需要注入的bean

this.findAutowireCandidates(beanName,elementType,new DefaultListableBeanFactory.MultiElementDescriptor(descriptor))

beanFactory#getBean方法,最終會調用 AbstractBeanFactory#doGetBean,獲取到需要裝配進去的屬性bean。

    public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException {
        return beanFactory.getBean(beanName);
    }

當所有的循環執行完畢,就獲取到了 multipleBeans ,驗證了前面的猜測。真是太不容易,趕緊設置緩存  

最終通過field.set 將獲取到的List屬性值value設置到當前bean里,代碼如下:

if (value != null) {
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
}

執行field的set方法后,再來看checkRuleService屬性就有值了

如果是Method注入,對應的就是通過反射調用 method.invoke 將屬性設置到方法參數里,大致流程差不多。到此,Autowired 裝配流程也就結束了。

前面在講到 populateBean 的時候,有個根據 autowireMode 判斷是否執行屬性注入,當時獲取的autowireMode==0,那麼什麼時候autowireMode 會有值並且會根據autowireByName 和 autowireByType來裝配呢?

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)

其實也很好理解,通過源碼我們知道,這裏的 mbd 是一個 RootBeanDefinition ,也就是說這裏的 mbd.getResolvedAutowireMode()獲取的值是通過Bean定義或者通過PostProcessor拿到BeanDefinition,然後設置了AutowireMode屬性才會有值。當我們查看這裏的autowireByType源碼(AbstractAutowireCapableBeanFactory#autowireByType)可以發現,其實autowireByType也是會調用resolveDependency,繼續跟進去,發現其實調用的 doResolveDependency 方法,而AutowiredAnnotationBeanPostProcessor 也是通過這個方法實現的自動注入,後面的流程就都一樣了。

最後總結一下

1、bean創建完成后,會調用 populateBean() 填充Bean,在populateBean()方法里會獲取所有的BeanPostProcessor,並循環執行 BeanPostProcessor#postProcessPropertyValues() 設置屬性

2、其中有個AutowiredAnnotationBeanPostProcessor,這個處理器里會根據當前Bean的Class,通過反射獲取 Field 和 Method ,分別獲取 Field 和 Method 上的自動注入的註解(@Autowired 和 @Value),構建注入元數據InjectionMetadata

3、調用注入元數據InjectionMetadata的 inject() 方法,裝配屬性(有兩種:AutowiredFieldElement 和AutowiredMethodElement),會調用this.beanFactory.resolveDependency(desc,beanName,autowiredBeanNames, typeConverter) 解析依賴的屬性值

4、resolveDependency 最終會調用到 resolveMultipleBeans ,而 resolveMultipleBeans 會根據當前注入屬性的類型分別按 Array、Collection、Map 走不同的分支,在分支里調用 findAutowireCandidates 獲取注入bean的實例,最終回調到 AbstractBeanFactory#doGetBean

5、獲取到所有需要注入的屬性 bean 實例后,通過反射設置到對應的屬性或方法里去,就完成了自動注入全流程了

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

帶你學夠浪:Go語言基礎系列 – 8分鐘學控制流語句

文章每周持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜索公眾號「 後端技術學堂 」第一時間閱讀(一般比博客早更新一到兩篇)

對於一般的語言使用者來說 ,20% 的語言特性就能夠滿足 80% 的使用需求,剩下在使用中掌握。基於這一理論,Go 基礎系列的文章不會刻意追求面面俱到,但該有知識點都會覆蓋,目的是帶你快跑趕上 Golang 這趟新車。

Hurry up , Let’s go !

控制語句是程序的靈魂,有了它們程序才能完成各種邏輯,今天我們就來學習 Go 中的各種控制語句。

通過本文的學習你將掌握以下知識:

  • if 條件語句
  • for 循環語句
  • switch 語句
  • defer 延遲調用

if 條件語句

與大多數編程語言一樣,if 用於條件判斷,當條件表達式 exprtrue 執行 {} 包裹的消息體語句,否則不執行。

語法是這樣的:

if expr {
    // some code
}

**注意:**語法上和 c 語言不同的是不用在條件表達式 expr 外帶括號,和 python 的語法類似。

當然,如果想在條件不滿足的時候做點啥,就可以 if 后帶 else 語句。語法:

if expr {
    // some code
} else {
    // another code
}

不僅僅是 if

除了可以在 if 中做條件判斷之外,在 Golang 中你甚至可以在 if 的條件表達式前執行一個簡單的語句。

舉個例子:

if x2 := 1; x2 > 10 { 
    fmt.Println("x2 great than 10")
} else {
    fmt.Println("x2 less than 10", x2)
}

上面的例子在 if 語句中先聲明並賦值了 x2,之後對 x2 做條件判斷。

注意:此處在 if 內聲明的變量 x2 作用域僅限於 if 和else 語句。

for循環語句

當需要重複執行的時候需要用到循環語句,Go 中只有 for 這一種循環語句。

標準的for循環語法:

for 初始化語句; 條件表達式; 後置語句 {
    // some code
}

這種語法形式和 C 語言中 for 循環寫法還是很像的,不同的是不用把這三個部分用 () 括起來。循環執行邏輯:

  • 初始化語句:初始循環時執行一次,做一些初始化工作,一般是循環變量的聲明和賦值。
  • 條件表達式:在每次循環前對條件表達式求值操作,若求值結果是
    true 則執行循環體內語句,否則不執行。
  • 後置語句:在每次循環的結尾執行,一般是做循環變量的自增操作。

舉個例子:

sum := 0
for i := 0; i < 10; i++ {
    sum += i // i作用域只在for語句內
    fmt.Println(i, sum)
}

注意:循環變量i 的作用域只在 for 語句內,超出這個範圍就不能使用了。

while循環怎麼寫?

前面說了,Golang 中只有 for 這一種循環語法,那有沒有類似 C 語言中 while 循環的寫法呢?答案是有的:把 for 語句的前後兩部分省略,只留中間的「條件表達式」的 for 語句等價於 while 循環。

像下面這樣:

sum1 := 0
for ;sum1 < 10; { // 可以省略初始化語句和後置語句
    sum1++
    fmt.Println(sum1)
}

上面的示例沒有初始化語句和後置語句,會循環執行 10 次後退出。

當然你要是覺得前後的分號也不想寫了,也可以省略不寫,上面的代碼和下面是等效的:

sum1 := 0
for sum1 < 10 { // 可以省略初始化語句和後置語句,分號也能省略
    sum1++
    fmt.Println(sum1)
}

在 Golang 中死循環可以這樣寫,相當於 C 語言中的 while(true)

 for { // 死循環
  // your code
 }

switch 語句

switch 語句可以簡化多個 if-else 條件判斷寫法,避免代碼看起來雜亂。

可以先定義變量,然後在 switch 中使用這個變量。

 a := 1
 switch a {
 case 1: 
  fmt.Println("case 1") // 不用寫break 執行到這自動跳出
 case 2:
  fmt.Println("case 2")
 default:
  fmt.Printf("unexpect case")
 }
輸出:case 1

從 C 語言過來的朋友一定有這樣的經歷:經常會在 case 語句中漏掉 break 導致程序繼續往下執行,從而產生奇奇怪怪的 bug ,這種問題在 Golang 中不復存在了。

Golang 在每個 case 後面隱式提供 break 語句。 除非以 fallthrough 語句結束,否則分支會自動終止。

 switch a := 1; a { //這裡有分號
 case 1: // case 無需為常量,且取值不必為整數。
  fmt.Println("case 1") // 不用寫break 執行到自動跳出 除非以 fallthrough 語句結束
  fallthrough
 case 2:
  fmt.Println("case 2")
 default:
  fmt.Printf("unexpect case")
 }
輸出:
case 1
case 2

還可以直接在 switch 中定義變量后使用,但是要注意變量定義之後又分號,比如下面這樣:

 switch b :=1; b { //注意這裡有分號
 case 1: 
  fmt.Println("case 1") 
 case 2:
  fmt.Println("case 2")
 default:
  fmt.Printf("unexpect case")
 }

沒有條件的switch

沒有條件的 switch 同 switch true 一樣,只有當 case 中的表達式值為「真」時才執行,這種形式能簡化複雜的 if-else-if else 語法。

下面是用 if 來寫多重條件判斷,這裏寫的比較簡單若是再多幾個 else if 代碼結構看起來會更糟糕。

    a := 1
    if a > 0 {
        fmt.Println("case 1") 
    } else if a < 0 {
        fmt.Println("case 2")   
    } else {
        fmt.Printf("unexpect case")   
    }

如果用上不帶條件的 switch 語句,寫出來就會簡潔很多,像下面這樣。

 a := 1
 switch {    // 相當於switch true
 case a > 0: // 若表達式為「真」則執行 
  fmt.Println("case 1") 
 case a < 0:
  fmt.Println("case 2")
 default:
  fmt.Printf("unexpect case")
 }

defer 語句

defer 語句有延遲調用的效果。具體來說defer後面的函數調用會被壓入堆棧,當外層函數返回才會對壓棧的函數按後進先出順序調用。說起來有點抽象,舉個例子:

package main

import "fmt"

func main() {
 fmt.Println("entry main")
 for i := 0; i < 6; i++ {
  defer fmt.Println(i)
 }
 fmt.Println("exit main")
}

fmt.Println(i) 不會每次立即執行,而是在 main 函數返回之後才依次調用,編譯運行上述程序的輸出:

entry main
exit main  //外層函數返回
5
4
3
2
1
0

上面是簡單的使用示例,實際使用中defer 通常用來釋放函數內部變量,因為它可以在外層函數 return 之後繼續執行一些清理動作。

這在文件類操作異常處理中非常實用,比如用於釋放文件描述符,我們以後會講解這塊應用,總之先記住 defer 延遲調用的特點。

總結

通過本文的學習,我們掌握了 Golang 中基本的控制流語句,利用這些控制語句加上一節介紹的變量等基礎知識,可以構成豐富的程序邏輯,就能用 Golang 來做一些有意思的事情了。

感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,我們一起在探討中學習.

今天的技術分享就到這裏,我們下期再見。

創作不易,白票不是好習慣,如果在我這有收穫,動動手指「點贊」「關注」是對我持續創作的最大支持。

微信搜索公眾號「 後端技術學堂 」回復「資料」「1024」有我給你準備的各種編程學習資料。文章每周持續更新,我們下期見!

本文使用 mdnice 排版

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

聊一聊高併發高可用那些事 – Kafka篇

目錄

為什麼需要消息隊列

1.異步 :一個下單流程,你需要扣積分,扣優惠卷,發短信等,有些耗時又不需要立即處理的事,可以丟到隊列里異步處理。

2.削峰 :按平常的流量,服務器剛好可以正常負載。偶爾推出一個優惠活動時,請求量極速上升。由於服務器 Redis,MySQL 承受能力不一樣,如果請求全部接收,服務器負載不了會導致宕機。加機器嘛,需要去調整配置,活動結束後用不到了,即麻煩又浪費。這時可以將請求放到隊列里,按照服務器的能力去消費。

3.解耦 :一個訂單流程,需要扣積分,優惠券,發短信等調用多個接口,出現問題時不好排查。像發短信有很多地方需要用到, 如果哪天修改了短信接口參數,用到的地方都得修改。這時可以將要發送的內容放到隊列里,起一個服務去消費, 統一發送短信。

高吞吐、高可用 MQ 對比分析

看了幾個招聘網站,提到較多的消息隊列有:RabbitMQ、RocketMQ、Kafka 以及 Redis 的消息隊列和發布訂閱模式。

Redis 隊列是用 List 數據結構模擬的,指定一端 Push,另一端 Pop,一條消息只能被一個程序所消費。如果要一對多消費的,可以用 Redis 的發布訂閱模式。Redis 發布訂閱是實時消費的,服務端不會保存生產的消息,也不會記錄客戶端消費到哪一條。在消費的時候如果客戶端宕機了,消息就會丟失。這時就需要用到高級的消息隊列,如 RocketMQ、Kafka 等。

ZeroMQ 只有點對點模式和 Redis 發布訂閱模式差不多,如果不是對性能要求極高,我會用其它隊列代替,畢竟關解決開發環境所需的依賴庫就夠折騰的。

RabbitMQ 多語言支持比較完善,特性的支持也比較齊全,但是吞吐量相對小些,而且基於 Erlang 語言開發,不利於二次開發和維護。

RocketMQ 和 Kafka 性能差不多,基於 Topic 的訂閱模式。RocketMQ 支持分佈式事務,但在集群下主從不能自動切換,導致了一些小問題。RocketMQ 使用的集群是 Master-Slave ,在 Master 沒有宕機時,Slave 作為災備,空閑着機器。而 Kafka 採用的是 Leader-Slave 無狀態集群,每台服務器既是 Master 也是 Slave。

Kafka 相關概念

在高可用環境中,Kafka 需要部署多台,避免 Kafka 宕機后,服務無法訪問。Kafka集群中每一台 Kafka 機器就是一個 Broker。Kafka 主題名稱和 Leader 的選舉等操作需要依賴 ZooKeeper。

同樣地,為了避免 ZooKeeper 宕機導致服務無法訪問,ZooKeeper 也需要部署多台。生產者的數據是寫入到 Kafka 的 Leader 節點,Follower 節點的 Kafka 從 Leader 中拉取數據同步。在寫數據時,需要指定一個 Topic,也就是消息的類型。

一個主題下可以有多個分區,數據存儲在分區下。一個主題下也可以有多個副本,每一個副本都是這個主題的完整數據備份。Producer 生產消息,Consumer 消費消息。在沒給 Consumer 指定 Consumer Group 時會創建一個臨時消費組。Producer 生產的消息只能被同一個 Consumer Group 中的一個 Consumer 消費。

  • Broker:Kafka 集群中的每一個 Kafka 實例
  • Zookeeper:選舉 Leader 節點和存儲相關數據
  • Leader:生產者與消費者只跟 Leader Kafka 交互
  • Follower:Follower 從 Leader 中同步數據
  • Topic:主題,相當於發布的消息所屬類別
  • Producer:消息的生產者
  • Consumer:消息的消費者
  • Partition:分區
  • Replica:副本
  • Consumer Group:消費組

分區、副本、消費組

  • 分區

主題的數據會按分區數分散存到分區下,把這些分區數據加起來才是一個主題的完整的數據。分區數最好是副本數的整數倍,這樣每個副本分配到的分區數比較均勻。同一個分區寫入是有順序的,如果要保證全局有序,可以只設置一個分區。

如果分區數小於消費者數,前面的消費者會配到一個分區,後面超過分區數的消費者將無分區可消費,除非前面的消費者宕機了。如果分區數大於消費者數,每個消費者至少分配到一個分區的數據,一些分配到兩個分區。這時如果有新的消費者加入,會把有兩個分區的調一個分配到新的消費者。

分區數可以設置成 6、12 等數值。比如 6,當消費者只有一個時,這 6 個分區都歸這個消費者,後面再加入一個消費者時,每個消費者都負責 3 個分區,後面又加入一個消費者時,每個消費者就負責 2 個分區。每個消費者分配到的分區數是一樣的,可以均勻地消費。

  • 副本

主題的副本數即數據備份的個數,如果副本數為 1 , 即使 Kafka 機器有多個,當該副本所在的機器宕機后,對應的數據將訪問失敗。

集群模式下創建主題時,如果分區數和副本數都大於 1,主題會將分區 Leader 較均勻的分配在有副本的 Kafka 上。這樣客戶端在消費這個主題時,可以從多台機器上的 Kafka 消息數據,實現分佈式消費。

副本數不是越多越好,從節點需要從主節點拉取數據同步,一般設置成和 Kafka 機器數一樣即可。如果只需要用到高可用的話,可以採用 N+1 策略,副本數設置為 2,專門弄一台 Kafka 來備份數據。然後主題分佈存儲在 “N” 台 Kafka 上,”+1″ 台 Kafka 保存着完整的主題數據,作為備用服務。

Replicas 表示在哪些 Kafka 機器上有主題的副本,Isr 表示當前有副本的 Kafka 機器上還存活着的 Kafka 機器。主題分區中所涉及的 Leader Kafka 宕機時,會將宕機 Kafka 涉及的分區分配到其它可用的 Kafka 節點上。如下:

  • 消費組

每一個消費組記錄者各個主題分區的消費偏移量,在消費的時候,如果沒有指定消費組,會默認創建一個臨時消費組。生產者生產的消息只能被同一消費組下某個消費者消費。如果想要一條消息可以被多個消費者消費,可以加入不同的消費組。

偏移量最大值,消息存儲策略

  • 偏移量的最大值

long 類型最大值是(2^63)-1 (為什麼要減一呢?第一位是符號位,正的有262,負的有262,其中+0 和 -0 是相等的 , 只不過有的語言把0算到負裏面,有的語言把0算到正裏面)。 偏移量是一個 long 類型,除去負數,包含0,其最大值為 2^62。

  • 消息存儲策略

Kafka 配置項提供兩種策略, 一種是基於時間:log.retention.hours=168,另一種是基於大小:log.retention.bytes=1073741824 。符合條件的數據會被標記為待刪除,Kafka會在恰當的時候才真正刪除。

Zookeeper 上存的 Kafka 相關數據

如何確保消息只被消費一次

前面已經講到,同一主題里的分區數據,只能被相同消費組裡其中一個消費者消費。當有多個消費者同時消費同一主題時,將這些消費者都加入相同的消費組,這時生產者的消息只能被其中一個消費者消費。

重複消費和數據丟失問題

  • 生產者

生產者發送消息成功后,不等 Kafka 同步完成的確認,繼續發送下一條消息。在發的過程中如果 Leader Kafka 宕機了,但生產者並不知情,發出去的信息 Kafka 就收不到,導致數據丟失。解決方案是將 Request.Required.Acks 設置為 -1,表示生產者等所有副本都確認收到后才發送下一條消息。

Request.Required.Acks=0 表示發送消息即完成發送,不等待確認(可靠性低,延遲小,最容易丟失消息)

Request.Required.Acks=1 表示當 Leader 提交同步完成后才發送下一條消息

  • 消費者

消費者有兩種情況,一種是消費的時候自動提交偏移量導致數據丟失:拿到消息的同時偏移量加一,如果業務處理失敗,下一次消費的時候偏移量已經加一了,上一個偏移量的數據丟失了。

另一種是手動提交偏移量導致重複消費:等業務處理成功后再手動提交偏移量,有可能出現業務處理成功,偏移量提交失敗,那下一次消費又是同一條消息。

怎麼解決呢?這是一個 or 的問題,偏移量要麼自動提交要麼手動提交,對應的問題是要麼數據丟失要麼重複消費。如果消息要求實時性高,丟個一兩條沒關係的話可以選擇自動提交偏移量。如果消息一條都不能丟的話可以選擇手動提交偏移量,然後將業務設計成冪等,不管這條消息消費多少次最終和消費一次的結果一樣。

Linux Kafka 操作命令

  • 查看 Kafka 中 Topic
  • 查看 Kafka 詳情
  • 消費 Topic
  • 查看所有消費組
  • 查看消費組的消費情況

Windows 可視化工具 Kafka Tool

  • 配置 Hosts 文件
123.207.79.96 ZooKeeper-Kafka-01
  • 配置 Kafka Tool 連接信息

  • 查看 Kafka 主題數據

生產者和消費者使用代碼

  • 具體操作參考 github.com/wong-winnie/library

訂閱號:偉洪winnie

  • 訂閱號回復關鍵字【聊聊高併發高可用那些事】獲取專欄文章

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準