分類: 3C資訊

  • 務實之選 二十來萬的合資SUV有啥好選擇?

    務實之選 二十來萬的合資SUV有啥好選擇?

    標緻4008的內飾採用了飛航式設計理念,飛機推桿式的檔把和撥片式的功能鍵顯得頗為新穎,小尺寸方向盤給人以一種很強烈的戰鬥氣息。350THp車型搭載的是一台1。6T渦輪增壓發動機,最大馬力167匹,峰值扭矩245牛米,傳動系統也是一款6AT手自一體變速箱。

    做汽車編輯久了,總會碰到很多朋友問同一個問題,就是預算二十來萬,買一台合資SUV有啥好的車型?往往這個問題後面會帶上一句:不買日系車。

    在這個預算區間現在可以購買的合資SUV已經非常非常多,這次就根據小編個人所開過和感受過車型做一個推薦,前提是,今兒不說日系。

    操控至上—福特翼虎

    指導價格:19.38-27.58萬

    推薦車型:EcoBoost 180 兩驅豪翼型

    指導價格:22.98萬

    福特翼虎在更換了家族式盾型前臉之後確實顯得更加漂亮了,大嘴式的進氣格柵讓它有了更加威武的視覺效果,車身線條方正,車尾的銀色下護板和雙邊排氣管也體現出了一種運動氛圍。

    翼虎的內飾呈現環抱式設計,駕駛感受更接近一台轎車,而且有着良好的開車視野,車內座椅填充柔軟度適中,乘坐人員也不會顯得難受膈應。

    EcoBoost 180搭載的是一台1.5T的渦輪增壓發動機,最大馬力181匹,峰值扭矩240牛米,傳動系統使用的是福特自家的6AT手自一體變速箱。

    福特翼虎是二十萬級別裏面操控性能和運動感較好的一台SUV,轉向精準,動力儲備也很足,雖然1.5T的發動機在低扭時候感覺那麼一點拖沓,但是由於福特一貫將油門初段調校得較為敏感的特性,所以起步感覺並不會顯得太肉;而2000轉的時候會迎來扭矩爆發,加速超車稍微給油,翼虎的動力響應還是可以讓人滿意的類型。

    個性為先—標緻4008

    指導價格:18.57-23.37萬

    推薦車型:2017款 350THp豪華版

    指導價格:23.07萬

    標緻4008的外觀設計秉承了法系車天馬行空的創意,但卻比以往的標緻品牌車型要來的更加和諧,車身肌肉感很強烈,給人以一種比較明顯的力量感,不同於普通SUV使用相對方正的設計理念,標緻4008的外觀顯得流線型更加強烈,視覺效果更加時尚。

    標緻4008的內飾採用了飛航式設計理念,飛機推桿式的檔把和撥片式的功能鍵顯得頗為新穎,小尺寸方向盤給人以一種很強烈的戰鬥氣息。

    350THp車型搭載的是一台1.6T渦輪增壓發動機,最大馬力167匹,峰值扭矩245牛米,傳動系統也是一款6AT手自一體變速箱。

    標緻4008從外觀上說是一款非常個性前衛的SUV;標緻車型以往給人的感覺似乎一直與操控感和運動格調相關聯,但本次的標緻4008則更趨向於一台普羅大眾所理解的歐系SUV,底盤和懸挂調校偏向舒適,但扭矩爆發平台會來得比福特翼虎更早,1400轉就可以達到峰值扭矩,所以超車感覺還是相當暢快。

    均衡主義—現代途勝

    指導價格:15.99-23.99萬

    推薦車型:2015款 1.6T 雙離合四驅尊貴型

    指導價格:21.59萬

    途勝的外觀設計延續了現代集團的設計理念,細節之處還是比較個性的類型,但整體看上去比較中庸,從外觀上就給人一種比較溫柔的親和力形象。

    內飾的設計也是走的柔和路線,類似展翼的設計語言看上去比較溫馨,加之韓系車型血統,人機工程學的設計布局也十分符合我們中國消費者的使用習慣,溝通感比較優秀。

    動力層面途勝使用的1.6T發動機最大馬力177匹,峰值扭矩265牛米,傳動系統使用的是一套7速雙離合變速箱。

    途勝的性格是一款比較溫順好用的家用SUV,1.6T的動力平台也比較早,1500轉就可以輸出最大扭矩,雙離合變速箱也比較平順好用。車內布局也是非常舒適,從家用層面來說著實是一款不錯的SUV。

    全文總結:以上三款車,從家用務實的角度來看小編推薦的是現代途勝,韓系SUV一直都以比較舒適、平和的性格在市場上立足,而且定價不高,21萬出頭的價格可以購買四驅版本,這是歐美系同級別SUV難以企及的配置,有四驅,在泥濘爛路的信心也更加充足。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

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

    ※回頭車貨運收費標準

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

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

  • 悅翔V7的小弟,據說比大哥操控還棒!

    悅翔V7的小弟,據說比大哥操控還棒!

    動力總成悅翔V3搭載了1。4L自然吸氣發動機+5擋手動變速箱,最大輸出101匹馬力和135牛米。實際表現來看,悅翔V3不同於一般小排量的車會把油門調得較為靈敏,它反而會調得偏肉,起步並不是很利索,但是3擋以下提速還是很給力的,只是滿載時會較為吃力,上個緩坡也不是很帶勁。

    一說起長安,大家首先想到的肯定是CS75。但是除了SUV車型以外,長安還有一些不錯的小車,例如悅翔V3。可能有少部分讀者聽過這台車,那其產品力到底如何呢?

    大哥悅翔V7已經獲得不少人的認可,但是小弟悅翔V3基本處於默默無聞的狀態。其實,悅翔V3的整個設計還是有點小V7的感覺。

    長安汽車-悅翔V3

    指導價:4.69-5.39萬

    外觀設計

    悅翔V3的車身尺寸為4200*1650*1465mm,軸距為2410mm。前臉來看,跟悅翔V7差別不大。發動機艙蓋上的兩條弧線剛強有力,兩顆大燈也顯得炯炯有神,中網上的鍍鉻裝飾條,點綴得剛好,不顯俗氣。

    側面的話,車身還是顯得比較緊湊,只是尾部看起來會有點臃腫。同時輪胎的尺寸也偏小了一點。

    相比起車頭,尾部會顯得有點平庸,僅有車牌上方的鍍鉻裝飾條略微點綴一下。

    內飾設計

    車廂的用料在這個價位來說算是可以的,中控台的設計中規中矩。烤黑鋼琴漆的面板算是為數不多的亮點所在。三副式的方向盤上面沒有任何按鈕,看起來很乾凈,樣式也不錯,給人感覺很好。

    動力總成

    悅翔V3搭載了1.4L自然吸氣發動機+5擋手動變速箱,最大輸出101匹馬力和135牛米。實際表現來看,悅翔V3不同於一般小排量的車會把油門調得較為靈敏,它反而會調得偏肉,起步並不是很利索,但是3擋以下提速還是很給力的,只是滿載時會較為吃力,上個緩坡也不是很帶勁。

    底盤表現

    悅翔V3的前懸架為麥弗遜式獨立懸架,而後懸架則為多連桿獨立懸架。這在同級別車型中是比較罕見的。實際表現來看,底盤的調校較為偏向於操控,過彎時的側傾不大。但是面對顛簸時會有種硬碰硬的感覺。

    空間表現

    受制於2410mm的軸距,後排空間並不充裕。身高為182cm的體驗者坐於後排,腿部僅有4指的空間,而頭部則僅為三指。雖然後排中央的拱起不高而且平整,但是受制於偏短的腿部空間,坐於後排中部的乘客也只能稍微將就一下。

    油耗表現

    多位車主反映的悅翔V3百公里綜合油耗為6.2L,這個数字是相當省的,也是許多車主購買它的原因。

    配置分析

    悅翔V3隻有三個車型可以選擇,但是每個車型又分國四與國五版本,中間價差1000元。這三個車型中,我會推薦中配的手動溫馨型,它比低配的手動美滿型貴了3000元,但是卻多出了主副駕駛座的安全氣囊和后駐車雷達。這兩項配置都很重要,儘管車子本身便宜,但是這種關乎生命安全的配置還是不能省。

    編者總結:

    悅翔V3可以說一直活在大哥悅翔V7的陰影下,同時受制於自身定位的問題,在市場上的表現也較為一般,但同級罕有的後輪獨立懸架賦予了它同級出色的操控性。如果是一台個人用車的話,悅翔V3可以滿足你對操控的幻想。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

    ※回頭車貨運收費標準

  • 只賣11萬起的大氣 好開大三廂家轎 為何油耗還低?

    只賣11萬起的大氣 好開大三廂家轎 為何油耗還低?

    但是,凌派的百公里加速時間約為10。05秒。其實在同級別中是屬於比較快的車型了,它的動力匹配得比較完善。轉向採用的是電動助力,手感輕盈,所以在日常開的時候,能給你輕鬆、好開的感覺。懸架調校比較中性,在舒適性和支撐性方面表現綜合。

    如果您想買一輛10萬左右、空間、動力、外觀設計都表現不錯的合資緊湊型車的話,來自廣汽本田的凌派是一個不錯的選擇!

    如今2016款的凌派採用的是CVT無級變速箱,其實在這款車型剛推出的時候,編者就已經深入試駕過它。今天我們就一起來聊聊這款車的駕駛感受吧!

    廣汽本田-凌派

    指導價:10.98-14.98萬

    我覺得凌派的外觀設計極力地營造出大氣、氣派的感覺。而這種感覺的營造是比較成功的,包括中網誇張的大嘴、粗壯的鍍鉻飾條、加入LED光源的大燈等。

    動力總成

    凌派搭載的是R18系列的發動機,代號為R18ZH,採用了本田特有的i-VTEC技術,最大功率136馬力,峰值扭矩169牛米/4300轉。採用了多點電噴的供油方式。

    變速箱採用的是本田自主研發的CVT變速箱,帶有S擋(運動模式)。底盤方面,它採用前麥弗遜式獨立懸架、后扭力梁式非獨立懸架。

    駕駛起來如何?

    首先,進入到車內,黑色的內飾給人的感覺比較年輕、動感。方向盤的握感不錯,而且多功能按鍵布局簡約,使用起來方便。

    凌派的油門響應靈敏,1.8L發動機也有着不錯的低扭輸出,所以每次起步動力都比較充足。

    動力響應性是不錯的,而CVT變速箱讓動力輸出均勻、持續。但是由於不像AT那樣每次換擋都帶有鏗鏘感,所以總會讓人有“動力不夠強”的錯覺。

    但是,凌派的百公里加速時間約為10.05秒!其實在同級別中是屬於比較快的車型了,它的動力匹配得比較完善。

    轉向採用的是電動助力,手感輕盈,所以在日常開的時候,能給你輕鬆、好開的感覺。懸架調校比較中性,在舒適性和支撐性方面表現綜合。

    油耗怎樣?

    1.8L自動擋車型車主口碑油耗:7.7L/100km

    1.8L手動擋車型車主口碑油耗:7.4L/100km

    CVT變速箱的加入讓凌派的油耗表現不錯,畢竟它採用的是1.8L的自然吸氣發動機。

    競爭對手:

    上汽大眾-朗逸

    指導價:10.99-15.99萬

    凌派的對手很多,因為國內的A級車市場戰火紛飛。而凌派的優惠幅度沒有朗逸、軒逸那麼大。不過凌派的動力總成表現有一定優勢!

    編者語:

    其實凌派的性價比挺高,不錯的配置、充足的空間、動力總成也表現給力。它並不是主打駕控,但是作為一輛家用車來說,還是很給力的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

    ※回頭車貨運收費標準

  • 能接娃又能下賽場,最低不到20萬就能享受這些好車!

    能接娃又能下賽場,最低不到20萬就能享受這些好車!

    聊起高爾夫筆者就忍不住多嘮叨兩句了。“R”代表的是高爾夫家族的精神領袖,碩大的前杠配合上LED日間行車燈的鑲嵌、標誌性藍色和令人熱血沸騰的“R”標識以及雙邊雙出的排氣機構似乎時刻在告誡後車“別惹我”。也許在你的印象中高爾夫本來就應該是一款平平淡淡的家用小車,但調皮的德國人卻非常樂於做扮豬吃老虎這件事,他們腦洞大開為一台買菜車搭載上了280匹馬力的2。

    拖家帶口大空間必然是家人的大需求,特別是當寶寶誕生后各種嬰兒車、採購車、日用品、備用內衣、尿不濕、遮陽傘、毛毯、帽子、小棉襖、防晒霜、常用藥品甚至部分洗漱用具…

    但對於一個男人,在一個依然熱血方剛的年紀,怎能甘心開着一台毫無樂趣可言的買菜車呢?這感覺讓我回想起了電影《速度與激情》男主保羅沃克開着一台MpV的場景,是多麼的滑稽可笑。

    於是乎,既能滿足我們內心一丟丟小自私又能滿足家用大空間的奶爸神車因需而出,擁有霸氣的外觀,無需經過後期的改裝、發動機低轉安靜高轉亢奮,這些車簡直就是奶爸們的性感“尤物”。

    經過篩選我們選出了高爾夫R旅行版、嘉年華ST、寶馬320i旅行版和奔馳A45AMG四款奶爸專用車。(先來說說篩選的方向,主要是從動力以及空間布局的實用性來篩選的)

    對於以上的幾款小鋼炮來講個性、實用、同時富有駕駛樂趣,簡直就是信手拈來的事,而在這些車當中動力最強的鋼炮無疑要屬A45 AMG了。聊到AMG相信大家都知道它是奔馳旗下的高性能產品的締造者,在90年代DTM、ITC等賽車史中AMG多次拿下德國房車錦標賽的年度總冠軍,當年的Mercedes AMG C-Class更是叱吒全球賽車界的風雲存在。

    而如今奔馳慷慨得將AMG帶到民用車上,與之饋贈的還有AMG那如同交響樂般的排氣聲浪,讓你的腎上腺素急劇飆升。若是道路條件允許你大可將它的換擋撥片玩弄於指掌之間,進彎前用方向盤的換擋撥片降擋,此時排氣管會發出如同雷般的回火聲,響徹雲霄。換擋的聲音是那麼的鏗鏘有力,這僅僅只有2.0L排量的發動機在你需要時能給你輸出足足381匹的馬力,將你緊緊壓在座椅上動彈不得,這種眩暈的感覺會一直伴隨你直到把車停下,這時你會感覺整個靈魂終於回到了身體。

    而第二位选手寶馬320i旅行版比起AMG那野獸般的狂野明顯要表現得斯文不少,對飈AMG本應該請出寶馬當家的M系列運動房車,但個人覺得寶馬的精髓並不在動力,人車合一才是寶馬的精髓所在並且三廂版的1M也缺乏了奶爸需要的實用性。

    作為旅行版車型,碩大的尾部略顯累贅,但大家可別被它外觀所欺騙了,320i旅行版在賽道中的表現依舊矯健,雖說它作為拖家帶口的旅行車,但寶馬卻能讓他在實用以及樂趣之間找到非常好的平衡點,不僅有兔子般加速,在彎道的操控同樣也是教科書般的經典。50:50的前後配重,讓它在彎中既不推頭也不甩尾,具有非常高的操控極限,即使在出彎時候給大了油門,車尾也會非常聽話得擺動起來,似乎任何的一切都在你的掌控之中,讓你無比的自信。

    在與“尤物”邂逅的同時還不忘顧家,這簡直就是好男人的典範。320i旅行版後備廂常規容積就達到了495L,內部則相當規整,而且後排摺疊后能跟後備廂地板完全平齊,進深能達到1900mm,應付寶寶的嬰兒車或者搬家時的大件傢具都顯得綽綽有餘。如此實用又不失樂趣的奶爸專用車您的家人還有什麼理由拒絕呢?

    論直線加速、品牌、樂趣BBA的確會更勝一籌,但對於很多喜歡性能車,但駕駛技術又不太高的年輕人來說,擁有四驅的高爾夫R旅行版無疑是更好的選擇。聊起高爾夫筆者就忍不住多嘮叨兩句了。“R”代表的是高爾夫家族的精神領袖,碩大的前杠配合上LED日間行車燈的鑲嵌、標誌性藍色和令人熱血沸騰的“R”標識以及雙邊雙出的排氣機構似乎時刻在告誡後車“別惹我”!

    也許在你的印象中高爾夫本來就應該是一款平平淡淡的家用小車,但調皮的德國人卻非常樂於做扮豬吃老虎這件事,他們腦洞大開為一台買菜車搭載上了280匹馬力的2.0T發動機,能幹翻BBA的全時四驅底盤,甚至在為它加長了屁股,讓它搖身一變,成為一台下的了菜市場,豁得了賽道的性能小鋼炮。

    百公里加速5秒出頭的成績,讓這樣一個短跑健將在城市中只做一個小小文員這顯然是不合適的,運動化的座椅,平底黑色打孔的皮革方向盤,底速高達320km/h的時速,無時無刻都在挑逗你的賽車神經。即使你是一個從未開過性能車的小白也無需擔心,四驅系統就像你的老師一筆一劃教導你如何快速劈彎。將這樣一款動力強大,全路況性能和實用都兼具的車型,交給你妻子讓她日常通勤接送孩子也完全沒問題,在周末你還可以將高爾夫R帶到賽道滿足你的小小激情心思,這無疑是人生最美妙的事。

    聊到小鋼炮怎麼能少了福特ST系列搭載手動“波棍”的車型呢?汽車界的小鋼炮不凸顯出個“小”字何來玩味,相比起那些大跑車它們身材嬌小,但戰鬥力十足並且在彎中表現更為矯健,例如我們今天介紹給奶爸們的嘉年華ST就是這樣一台車。

    三門掀背的結構讓嘉年華ST顯露出了與家用車不同的調性,看到這樣一台存粹的駕駛機器它會讓你時刻忘記了自己是奶爸身份,令你興奮得犹如孩子一般迫不及待,踩下離合按動啟動按鈕。短促具有極強吸入感的換擋節奏讓你下意識得撫摸着擋把愛不釋手,你也不必擔心它是否會輕易死火,離合極大的寬容度讓你可以從容得駕馭它,乖巧、聽話正是它作為城市代步車溫和的一面。

    而在你將它駛離喧嘩的街道,開向郊區將油門毫不留情面的踩下去,來自後方的排氣閥門會瞬間打開,渾厚的排氣聲浪會充斥着你的耳膜,1.6T的小心臟會瘋狂得拉扯着你,在8秒內把你帶到國內高速公路最高限速。

    如此鏗鏘有力的動力輸出,來自於這麼一台小車中就足矣令人吃驚了,但最能體現它駕駛樂趣的恐怕還是它極其短的懸挂行程、以及極其靈活的車尾表現,筆者試圖在彎中逼出嘉年華ST前驅車推頭的彎道極限,而它反饋給你的卻是略微轉向過度的感覺。是誰說前驅車就沒駕駛樂趣就得推頭的,嘉年華ST無疑給這些人狠狠得打臉了。彎中車尾極其靈活,動作如魚得水一般在彎心將車頭送入彎中,這種感覺真是太美妙了。

    由此可見,並非所有的奶爸車都是毫無樂趣可言的。這些車你可以非常放心得載着你的家人日常使用,寬敞實用的內部空間不會讓你的家人對你產生任何抱怨。而在你寂寞難耐的時候,偶爾和它們約上一炮那也是蠻爽的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

    ※回頭車貨運收費標準

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

  • 誰說豪車才有樂趣?告訴你十來萬的車也很好開(無水印)

    誰說豪車才有樂趣?告訴你十來萬的車也很好開(無水印)

    >>>>前言:空間大,省油,這是中國消費者選車購車時的剛需,其中也有一部分消費者喜歡以“玩”為主,“玩”指的不是改裝也不是要把車開上賽道比賽,而是“玩”車的樂趣。

    >>>>

    前言:空間大,省油,這是中國消費者選車購車時的剛需,其中也有一部分消費者喜歡以“玩”為主,“玩”指的不是改裝也不是要把車開上賽道比賽,而是“玩”車的樂趣。

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

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • 沒了IDE,你的Java項目還能Run起來嗎~

    沒了IDE,你的Java項目還能Run起來嗎~

    計算機只能識別機器碼0101…編程語言->能執行的機器碼 需要經過 預處理->編譯->彙編->鏈接->機器碼過程。一個語言處理系統的示意圖如下:

    編譯器 是將源語言程序一次性翻譯成一個等價的,用目標語言編寫的程序。還存在另一種常見的語言處理器,解釋器:它是逐個語句的執行源語言程序。由一個編譯器產生的目標語言程序通常比一個解釋器快,但解釋器的錯誤診斷效果通常更好。

    Java語言處理器結合了編譯和解釋的過程。一個.Java源程序首先被編譯為.class字節碼文件,被加載到虛擬機中,然後由虛擬機將字節碼翻譯成機器碼。

    虛擬機的好處在於:一旦一個程序被轉換成 Java 字節碼,那麼它便可以在不同平台上的虛擬機實現里運行。實現一次編寫,到處運行。另外一個好處是它帶來了一個託管環境。這個託管環境能夠代替我們處理一些代碼中冗長而且容易出錯的部分,如自動內存管理與垃圾回收。

    在Hotspot中,虛擬機翻譯字節碼有兩種方式:

    1.解釋執行
    即逐條將字節碼翻譯成機器碼並執行。

    2.即時編譯
    即將一個方法中包含的所有字節碼編譯成機器碼后再執行。

    前者的優勢在於無需等待編譯,而後者的優勢在於實際運行速度更快。HotSpot 默認採用混合模式,綜合了解釋執行和即時編譯兩者的優點。它會先解釋執行字節碼,而後將其中反覆執行的熱點代碼,以方法為單位進行即時編譯。

    即時編譯建立在程序符合二八定律的假設上,也就是百分之二十的代碼佔據了百分之八十的計算資源。

    好了,裝X結束。

    阿姨知道的編譯知識全在上面了。。(っ╥╯﹏╰╥c)

    如題,下面我們來看一下讓Java項目運行起來我們能做什麼。

    我們能做的很簡單,當然不是寫虛擬機。我們只需要:

    1.執行command javac,將.Java文件變為.class文件。
    2.執行command java,讓.class文件運行起來。

    也就是 執行command :)

    Java程序的運行方式

    Java程序可以通過java命令運行.class文件運行可執行Jar文件
    我們先看第一種方式:從Hello World開始。

    運行.class文件

    Step1:編寫Java文件

    Step2:執行 command javac

    將.Java文件變為.class文件

    小貼士:class文件的全路徑名是包名目錄+ 類文件名。

    Step3:執行 command java

    運行.class文件

    神奇,我們沒有用IDE讓Java程序運行起來了 :)

    小夥伴先別噴老阿姨,哪特么有這麼簡單的Java項目啊。。我們工作中用的明明都是Jar文件啊…
    Jar文件咋運行啊!!

    運行可執行Jar文件

    Jar文件是基於ZIP文件格式的一種文件格式,它將大量的Java類文件、相關的元數據和資源(文本、圖片等)文件聚合到一個Jar文件中,此外還包含一個可選的META-INF文件夾。這個文件夾下的文件或文件夾主要用來打包和擴展配置信息,包括安全,版本,擴展程序和服務等。如MANIFEST.MF文件定義了擴展和打包的相關數據信息。
    一個Jar文件通常在項目中用作第三方類庫使用,也是項目構建的一部分。

    生成一個Jar文件大致分為兩步:

    1.將源文件編譯為.class文件

    2.通過 command jar命令將.class文件,資源文件等等打成一個文件格式的Jar文件。

    我們以一個SbDemo項目為例來看Jar文件的打包和運行。項目目錄結構如下:

    Test2.java中調用了Test1.java的方法,

    我們需要先將Test1.java編譯並打成一個Test1.jar文件,然後通過Test1.jar將Test2.java編譯並打成一個可執行的Test2.jar文件

    可執行和不可執行的Jar文件 區別在於是否在Jar文件中指定了main方法的入口,我們後面再看。

    Step1:Test1.java的編譯

    Step2:將編譯后的classes/com/Test1.class文件打成一個Test1.jar包

    Java中和jar包相關的命令是jar命令,生成一個jar包我們需要定義信息文件(manifest-file),它可以定義所生成jar包的classpath類搜索路徑,jar包的入口類等等。可以理解為與Jar包相關的元數據配置信息
    Step2.1 書寫信息文件
    這裏我們使用resources/manifest-test1.text文件作為信息文件

    是的,Test1.java太簡單了,就是打成一個可被他人引用的jar包,信息文件不重要。
    Step2.2 執行打包命令

    Step3. 編譯Test2.java文件
    因為Test2.java中引用了com.Test1類,所以我們需要在編譯時指定Classpath路徑。
    Classpath:顧名思義,是指待編譯類依賴的類所在路徑位置。我們可以通過 javac 的 -cp 參數指定。
    關於編譯時classpath的值優先級如下:

    • 如果沒有傳入classpath參數,將使用環境變量CLASSPATH的值。(小夥伴不知道環境變量咋查看和設置?去看阿姨的上一篇文章:)
    • 如果沒有發現環境變量CLASSPATH,將使用 執行命令的當前文件夾(.)。
    • 如果javac命令行 通過-classpath or -cp參數指定了類路徑值,則優先級最高。

    這裏我們使用-cp指定Test1.jar所在位置

    可以看到classes目錄下已經生成了com2/Test2.class文件了。

    Step4. 將編譯后的Test2.class和它依賴的Test1.jar一起打成一個可執行的Jar包
    Step4.1 書寫信息文件
    這時候我們使用信息文件resources/manifest-test2.text文件指定這些信息

    Step4.2 執行Jar包生成命令

    可以看到在lib目錄下生成了Test2.jar

    Step5.運行我們的可執行Jar

    大功告成了,我們的SbDemo項目Run起來了…

    當然實際項目不可能人肉編譯,打包。我們需要通過Maven/Gradle等構建工具,幫助我們管理代碼之間的Jar包依賴,構建,部署…我們可能大多時候通過點一下IDE就託管了Maven的構建部署命令。

    拿Maven舉例子,Maven首先定義了一套項目結構,我們按照它的結構書寫代碼,引入各個模塊所需要的Jar包依賴。然後Maven可以通過自己的生命周期管理項目的清理,構建,打包,部署階段。每個階段有對應的Maven插件執行相應的目標。IDE又整合了Maven,使我們通過點吧點吧按鈕就完成了項目的運行。

    但是當一個項目並沒有按照規範的構建工具結構搭建,或者項目沒有成功運行報錯時,了解Java實際的編譯運行過程會對理解、解決這類問題有所幫助。

    好啦,限於篇幅,阿姨先不講這些年Maven躺過的坑了,有想看的嗎?關注,在看,轉發三連回應下 >-<

    參考資料:
    [1].《編譯原理》序 (゚´ω`゚)゚
    [2].https://time.geekbang.org/column/article/11289

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

    ※回頭車貨運收費標準

  • 【asp.net core 系列】8 實戰之 利用 EF Core 完成數據操作層的實現

    【asp.net core 系列】8 實戰之 利用 EF Core 完成數據操作層的實現

    0. 前言

    通過前兩篇,我們創建了一個項目,並規定了一個基本的數據層訪問接口。這一篇,我們將以EF Core為例演示一下數據層訪問接口如何實現,以及實現中需要注意的地方。

    1. 添加EF Core

    先在數據層實現層引入 EF Core:

    cd Domain.Implements
    dotnet add package Microsoft.EntityFrameworkCore
    

    當前項目以SqlLite為例,所以再添加一個SqlLite數據庫驅動:

    dotnet add package Microsoft.EntityFrameworkCore.SQLite
    

    刪除 Domain.Implements 里默認的Class1.cs 文件,然後添加Insfrastructure目錄,創建一個 DefaultContext:

    using Microsoft.EntityFrameworkCore;
    
    namespace Domain.Implements.Insfrastructure
    {
        public class DefaultContext : DbContext
        {
            private string ConnectStr { get; }
            public DefaultContext(string connectStr)
            {
                ConnectStr = connectStr;
            }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlite(ConnectStr);//如果需要別的數據庫,在這裏進行修改
            }
        }
    }
    

    2. EF Core 批量加載模型

    通常情況下,在使用ORM的時候,我們不希望過度的使用特性來標註實體類。因為如果後期需要變更ORM或者出現其他變動的時候,使用特性來標註實體類的話,會導致遷移變得複雜。而且大部分ORM框架的特性都依賴於框架本身,並非是統一的特性結構,這樣就會造成一個後果:本來應該是對調用方隱藏的實現就會被公開,而且在項目引用關係中容易出現循環引用。

    所以,我在開發中會尋找是否支持配置類,如果使用配置類或者在ORM框架中設置映射關係,那麼就可以保證數據層的純凈,也能實現對調用方隱藏實現。

    EF Core的配置類我們在《C# 數據訪問系列》中關於EF的文章中介紹過,這裏就不做過多介紹了(沒來得及看的小夥伴們不着急,後續會有一個簡單版的介紹)。

    通常情況下,配置類我也會放在Domain.Implements項目中。現在我給大家介紹一下如何快速批量加載配置類:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(this.GetType()),
    		t => t.GetInterfaces().Any(i => t.Name.Contains("IEntityTypeConfiguration")));
    }
    

    現在版本的EF Core支持通過Assembly加載配置類,可以指定加載當前上下文類所在的Assembly,然後篩選實現接口中包含IEntityTypeConfiguration的類即可。

    3. 使用EF Core實現數據操作

    我們已經創建好了一個EF Context,那麼現在就帶領大家一起看一下,如何使用EF來實現 上一篇《「asp.net core」7 實戰之 數據訪問層定義》中介紹的數據訪問接口:

    新建一個BaseRepository類,在Domain.Implements項目的Insfrastructure 目錄下:

    using Domain.Infrastructure;
    using Microsoft.EntityFrameworkCore;
    
    namespace Domain.Implements.Insfrastructure
    {
        public abstract class BaseRepository<T> : ISearchRepository<T>, IModifyRepository<T> where T : class
        {
            public DbContext Context { get; }
            protected BaseRepository(DbContext context)
            {
                Context = context;
            }
        }
    }
    

    先創建以上內容,這裏給Repository傳參的時候,使用的是EFCore的默認Context類不是我們自己定義的。這是我個人習慣,實際上並沒有其他影響。主要是為了對實現類隱藏具體的EF 上下文實現類。

    在實現各接口方法之前,創建如下屬性:

    public DbSet<T> Set { get => Context.Set<T>(); }
    

    這是EF操作數據的核心所在。

    3.1 實現IModifyRepository接口

    先實現修改接口:

    public T Insert(T entity)
    {   
        return Set.Add(entity).Entity;
    }
    
    public void Insert(params T[] entities)
    {
        Set.AddRange(entities);
    }
    
    public void Insert(IEnumerable<T> entities)
    {
        Set.AddRange(entities);
    }
    public void Update(T entity)
    {
        Set.Update(entity);
    }
    
    public void Update(params T[] entities)
    {
        Set.UpdateRange(entities);
    }
    
    public void Delete(T entity)
    {
        Set.Remove(entity);
    }
    
    public void Delete(params T[] entities)
    {
        Set.RemoveRange(entities);
    }
    

    在修改接口裡,我預留了幾個方法沒有實現,因為這幾個方法使用EF Core自身可以實現,但實現會比較麻煩,所以這裏藉助一個EF Core的插件:

    dotnet add package Z.EntityFramework.Plus.EFCore
    

    這是一個免費開源的插件,可以直接使用。在Domain.Implements 中添加后,在BaseRepository 中添加如下引用:

    using System.Linq;
    using System.Linq.Expressions;
    

    實現方法:

    public void Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updator)
    {
        Set.Where(predicate).UpdateFromQuery(updator);
    }
    
    public void Delete(Expression<Func<T, bool>> predicate)
    {
        Set.Where(predicate).DeleteFromQuery();
    }
    
    public void DeleteByKey(object key)
    {
        Delete(Set.Find(key));
    }
    
    public void DeleteByKeys(params object[] keys)
    {
        foreach (var k in keys)
        {
            DeleteByKey(k);
        }
    }
    

    這裏根據主鍵刪除的方法有個問題,我們無法根據條件進行刪除,實際上如果約定泛型T是BaseEntity的子類,我們可以獲取到主鍵,但是這樣又會引入另一個泛型,為了避免引入多個泛型根據主鍵的刪除就採用了這種方式。

    3.2 實現ISearchRepository 接口

    獲取數據以及基礎統計接口:

    public T Get(object key)
    {
        return Set.Find(key);
    }
    
    public T Get(Expression<Func<T, bool>> predicate)
    {
        return Set.SingleOrDefault(predicate);
    }
    
    public int Count()
    {
        return Set.Count();
    }
    
    public long LongCount()
    {
        return Set.LongCount();
    }
    
    public int Count(Expression<Func<T, bool>> predicate)
    {
        return Set.Count(predicate);
    }
    
    public long LongCount(Expression<Func<T, bool>> predicate)
    {
        return Set.LongCount(predicate);
    }
    
    public bool IsExists(Expression<Func<T, bool>> predicate)
    {
        return Set.Any(predicate);
    }
    

    這裡有一個需要關注的地方,在使用條件查詢單個數據的時候,我使用了SingleOrDefault而不是FirstOrDefault。這是因為我在這裏做了規定,如果使用條件查詢,調用方應該能預期所使用條件是能查詢出最多一條數據的。不過,這裏可以根據實際業務需要修改方法:

    • Single 返回單個數據,如果數據大於1或者等於0,則拋出異常
    • SingleOrDefault 返回單個數據,如果結果集沒有數據,則返回null,如果多於1,則拋出異常
    • First 返回結果集的第一個元素,如果結果集沒有數據,則拋出異常
    • FirstOrDefault 返回結果集的第一個元素,如果沒有元素則返回null

    實現查詢方法:

    public List<T> Search()
    {
        return Query().ToList();
    }
    
    public List<T> Search(Expression<Func<T, bool>> predicate)
    {
        return Query(predicate).ToList();
    }
    
    public IEnumerable<T> Query()
    {
        return Set;
    }
    
    public IEnumerable<T> Query(Expression<Func<T, bool>> predicate)
    {
        return Set.Where(predicate);
    }
    
    public List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order)
    {
        return Search(predicate, order, false);
    }
    
    public List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order, bool isDesc)
    {
        var source = Set.Where(predicate);
        if (isDesc)
        {
            source = source.OrderByDescending(order);
        }
        else
        {
            source = source.OrderBy(order);
        }
        return source.ToList();
    }
    

    這裏我盡量通過調用了參數最多的方法來實現查詢功能,這樣有一個好處,小夥伴們可以想一下哈。當然了,這是我自己覺得這樣會好一點。

    實現分頁:

    在實現分頁之前,我們知道當時我們定義的分頁參數類的排序字段用的是字符串,而不是lambda表達式,而Linq To EF需要一個Lambda表示才可以進行排序。這裏就有兩種方案,可以自己寫一個方法,實現字符串到Lambda表達式的轉換;第二種就是借用三方庫來實現,正好我們之前引用的EF Core增強插件里有這個功能:

    var list = context.Customers.OrderByDescendingDynamic(x => "x.Name").ToList();
    

    這是它給出的示例。

    我們可以先依此來寫一份實現方法:

    public PageModel<T> Search(PageCondition<T> condition)
    {
        var result = new PageModel<T>
        {
            TotalCount = LongCount(condition.Predicate),
            CurrentPage = condition.CurrentPage,
            PerpageSize = condition.PerpageSize,
        };
        var source = Query(condition.Predicate);
        if (condition.Sort.ToUpper().StartsWith("a")) // asc
        {
            source = source.OrderByDynamic(t => $"t.{condition.OrderProperty}");
        }
        else // desc
        {
            source = source.OrderByDescendingDynamic(t => $"t.{condition.OrderProperty}");
        }
        var items = source.Skip((condition.CurrentPage -1)* condition.PerpageSize).Take(condition.PerpageSize);
        result.Items = items.ToList();
        return result;
    }
    

    回到第一種方案:

    我們需要手動寫一個字符串的處理方法,先在Utils項目創建以下目錄:Extend>Lambda,並在目錄中添加一個ExtLinq類,代碼如下:

    using System.Linq;
    using System.Linq.Expressions;
    using System.Text.RegularExpressions;
    
    namespace Utils.Extend.Lambda
    {
        public static class ExtLinq
        {
            public static IQueryable<T> CreateOrderExpression<T>(this IQueryable<T> source, string orderBy, string orderAsc)
            {
                if (string.IsNullOrEmpty(orderBy)|| string.IsNullOrEmpty(orderAsc)) return source;
                var isAsc = orderAsc.ToLower() == "asc";
                var _order = orderBy.Split(',');
                MethodCallExpression resultExp = null;
                foreach (var item in _order)
                {
                    var orderPart = item;
                    orderPart = Regex.Replace(orderPart, @"\s+", " ");
                    var orderArry = orderPart.Split(' ');
                    var orderField = orderArry[0];
                    if (orderArry.Length == 2)
                    {
                        isAsc = orderArry[1].ToUpper() == "ASC";
                    }
                    var parameter = Expression.Parameter(typeof(T), "t");
                    var property = typeof(T).GetProperty(orderField);
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
                    resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending",
                        new[] {typeof(T), property.PropertyType},
                        source.Expression, Expression.Quote(orderByExp));
                }
    
                return resultExp == null
                    ? source
                    : source.Provider.CreateQuery<T>(resultExp);
            }
        }
    }
    

    暫時不用關心為什麼這樣寫,後續會為大家分析的。

    然後回過頭來再實現我們的分頁,先添加Utils 到Domain.Implements項目中

    cd ../Domain.Implements # 進入Domain.Implements 項目目錄
    dotnet add reference ../Utils
    
    public PageModel<T> Search(PageCondition<T> condition)
    {
        var result = new PageModel<T>
        {
            TotalCount = LongCount(condition.Predicate),
            CurrentPage = condition.CurrentPage,
            PerpageSize = condition.PerpageSize,
        };
        var source = Set.Where(condition.Predicate).CreateOrderExpression(condition.OrderProperty, condition.Sort);
        var items = source.Skip((condition.CurrentPage -1)* condition.PerpageSize).Take(condition.PerpageSize);
        result.Items = items.ToList();
        return result;
    }
    

    記得添加引用:

    using Utils.Extend.Lambda;
    

    在做分頁的時候,因為前台傳入的參數大多都是字符串的排序字段,所以到後端需要進程字符串到字段的處理。這裏的處理利用了C# Expression的一個技術,這裏就不做過多介紹了。後續在.net core高級篇中會有介紹。

    4. 總結

    到目前為止,看起來我們已經成功實現了利用EF Core為我們達成 數據操作和查詢的目的。但是,別忘了EF Core需要手動調用一個SaveChanges方法。下一篇,我們將為大家介紹如何優雅的執行SaveChanges方法。

    這一篇介紹到這裏,雖然說明不是很多,但是這也是我在開發中總結的經驗。

    更多內容煩請關注我的博客《高先生小屋》

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

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

  • Python 3.9 beta2 版本發布了,看看這 7 個新的 PEP 都是什麼?

    原作:Jake Edge

    譯者:豌豆花下貓@Python貓

    英文:https://lwn.net/Articles/819853/

    隨着 Python 3.9.0b1 的發布,即開發周期中計劃的四個 beta 版本的首個,Python 3.9 的功能已經是完善了。在 10 月發布最終版本之前,還會有許多測試和穩定性方面的工作要做。

    (譯註:beta1 版本發佈於 5 月 18 日,作者文章寫於 5 月 20,而到本篇譯文發布時,beta2 剛好在今天即 6 月 9 日發布,這是一個巧合!)

    該發布說明中列出了被 3.9 接受的 7 個 Python 增強提案(PEP)。我們研究了其中的一些 PEP,看到有一些更新。現在似乎是一個介紹 Python 3.9 帶來的一些東西的好時機。

    1、字符串操作

    有時最簡單(表明上的)的事情最困難,或者至少會引起巨大的討論。其中大部分的爭議是關於命名(還能是什麼?),但是給標準字符串對象添加函數,來刪除前綴和後綴,這種想法是毫無爭議的。

    是否可以將那些詞綴(前綴和後綴的統稱)指定為序列,以便在一次調用中處理多個詞綴,這一點尚不明確,最後它被從提案中刪除了,等待着其他人再次推動更改。

    在 3 月底,Dennis Sweeney 在 python-dev 郵件列表上請求核心開發者支持 PEP 616(“字符串刪除前綴和後綴的方法”)。他指出了自 2019 年 3 月以來關於該話題的 python-ideas 討論。埃里克·史密斯(Eric V. Smith)同意支持該 PEP,這促使 Sweeney 發布並啟動了討論。

    在最初版本中,他使用 cutprefix() 和 cutsuffix() 作為要添加給字符串對象的方法名。四種類型的 Python 對象將獲得新的方法:str(Unicode 字符串),byte(二進制序列),bytearray(可變的二進制序列)和 collections.UserString(字符串對象的一種封裝)。

    它的寫法如下:

    'abcdef'.cutprefix('abc') # 返回'def'
    'abcdef'.cutsuffix('ef') # 返回'abcd'
    

    針對命名部分,出現了一大堆的建議。基本上很少有人喜歡“cut”,因此“strip”、“strim”和“remove”被提出來了,並且都獲得了一些支持。

    stripprefix() 以及 stripsuffix() 由於 PEP 中指出的一種理由,至少是被部分地反對了;現有的“strip”函數令人困惑,因此應避免重用該名稱。

    str.lstrip() 和 str.rstrip() 方法也用於刪除前導字符和尾隨字符,但是它們對於真正在尋找 cutprefix() 功能的程序員來說是一個困惑的來源。

    *strip() 在調用時接收一個字符串參數,但會將其視為一組字符,並從字符串開頭或結尾消除:

    'abcdef'.lstrip('abc') # 返回“def”,符合預期
    'abcbadefed'.lstrip('abc') # 返回'defed',完全不符合預期
    

    最終,removeprefix() 和 removesuffix() 似乎佔據了上風,這正是 Sweeney 最終改成的版本。Guido van Rossum 也支持這些名字。

    埃里克·法格倫(Eric Fahlgren)這樣搞笑地總結了命名的爭論:

    我認為如果你先寫文檔,則名稱的選擇會更容易些:

    cutprefix – 刪除指定的前綴。

    trimprefix – 刪除指定的前綴。

    stripprefix – 刪除指定的前綴。

    removeprefix – 刪除指定的前綴。廢話

    Sweeney 更新了 PEP,回應了許多評論,但還增加了提議將字符串元組作為詞綴的功能(可以在 PEP GitHub 倉庫中看到該版本)。

    但是史蒂文·達普拉諾(Steven D’Aprano)不確定這樣做是否合理。他指出,唯一接受元組參數的字符串操作是 str.startswith() 和 str.endswith(),而它們不返回字符串(只是一個布爾值)。他懷疑添加這一種接收元組參數卻返回字符串的方法,因為無論選擇何種規則來處理元組,對於某些人來說都是“錯誤的”選擇。

    例如:

    這裏的困難在於,如果兩個或多個前綴都能匹配,則“剪切這些前綴中的一個”的概念是模稜兩可的。對 startwith 沒有區別:

     "extraordinary".startswith(('ex', 'extra'))
    

    因為是從左到右,從最短到最大,甚至是隨機順序匹配都為True。但是對於 cutprefix,應該刪除哪個前綴?

    如他所說,建議的規則是使用從左到右處理元組的第一個匹配字符串,但是有些人可能想要最長的匹配或最後一個匹配;這一切都取決於使用的上下文。他建議在提交添加此類行為之前,要給該功能更多的“浸泡時間”(譯註:即預備時間):“在添加多前綴/後綴的支持之前,我們首先應該對簡單的情況進行一些實際的體驗。”

    伊桑·弗曼(Ethan Furman)同意達普拉諾(D’Aprano)的意見。但是維克托·斯汀納(Victor Stinner)強烈贊成元組參數的想法,只不過,他還想知道當傳入的元組有空字符串時,會怎麼處理。根據 PEP 提議,在處理元組時遇到空字符串(實際上可以匹配任何內容)只會返回原始字符串,這會導致令人驚訝的結果:

    cutsuffix("Hello World", ("", " World"))    # 返回 "Hello World"
    cutsuffix("Hello World", (" World", ""))    # 返回 "Hello"
    

    這個例子不太明顯;詞綴不一定是硬編碼的,因此空字符串可能會溜進意想不到的位置。Stinner 建議如果遇到空字符串,則拋出 ValueError,類似於 str.split()。但是 Sweeney 決定完全刪除元組參數功能,以便“允許對此有更強見解的人在另外的 PEP 中提出並捍衛一系列的語義”。他在 3 月 28 日發布了該 PEP 的最新版本。

    4 月 9 日,Sweeney 發起了一個指導委員會 issue,請求對其 PEP 進行評審。4 月 20 日,Stinner 代表委員會接受了該提案。

    這是一個很小的更改,但值得花時間確保它具有長期適用的接口(和語義)。我們將在 Python 3.9 中看到 removeprefix() 和removesuffix()。

    2、新解析器

    並不令人感到驚訝的是,指導委員會已經接受了我們在 4 月中旬介紹過的 CPython 新解析器。PEP 617(“CPython 新的 PEG 解析器”)由 Python 創始人即前仁慈的獨裁者(BDFL) Guido van Rossum 以及 Pablo Galindo Salgado 和 Lysandros Nikolaou 共同提出。

    它已經運行良好,並且在現有解析器的速度和內存使用方面提升了 10% 以內的性能。由於解析器是基於解析表達語法(PEG),因此也將簡化語言規範。CPython 現有的 LL(1) 解析器存在諸多缺點和一些 hack,新的解析器將會消除掉。

    這一更改為 Python 超越 LL(1) 語法鋪平了道路,儘管現有語言並不完全是 LL(1)。這一更改不會太快,因為計劃是在 Python 3.9 的命令行中提供開關,保持現有解析器可用。

    但是 Python 3.10 將刪除現有的解析器,這可能會導致語言變更。如果做了那些更改,那麼,其它的 Python 實現(例如 PyPy 和 MicroPython)就需要切換解析器的 LL(1) 實現,以便跟上語言規範的要求。這可能會使核心開發者暫停進行此類更改。

    3、更多內容

    我們在三月初查看了 PEP 615(“在標準庫中支持 IANA 時區數據庫”)。它將在標準庫中添加一個zoneinfo 模塊,該模塊將有助於從 IANA 時區數據庫中(也稱為“Olson數據庫”)獲取時區信息,以填充時區對象。在撰寫本文時,它看起來很順利。

    在 3 月底,Paul Ganssle 請求就該 PEP 作出決議。他認為在一個有趣的時間範圍內接受它,可能會很有趣:

    … 我希望(出於異想天開的原因)在 4 月 5 日(星期日)UTC 時間 02:00-04:00 或 13:00-17:30 之間接受它,因為這些時間代表着地球上某些地方的不明確時間(主要在澳大利亞)。還有另一個時機,那就是在 4 月 19 日星期日 UTC 01:00-03:00 之間,這段時間在西撒哈拉是不明確的。

    他意識到這可能難以實現,它當然不是優先考慮的事。指導委員會沒有錯過第二個時間窗太多;Barry Warsaw 於 4 月 20 日宣布接受該 PEP。

    Python 現在將具有一種機制來訪問系統的時區數據庫,以創建和處理時區。另外,Python 軟件包索引(PyPI)中有一個 tzdata 模塊,它為缺少 IANA 數據的系統提供這些數據;它將由 Python 核心開發者維護。

    PEP 593(“靈活的函數和變量註釋”)添加了一種將上下文特定的(context-specific)元數據與函數和變量關聯的方法。實際上,type hint 註解已擠出了很多年前在 Python 3.0 中實現的 PEP 3107(“函數註釋”)中設想的其它用例。PEP 593 使用註解的(Annotated)類型提示為這些用例創建了一種新的機制。

    PEP 585(“標準集合中的類型提示泛型”)提供了另一種清除方法。它將允許刪除在 typing 模塊中維護的一組并行的類型別名,以支持泛型。例如,type.List 類型將不再需要支持諸如“dict[str,list[int]]”之類的註解(例如,一個帶有字符串鍵和整數列表的值的字典)。

    字典“加法”的聯合操作也會是 Python 3.9 的一部分。它曾不時引起爭議,但是 2 月中旬,PEP 584(“給字典添加聯合操作符”)被 Van Rossum 推薦採納。指導委員會迅速同意了,該特性於 2 月 24 日合入。

    最後一個 PEP 是 PEP 602(“Python 的年度發布周期”)。如提案所書,它將發布節奏從每 18 個月更改為每年一次。但是,開發和發布周期是重疊的,因此整個功能開發需要 12 個月的時間。當第一個 Python 3.9 beta 版本發布時(即現在),Python 3.10 的功能開發就開始了。請繼續關注來年的下一輪 PEP。

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

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

  • [原創][開源] SunnyUI.Net 主題

    [原創][開源] SunnyUI.Net 主題

    SunnyUI.Net, 基於 C# .Net WinForm 開源控件庫、工具類庫、擴展類庫、多頁面開發框架

    • Blog: https://www.cnblogs.com/yhuse
    • Gitee: https://gitee.com/yhuse/SunnyUI
    • GitHub: https://github.com/yhuse/SunnyUI
    • 幫助文檔目錄: https://www.cnblogs.com/yhuse/p/SunnyUI_Menu.html
    • 歡迎交流,QQ群:  56829229 (SunnyUI技術交流群) 

    主題

    1、Color 色彩

    SunnyUI為了避免視覺傳達差異,使用一套特定的調色板來規定顏色,為你所搭建的產品提供一致的外觀視覺感受。主要顏色參照Element(https://element.eleme.cn/

    • 主色

    SunnyUI主要品牌顏色是鮮艷、友好的藍色。

    • 輔助色

    除了主色外的場景色,需要在不同的場景中使用(例如紅色表示危險的操作)。

    • 中性色

    中性色用於文本、背景和邊框顏色。通過運用不同的中性色,來表現層次結構。

     

    2、Rect邊框

    我們對邊框進行統一規範,可用於按鈕、卡片、彈窗等組件里。

    主要屬性如下:

    • RectColor:邊框顏色
    • RectDisableColor:控件不可用時邊框顏色
    • RectSides:邊框显示方向
    • 無:不显示邊框
    • 全部:显示全部邊框
    • 頂:显示頂部邊框
    • 底:显示底部邊框
    • 左:显示左側邊框
    • 右:显示右側邊框

    注:邊框显示和圓角設置相關,如果一側的邊框兩端端點為圓角,則此邊框必定显示。

     

    3、Radius圓角

    我們提供了以下幾種圓角樣式,以供選擇。默認圓角大小為5px。

    主要屬性如下:

    Radius:圓角大小

    RadiusSides:显示四個角圓角的显示與否

    • 圓角不显示

    • 默認圓角大小為5px

    • 圓角大小與控件高度相等時,显示大圓角

    • 可通過四個角圓角的設置,對控件組合显示

     

    4、Font字體

    默認字體為:微軟雅黑, 12pt

     

    5、Style主題

    SunnyUI包含 Element 風格主題 11 個,DotNetBar 主題 3 個,其他主題 2 個,包含主題管理組件 UIStyleManager,可自由切換主題。

    •  UIStyleManager

    參考SunnyUI.Demo.exe,將UIStyleManager放置在主窗體上,通過選擇UIStyleManager的屬性Style,或者通過代碼設置統一主題風格。

    UIStyleManager.Style = style;

     

    • Style主要屬性如下:

    Style:設置主題風格

    StyleCustomMode:是否為自定義主題,設置為False時使用UIStyleManager提供的統一主題風格,設置為Ture時可手動調整控件配色,不受UIStyleManager約束。

     

    • UIStyle.Blue

    • UIStyle.Green

    • UIStyle.Orange

    • UIStyle.Red

    • UIStyle.Gray

    • UIStyle.White

    • UIStyle.DarkBlue

    • UIStyle.Black

    • UIStyle.Office2010Blue

    • UIStyle.Office2010Silver

    • UIStyle.Office2010Black

      

    原創文章,轉載請保留鏈接 Sunny’s blog

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

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

  • Spring Boot 教程 – Elasticsearch

    Spring Boot 教程 – Elasticsearch

    1. Elasticsearch簡介

    Elasticsearch是一個基於Lucene的搜索服務器。它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用Java語言開發的,並作為Apache許可條款下的開放源碼發布,是一種流行的企業級搜索引擎。Elasticsearch用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。官方客戶端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和許多其他語言中都是可用的。根據DB-Engines的排名显示,Elasticsearch是最受歡迎的企業搜索引擎,其次是Apache Solr,也是基於Lucene。以後再給大家詳細介紹solr。

    它能很方便的使大量數據具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸縮性,能使數據在生產環境變得更有價值。Elasticsearch 的實現原理主要分為以下幾個步驟,首先用戶將數據提交到Elasticsearch 數據庫中,再通過分詞控制器去將對應的語句分詞,將其權重和分詞結果一併存入數據,當用戶搜索數據時候,再根據權重將結果排名,打分,再將返回結果呈現給用戶。

    Elasticsearch可以用於搜索各種文檔。它提供可擴展的搜索,具有接近實時的搜索,並支持多租戶。”Elasticsearch是分佈式的,這意味着索引可以被分成分片,每個分片可以有0個或多個副本。每個節點託管一個或多個分片,並充當協調器將操作委託給正確的分片。再平衡和路由是自動完成的。“相關數據通常存儲在同一個索引中,該索引由一個或多個主分片和零個或多個複製分片組成。一旦創建了索引,就不能更改主分片的數量。

    Elasticsearch使用Lucene,並試圖通過JSON和Java API提供其所有特性。它支持facetting和percolating,如果新文檔與註冊查詢匹配,這對於通知非常有用。另一個特性稱為“網關”,處理索引的長期持久性;例如,在服務器崩潰的情況下,可以從網關恢復索引。Elasticsearch支持實時GET請求,適合作為NoSQL數據存儲,但缺少分佈式事務。

    2. Elasticsearch深入了解

    2.1 Elasticsearch的底層實現

    • 2.1.1 lucene

      Es是一個比較複雜的搜索服務器,本身也是使用Java語言編寫的,在上面的簡介中,說明了ES是一個基於lucene的搜索服務器,lucene是什麼呢?Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎。lucene也是使用Java語言編寫的,Java天下第一!

      Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程式接口,能夠做全文索引和搜尋。在Java開發環境里Lucene是一個成熟的免費開源工具。就其本身而言,Lucene是當前以及最近幾年最受歡迎的免費Java信息檢索程序庫。至於lucene到底是怎麼實現的,牛牛們可能要自己去百度或者谷歌一下啦。

    • 2.1.2 Elasticsearch的基本概念

      1. 集群(Cluster):就是多台ES服務器在一起構成搜索服務器,現在很多應用基本上都有集群的概念,提高性能,讓應用具有高可用性,一台服務器掛掉,可以很快有另一台ES服務器補上。

      2. 節點(Node):節點就是集群中的某一台ES服務器就稱為一個節點。

      3. 索引庫(Index Indices):就是ES服務器上的某一個索引,相當於Mysql數據庫中的數據庫的概念,一個節點可以有很多個索引庫。

      4. 文檔類型(Type):這個概念就相當於Mysql數據庫中表的概念,一個索引庫可以有很多個文檔類型,但是這個概念現在慢慢淡化了,因為在ES中一個索引庫直接存數據文檔就挺好的,這個概念現在來說有點多餘了,所以ES官方也在淡化這個概念,在ES8中,這個概念將會徹底的消失。

      5. 文檔(Doc):文檔就相當於Mysql是數據庫中某個表的一條數據記錄,現在ES已經到7.7版本了,我們也就忽略type這個概念,直接在索引庫中存文檔即可。另外需要說一下,我們一般把數據文檔存到Es服務器的某個索引庫的這個動作稱之為索引

        最後還有兩個比較重要的概念,但是可能不是那麼直觀的可以感受得到:

        分片(Shards)和副本(Replicas)

        索引可能會存儲大量數據,這些數據可能超過單個節點的硬件限制。例如,十億個文檔的單個索引佔用了1TB的磁盤空間,可能不適合單個節點的磁盤,或者可能太慢而無法單獨滿足來自單個節點的搜索請求。

        為了解決此問題,Elasticsearch提供了將索引細分為多個碎片的功能。創建索引時,只需定義所需的分片數量即可。每個分片本身就是一個功能齊全且獨立的“索引”,可以託管在群集中的任何節點上。

        分片很重要,主要有兩個原因:

        • 它允許您水平分割/縮放內容量
        • 它允許您跨碎片(可能在多個節點上)分佈和并行化操作,從而提高性能/吞吐量

        分片如何分佈以及其文檔如何聚合回到搜索請求中的機制由Elasticsearch完全管理,並且對您作為用戶是透明的。

        在隨時可能發生故障的網絡/雲環境中,非常有用,強烈建議您使用故障轉移機制,以防碎片/節點因某種原因脫機或消失。為此,Elasticsearch允許您將索引分片的一個或多個副本製作為所謂的副本分片(簡稱副本)。

        複製很重要,主要有兩個原因:

        • 如果分片/節點發生故障,它可提供高可用性。因此,重要的是要注意,副本碎片永遠不會與從其複製原始/主要碎片的節點分配在同一節點上。
        • 由於可以在所有副本上并行執行搜索,因此它可以擴展搜索量/吞吐量。

        總而言之,每個索引可以分為多個碎片。索引也可以複製零(表示沒有副本)或多次。複製后,每個索引將具有主碎片(從中進行複製的原始碎片)和副本碎片(主碎片的副本)。可以在創建索引時為每個索引定義分片和副本的數量。創建索引后,您可以隨時動態更改副本數,但不能事後更改分片數。

        默認情況下,Elasticsearch中的每個索引分配有5個主碎片和1個副本,這意味着如果集群中至少有兩個節點,則索引將具有5個主碎片和另外5個副本碎片(1個完整副本),總共每個索引10個碎片。

    • 2.1.3 Elasticsearch的索引原理

      Es作為一個全文檢索服務器,那麼它在搜索方面肯定很在行啦!那它是怎麼做到的呢?

      Es官方有這麼一句話:一切設計都是為了提高搜索的性能!

      Es能夠快速的搜索出我們需要的內容,靠的就是倒排索引的思想,或者說是一種設計!

      在沒有使用倒排索引的情況下,正常思路是根據搜索關鍵字去查找相應的內容,但是使用了倒排索引之後,ES會先將文檔的所有內容拆分成多個詞條,創建一個包含所有不重複詞條的排序列表,然後列出每個詞條出現在哪個文檔。

      例如,假設我們有兩個文檔,每個文檔的 content 域包含如下內容:

      ​ Doc_1:The quick brown fox jumped over the lazy dog

      ​ Doc_2:Quick brown foxes leap over lazy dogs in summer

      ES首先會將這兩個文檔拆分成多個單獨的詞,或者叫做詞條,然後為所有的詞條創建一個排序列表,並記錄每個詞條出現的文檔的信息。就像下面這樣:

      Term      Doc_1  Doc_2
      -------------------------
      Quick   |       |  X                        /*
      The     |   X   |								Term就是詞條,比如第一個Term就是Quick關鍵字,在Doc_1中不存
      brown   |   X   |  X							在,在Doc_2中存在,其他的以此類推。
      dog     |   X   |							*/
      dogs    |       |  X
      fox     |   X   |
      foxes   |       |  X
      in      |       |  X
      jumped  |   X   |
      lazy    |   X   |  X
      leap    |       |  X
      over    |   X   |  X
      quick   |   X   |
      summer  |       |  X
      the     |   X   |
      ------------------------
      

      現在,如果我們想搜索 quickbrown這兩個關鍵字,我們只需要查找包含每個詞條的文檔,就相當於我們查詢的時候,是通過這個索引表找到文檔,在通過文檔去找文檔內容中的搜索關鍵字,與傳統的通過關鍵字去找內容是不同的。

      倒排索引到底是個怎麼實現的,怎麼個思想,我在這裏就不一一說明了,大家可以看下官方的詳細介紹:倒排索引的原理

      還有es官方的一系列的說明也都可以了解一下:什麼是Elasticsearch?

    2.2 Elasticsearch的安裝

    本演示項目ES版本為7.0.0版本,其他版本的ES的maven依賴與其他的jar包關係請自行查閱官方文檔,保證不衝突。

    • Windows

      Es服務器的安裝很簡單,Windows版本特別的簡單,直接去官網下載,運行 bin/elasticsearch 或者bin\elasticsearch.bat

    • Linux(CentOS7)

      首先我們去官網下載ES的tar.gz包,然後自建一個文件夾放好,然後解壓tar.zg壓縮包:

      tar -xvf elasticsearch-7.0.0.tar.gz
      

      然後進入到bin目錄下:

      cd elasticsearch-7.0.0/bin
      

      然後運行elasticsearch:

      ./elasticsearch
      

      這個時候肯定會報錯的,因為沒有進行配置,所以我們先對es進行一些簡單的配置,保證能單機運行,進入elasticsearch-7.7.0/config目錄,對es的核心配置文件進行編輯:

      vim elasticsearch.yml
      

      進入到了elasticsearch.yml文件的編輯頁面:

      首先我們配置集群名稱,集群名稱自己取一個喜歡的名字就好:

      接下來配置節點名稱,就是在這個集群中,這個es服務器的名稱:

      接下來配置一些必要的參數:

      bootstrap.memory_lock: 是否鎖住內存,避免交換(swapped)帶來的性能損失,默認值是: false。

      bootstrap.system_call_filter: 是否支持過濾掉系統調用。elasticsearch 5.2以後引入的功能,在bootstrap的時候check是否支持seccomp。

      配置network為所有人都可以訪問,因為我們一般是使用ssh連接工具在其他的電腦上操作Linux系統,所以我們需要配置一下:

      到這裏就配置完成了,但是當你重新去運行.elasticsearch的可執行文件的時候,依然會報錯。

      報錯信息中可能包含以下幾個錯誤:

      • max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]

        原因:無法創建本地文件問題,用戶最大可創建文件數太小。

        解決方法:切換到root賬戶下,進入Linux系統文件夾,編輯limits.conf文件:

        vim /etc/security/limits.conf
        

        在文件的末尾加上:

        *                soft    nofile          65536
        *                hard    nofile          65536
        *                soft    nproc           4096
        *                hard    nproc           4096
        
      • max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

        原因:最大虛擬內存太小,需要修改系統變量的最大值。

        解決方法:切換到root賬戶下,進入Linux系統文件夾,編輯sysctl.conf文件:

        vim /etc/sysctl.conf
        

        在文件的末尾加上:

        vm.max_map_count=262144
        
      • max number of threads [1024] for user [es] likely too low, increase to at least [2048]

        原因:無法創建本地線程問題,用戶最大可創建線程數太小。

        解決方法:如果你是CentOS6及以下系統,編輯的文件是90-nproc.conf這個文件,如果你和我一樣使用的是CentOS7的話,編輯的文件是20-nproc.conf文件,其實這兩個文件是一樣的,只是在不同CentOS系統中名稱不一樣而已。

        CentOS7使用這個命令:

        vim /etc/security/limits.d/20-nproc.conf
        

        CentOS6使用這個命令:

        vim /etc/security/limits.d/90-nproc.conf
        

        只需要在文件中加上以下配置:

        *          soft    nproc     4096
        

        這個配置的意思是說賦予其他用戶的可創建本地線程數為4096。在這個文件中本來就有一個配置,意思是說賦予root賬戶創建線程數不受限制。我們就把上面的配置加在本來存在的配置的下面一行就可以了。

        如果是CentOS7的使用者,還需要配置另一個文件,否則這個最大線程數是不會生效的。CentOS 7 使用systemd替換了SysV,Systemd目的是要取代Unix時代以來一直在使用的init系統,兼容SysV和LSB的啟動腳本,而且夠在進程啟動過程中更有效地引導加載服務。在/etc/systemd目錄下有一個系統的默認管理配置,這裡有登陸、日誌、服務、系統等。所以CentOS7的使用者還需要配置下面這個文件:

        vim /etc/systemd/system.conf
        

        對其中的選項進行配置,在文件的末尾加上:

        DefaultLimitNOFILE=65536
        DefaultLimitNPROC=4096
        

      上面的所以錯誤解決完畢之後,我們再運行.elasticsearch可執行文件,es才可以啟動成功。

    2.3 Elasticsearch的使用

    首先給大家介紹一個谷歌瀏覽器插件,這個插件是用來可視化展示es的索引庫數據的,這個插件叫做ElasticVue,個人感覺挺好用的,展示也比較方便,給大家截個圖看看:

    大家可以使用這個建立索引庫,然後調用es官方的es專用的語法操作es服務器進行CRUD操作,但是此處我只介紹Java語言如何調用es服務器API,廢話不多說,我們直接開始下一步。

    • 2.3.1 引入依賴

      搭建工程的過程我就不演示了,直接上pom.xml依賴文件。

      pom.xml

      <!--springboot父工程-->
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.2.2.RELEASE</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
      
          <dependencies>
              <!--springboot-web組件-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
                  <version>2.2.2.RELEASE</version>
              </dependency>
              <!--elasticsearch-rest-client組件-->
              <dependency>
                  <groupId>org.elasticsearch.client</groupId>
                  <artifactId>elasticsearch-rest-client</artifactId>
                  <version>7.7.0</version>
              </dependency>
              <!--elasticsearch-rest-high-level-client組件-->
              <dependency>
                  <groupId>org.elasticsearch.client</groupId>
                  <artifactId>elasticsearch-rest-high-level-client</artifactId>
                  <version>7.7.0</version>
              </dependency>
              <!--elasticsearch組件-->
              <dependency>
                  <groupId>org.elasticsearch</groupId>
                  <artifactId>elasticsearch</artifactId>
                  <version>7.7.0</version>
              </dependency>
              <!--mybatis整合springboot組件-->
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>2.1.0</version>
              </dependency>
              <!--mysql數據庫連接驅動-->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.18</version>
              </dependency>
              <!--lombok組件-->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.10</version>
              </dependency>
              <!--json組件gson-->
              <dependency>
                  <groupId>com.google.code.gson</groupId>
                  <artifactId>gson</artifactId>
                  <version>2.8.5</version>
              </dependency>
              <!--springboot-test組件-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-test</artifactId>
              </dependency>
              <!--單元測試junit組件-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
                  <scope>test</scope>
              </dependency>
              <!--spring-test組件-->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-test</artifactId>
                  <version>5.2.2.RELEASE</version>
                  <scope>test</scope>
              </dependency>
          </dependencies>
      
          <build>
              <!--springboot的maven插件-->
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <configuration>
                          <compilerArgs>
                              <arg>-parameters</arg>
                          </compilerArgs>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      
    • 2.3.2 Elasticsearch的配置類和Gson配置類和應用配置文件

      application.yml

      butterflytri:
        databaseurl-port: 127.0.0.1:3306 # 數據庫端口
        database-name: student_db # 數據庫名
        host: 192.168.129.100:9200 # es服務端
      server:
        port: 8080 # 應用端口
        servlet:
          context-path: /butterflytri # 應用映射
      spring:
        application:
          name: mybatis # 應用名稱
        datasource:
          url: jdbc:mysql://${butterflytri.databaseurl-port}/${butterflytri.database-name}?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password: root
      mybatis:
        type-aliases-package: com.butterflytri.entity # entity別名
        mapper-locations: classpath:com/butterflytri/mapper/*Mapper.xml # mapper映射包掃描
      

      注意:yml文件中的192.168.129.100:9200是es對外的端口,使用的http協議進行操作,es服務器還有個9300端口,這個端口是es集群中各個節點進行交流的端口,使用的是tcp協議。所以我們連接的時候,端口要使用9200端口。

      項目啟動類沒有什麼特別的東西,就不展示了。

      ElasticsearchConfig.java

      package com.butterflytri.config;
      
      import org.apache.http.HttpHost;
      import org.elasticsearch.client.RestClient;
      import org.elasticsearch.client.RestHighLevelClient;
      import org.springframework.beans.factory.DisposableBean;
      import org.springframework.beans.factory.FactoryBean;
      import org.springframework.beans.factory.InitializingBean;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Configuration;
      
      /**
       * @author: WJF
       * @date: 2020/5/22
       * @description: ElasticSearchConfig
       */
      @Configuration
      public class ElasticSearchConfig implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
      
          /**
           * {@link FactoryBean<T>}:FactoryBean<T>是spring對外提供的對接接口,當向spring對象使用getBean("..")方法時,
           *                         spring會使用FactoryBean<T>的getObject 方法返回對象。所以當一個類實現的factoryBean<T>接口時,
           *                         那麼每次向spring要這個類時,spring就返回T對象。
           *
           * {@link InitializingBean}:InitializingBean接口為bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,
           *                          凡是繼承該接口的類,在初始化bean的時候會執行該方法。在spring初始化bean的時候,如果該bean是
           *                          實現了InitializingBean接口,並且同時在配置文件中指定了init-method,系統則是
           *                          先調用afterPropertiesSet方法,然後在調用init-method中指定的方法。
           *
           * {@link DisposableBean}:DisposableBean接口為bean提供了銷毀方法destroy-method,會在程序關閉前銷毀對象。
           */
      
          @Value("#{'${butterflytri.host}'.split(':')}")
          private String[] host;
      
          private RestHighLevelClient restHighLevelClient;
      
          private RestHighLevelClient restHighLevelClient() {
              restHighLevelClient = new RestHighLevelClient(
      
                      RestClient.builder(new HttpHost(host[0],Integer.valueOf(host[1]),"http"))
      
              );
              return restHighLevelClient;
          }
      
          @Override
          public void destroy() throws Exception {
              restHighLevelClient.close();
          }
      
          @Override
          public RestHighLevelClient getObject() throws Exception {
              return restHighLevelClient;
          }
      
          @Override
          public Class<?> getObjectType() {
              return RestHighLevelClient.class;
          }
      
          @Override
          public void afterPropertiesSet() throws Exception {
              restHighLevelClient();
          }
      
      }
      

      ES的配置類,這個配置類實現了三個接口,三個接口的作用我也寫上了註釋,大家可以看下,需要注意的是FactoryBean這個接口,一但實現了這個接口,每當你需要使用泛型表示的對象T的時候,Spring不會從容器中去拿這個對象,而是會調用這個FactoryBean.getObject()方法去拿對象。其他的就沒有什麼了。

      Gson.java

      Gson是一個操作json數據的類,它的執行效率可能會慢一點,但是它在解析json數據的時候不會出Bug。

      package com.butterflytri.config;
      
      import com.google.gson.Gson;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      /**
       * @author: WJF
       * @date: 2020/5/22
       * @description: GsonConfig
       */
      @Configuration
      public class GsonConfig {
      
          /**
           * {@link Gson}:一個操作json的對象,有比較好的json操作體驗,相對於Alibaba的FastJson來說速度慢一些,但是FastJson在解析
           *              複雜的的json字符串時有可能會出現bug。
           * @return Gson
           */
      
          @Bean
          public Gson gson() {
              return new Gson();
          }
      
      }
      

      Constants.java

      這是我寫的常量類,放一些ES使用的常量,直接寫字符串也行,但是我建議這樣做。

      package com.butterflytri.constants;
      
      
      /**
       * @author: WJF
       * @date: 2020/5/22
       * @description: Constants
       */
      public class Constants {
      
          /**
           * es搜索關鍵字
           */
          public static final String KEYWORD = ".keyword";
      
          /**
           * es的type類型:type字段將在 elasticsearch-version:8 中徹底刪除,本來就覺得沒得啥用。
           */
          public static final String DOC_TYPE = "_doc";
      
          /**
           * 學生信息索引類型
           */
          public static final String INDEX_STUDENT = "student_info";
      
      
          /**
           * 自定連接符
           */
          public static final String CONNECTOR = " --> ";
      
      }
      

      Student.java

      package com.butterflytri.entity;
      
      import lombok.Getter;
      import lombok.Setter;
      import lombok.ToString;
      
      import java.io.Serializable;
      
      /**
       * @author: WJF
       * @date: 2020/5/16
       * @description: Student
       */
      
      @ToString
      @Getter
      @Setter
      public class Student implements Serializable {
      
          private Long id;
      
          private String studentName;
      
          private String studentNo;
      
          private String sex;
      
          private Integer age;
      
          private String clazz;
      
      }
      

      StudentMapper.java

      package com.butterflytri.mapper;
      
      import com.butterflytri.entity.Student;
      import org.apache.ibatis.annotations.Mapper;
      
      import java.util.List;
      
      /**
       * @author: WJF
       * @date: 2020/5/16
       * @description: StudentMapper
       */
      @Mapper
      public interface StudentMapper {
      
          /**
           * 查詢所有學生信息
           * @return List<Student>
           */
          List<Student> findAll();
      
          /**
           * 通過id查詢學生信息
           * @param id:學生id
           * @return Student
           */
          Student findOne(Long id);
      
          /**
           * 通過學號查詢學生信息
           * @param studentNo:學生學號
           * @return Student
           */
          Student findByStudentNo(String studentNo);
      
      }
      

      mybatis的SQL映射文件我就不展示了,也很簡單,大家看接口方法名就應該可以想象得到SQL語句是怎樣的。

    • 2.3.3 索引數據到ES服務器

      IndexServiceImpl.java

      package com.butterflytri.service.impl;
      
      import com.butterflytri.constants.Constants;
      import com.butterflytri.entity.Student;
      import com.butterflytri.service.IndexService;
      import com.google.gson.Gson;
      import org.elasticsearch.action.ActionListener;
      import org.elasticsearch.action.index.IndexRequest;
      import org.elasticsearch.action.index.IndexResponse;
      import org.elasticsearch.client.RequestOptions;
      import org.elasticsearch.client.RestHighLevelClient;
      import org.elasticsearch.common.xcontent.XContentType;
      import org.springframework.stereotype.Service;
      
      import javax.annotation.Resource;
      import java.io.IOException;
      
      /**
       * @author: WJF
       * @date: 2020/5/22
       * @description: IndexServiceImpl
       */
      @Service
      public class IndexServiceImpl implements IndexService {
      
          @Resource
          private Gson gson;
      
          @Resource
          private RestHighLevelClient restHighLevelClient;
      
          @Override
          public String index(Student student) {
              StringBuilder builder = new StringBuilder();
              IndexRequest indexRequest = this.initIndexRequest(student);
              try {
                  // 同步索引到elasticsearch服務器,獲取索引響應IndexResponse
                  IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
                  String statusName = indexResponse.status().name();
                  int statusCode = indexResponse.status().getStatus();
                  builder.append(statusName).append(Constants.CONNECTOR).append(statusCode);
              } catch (IOException e) {
                  builder.append("Fail").append(Constants.CONNECTOR).append(e.getMessage());
              }
              return builder.toString();
          }
      
      
          @Override
          public String indexAsync(Student student) {
              StringBuilder builder = new StringBuilder();
              IndexRequest indexRequest = this.initIndexRequest(student);
              // 異步索引到elasticsearch服務器,獲取索引響應IndexResponse
              restHighLevelClient.indexAsync(indexRequest, RequestOptions.DEFAULT,actionListener(builder));
              return builder.toString();
          }
      
      
      
          /**
           * 初始化IndexRequest,並設置數據源。
           * @param student
           * @return IndexRequest
           */
          private IndexRequest initIndexRequest(Student student) {
              // 構建IndexRequest,設置索引名稱,索引類型,索引id
              IndexRequest indexRequest = new IndexRequest(Constants.INDEX_STUDENT);
              // 可以不設置,默認就是'_doc'
              indexRequest.type(Constants.DOC_TYPE);
              // 設置索引id為studentId
              indexRequest.id(String.valueOf(student.getId()));
              // 設置數據源
              String studentJson = gson.toJson(student);
              indexRequest.source(studentJson, XContentType.JSON);
              return indexRequest;
          }
      
          /**
           * 異步索引的回調監聽器,根據不同的結果做出不同的處理
           * @param builder
           * @return ActionListener<IndexResponse>
           */
          private ActionListener<IndexResponse> actionListener(StringBuilder builder) {
              return new ActionListener<IndexResponse>() {
                  // 當索引數據到es服務器時,返回不同的狀態
                  @Override
                  public void onResponse(IndexResponse indexResponse) {
                      String statusName = indexResponse.status().name();
                      int statusCode = indexResponse.status().getStatus();
                      builder.append(statusName).append(Constants.CONNECTOR).append(statusCode);
                  }
      
                  // 當索引數據時出現異常
                  @Override
                  public void onFailure(Exception e) {
                      builder.append("Fail").append(Constants.CONNECTOR).append(e.getMessage());
                  }
              };
          }
      }
      

      上面的內容很簡單,就是將Student對象格式化為Json字符串,然後存到es服務器中,大家只要遵守一個規則就好,就是操作es服務器,不管是什麼操作都是用RestHighLevelClient這個類去操作,上面的就是student對象索引的es服務器中,使用restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT),首先就是構建indexRequest對象,這個對象就是索引請求對象,具體幹了什麼看代碼上的註釋。這裏還有個restHighLevelClient.indexAsync()這個方法,這個方法和上面的index方法一樣的效果,只不過是異步調用。

      接下來我們測試一下這個代碼,請看:

      @Test
          public void indexTest() {
              List<Student> list = studentMapper.findAll();
              for (Student student : list) {
                  String message = indexService.index(student);
                  System.out.println(message);
              }
          }
      

      我們使用ElasticVue插件連接es服務器即可看到有一個索引庫:

      當我們點擊到show按鈕的時候,可以看到student_info索引庫中有幾條記錄:

      索引數據到數據庫成功了。

    • 2.3.4 獲取Es服務器數據

      獲取數據,是es提供給我們的API,這個Api只能獲取某個索引的某一條文檔,示例如下:

      GetServiceImpl.java

      	@Override
          public Student get(String id) {
              Student student = new Student();
              GetRequest getRequest = new GetRequest(Constants.INDEX_STUDENT, id);
              try {
                  GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
                  String source = getResponse.getSourceAsString();
                  student = gson.fromJson(source, Student.class);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return student;
          }
      

      接着我們在測試類中,調用這個方法然後打印一下結果:

      GetServiceTest.java

      	@Test
          public void getTest() {
              Student student = getService.get("1");
              System.out.println(student);
          }
      

      結果如下:

      更新數據文檔和刪除數據文檔我就不演示了,都是大同小異,大家可以拉下我的代碼,好好研究一下,都有詳細的註釋,覺得可以的話,給我點下star也是極好的。下面演示一下searchApi,這個Api是我們經常需要使用的,特別重要。

    • 2.3.5 搜索Es服務器數據

      ES的搜索API包含很多,比如說組合搜索,區間搜索,高亮显示,分詞搜索等等。我先給大家演示一下組合搜索,區間搜索其實也是組合搜索的一個子條件,其他的搜索其實也都是,代碼如下:

      SearchServiceImpl.java

      	@Override
          public List<Student> searchRange(Object from, Object to, String field, String index) {
              List<Student> list = new ArrayList<>();
              BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
              // 需要搜索的區間字段field
              RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field);
              // 左區間
              if (from != null) {
                  rangeQueryBuilder.from(from, true);
              }
              // 右區間
              if (to != null) {
                  rangeQueryBuilder.to(to, true);
              }
              boolQueryBuilder.must();
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              searchSourceBuilder.query(boolQueryBuilder);
              SearchRequest searchRequest = new SearchRequest(index);
              searchRequest.source(searchSourceBuilder);
              try {
                  SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                  for (SearchHit hit : search.getHits()) {
                      String source = hit.getSourceAsString();
                      Student student = gson.fromJson(source, Student.class);
                      list.add(student);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return list;
          }
      

      上面的代碼其實很簡單,就是一個區間查詢構建器,查詢指定字段處於區間的所有數據,rangeQueryBuilder.from(from, true)的第一個參數就是字段的下邊界,第二個參數代表是否包含邊界。SearchResponse就是搜索的響應對象,所有的數據都在SearchHit對象中。

      接下來給大家演示一些組合查詢,這個方法搜索年齡在18到19歲並且班級為’G0305’的學生。記得ES默認是分頁的,如果想不分頁,一定要記得給搜索字段加上.keyword(字符串加,数字不支持)。

      SearchServiceImpl.java

      @Override
          public List<Student> searchBool() {
              List<Student> list = new ArrayList<>();
              BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
              boolQuery.must(QueryBuilders.rangeQuery("age").gte(18).lte(19));
              boolQuery.must(QueryBuilders.termQuery("clazz" + Constants.KEYWORD,"G0305"));
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              searchSourceBuilder.query(boolQuery);
              SearchRequest searchRequest = new SearchRequest(Constants.INDEX_STUDENT);
              searchRequest.source(searchSourceBuilder);
              try {
                  SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                  for (SearchHit hit : search.getHits()) {
                      String source = hit.getSourceAsString();
                      Student student = gson.fromJson(source, Student.class);
                      list.add(student);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return list;
          }
      

      上面的代碼中的類BoolQueryBuilder就是組合查詢構建器,這個類可以用來構建組合的條件查詢。boolQuery.must()方法就是用來拼接條件的一種方式,使用這個方法代表必須滿足這個條件才會查詢出來,上面的代碼說明必須滿足年齡為18(包含18)到19(包含19)歲,並且班級為’G0305’的學生才會查詢出來。還有其他的一些常見的組合查詢方法,如下:

      • boolQuery.must():必須滿足此條件,相當於=或者&
      • boolQuery.mustNot():必須不滿足此條件,相當於!=
      • boolQuery.should():相當於||或者or
      • boolQuery.filter():過濾。

      然後是聚合查詢,很類似於MySQL中的聚合函數,這個示例我就不再解釋了,代碼註釋很清楚:

      @Override
          public void searchBoolAndAggregation() {
              BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
              boolQuery.must(QueryBuilders.rangeQuery("age").gte(18).lte(19));
              boolQuery.must(QueryBuilders.termQuery("clazz" + Constants.KEYWORD,"G0305"));
              // 聚合分組:按clazz字段分組,並將結果取名為clazz,es默認是分詞的,為了精確配置,需要加上‘.keyword’關鍵詞後綴。
              TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("clazz").field("clazz" + Constants.KEYWORD);
              // 聚合求和:求符合查詢條件的學生的年齡的和,並將結果取名為ageSum,因為不是字符串,所以默認是精確匹配,不支持分詞。
              aggregationBuilder.subAggregation(AggregationBuilders.sum("ageSum").field("age"));
              // 聚合求平均:求符合查詢條件的學生的年齡的平均值,並將結果取名為ageAvg,因為不是字符串,所以默認是精確匹配,不支持分詞。
              aggregationBuilder.subAggregation(AggregationBuilders.avg("ageAvg").field("age"));
              // 聚合求數量:按學號查詢符合查詢條件的學生個數,並將結果取名為count,es默認是分詞的,為了精確配置,需要加上‘.keyword’關鍵詞後綴。
              aggregationBuilder.subAggregation(AggregationBuilders.count("count").field("studentNo" + Constants.KEYWORD));
              SearchSourceBuilder builder = new SearchSourceBuilder();
              builder.query(boolQuery);
              builder.aggregation(aggregationBuilder);
              // 按年齡降序排序。
              builder.sort("age", SortOrder.DESC);
              SearchRequest request = new SearchRequest("student_info");
              request.source(builder);
              try {
                  SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
                  for (SearchHit hit : search.getHits()) {
                      String source = hit.getSourceAsString();
                      Student student = gson.fromJson(source, Student.class);
                      System.out.println(student);
                  }
                  // 使用Terms對象接收
                  Terms clazz = search.getAggregations().get("clazz");
                  for (Terms.Bucket bucket : clazz.getBuckets()) {
                      System.out.println(bucket.getDocCount());
      
                      System.out.println("=====================");
                      // 使用ParsedSum對象接收
                      ParsedSum ageCount = bucket.getAggregations().get("ageSum");
                      System.out.println(ageCount.getType());
                      System.out.println(ageCount.getValue());
                      System.out.println(ageCount.getValueAsString());
                      System.out.println(ageCount.getMetaData());
                      System.out.println(ageCount.getName());
      
                      System.out.println("=====================");
                      // 使用ParsedAvg對象接收
                      ParsedAvg ageAvg = bucket.getAggregations().get("ageAvg");
                      System.out.println(ageAvg.getType());
                      System.out.println(ageAvg.getValue());
                      System.out.println(ageAvg.getValueAsString());
                      System.out.println(ageAvg.getMetaData());
                      System.out.println(ageAvg.getName());
      
                      System.out.println("=====================");
                      // 使用ParsedValueCount對象接收
                      ParsedValueCount count = bucket.getAggregations().get("count");
                      System.out.println(count.getType());
                      System.out.println(count.getValue());
                      System.out.println(count.getValueAsString());
                      System.out.println(count.getMetaData());
                      System.out.println(count.getName());
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      最後還有分詞查詢,分詞查詢就不加.keyword關鍵字即可。

      @Override
          public List<Student> searchMatch(String matchStudentName) {
              List<Student> list = new ArrayList<>();
              BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
              // 分詞查詢時不加'.keyword'關鍵字
              boolQueryBuilder.must(QueryBuilders.matchQuery("studentName",matchStudentName));
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              searchSourceBuilder.query(boolQueryBuilder);
              SearchRequest searchRequest = new SearchRequest("student_info");
              searchRequest.source(searchSourceBuilder);
              try {
                  SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                  for (SearchHit hit : search.getHits().getHits()) {
                      String source = hit.getSourceAsString();
                      Student student = gson.fromJson(source, Student.class);
                      list.add(student);
                  }
      
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return list;
          }
      

      請記住,一般的進行分詞都是字符串才進行分詞搜索,数字等類型只能是精準匹配。

      最後,ES功能很強大,作為搜索界的扛把子,ES的功能遠遠不止這些,它還可以高亮搜索,數據分析等等。我在這裏演示的僅僅只是皮毛,甚至都不是皮毛,僅作為初學者的參考。如有大佬覺得我哪裡寫錯了,或者有不同見解,歡迎留言。

    3. 項目地址

    本項目傳送門:

    • GitHub —> spring-boot-elasticsearch
    • Gitee —> spring-boot-elasticsearch

    此教程會一直更新下去,覺得博主寫的可以的話,關注一下,也可以更方便下次來學習。

    • 作者:Butterfly-Tri
    • 出處:Butterfly-Tri個人博客
    • 版權所有,歡迎保留原文鏈接進行轉載

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

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

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

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