分類
發燒車訊

中國 YouTuber 搶先揭露 i9-11900K 的測試影片,單核心擊敗對手 Ryzen 9 5900X,時脈達 5.3GHz_包裝設計

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

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

繼昨日的 i7-11700K 之後,最多遊戲玩家期待的 i9-11900K,也有新傳聞出現了,而且這次還是直接被中國 YouTuber 洩漏評測影片,即便只是測試版的 BIOS,單核心跑分依舊贏過上一代的 i9-10900KF,也擊敗競爭對手的 Ryzen 9 5900X,非常亮眼。另外溫度部分也不意外相當高,壓力測試浮點 FPU 直接過熱降頻,有意入手的人,記得也準備好散熱器的錢。

中國 YouTuber 搶先揭露 i9-11900K 評測影片

上個月中中國就曾出現過第 11 代桌上型處理器 i9-11900 的 ES 工程版,因此這次 i9-11900K 再度於中國現身其實也不讓人意外。雖然這位潮玩客並沒有特別說明是 ES 版還是正式版,但就時間點來看,ES 版的可能性很大。

搭配的主機板為 Socket 1200 LGA,時脈最高可達到 5.3GHz,採 8 核心與 16 執行緒設計:

而實際測試時,目前安裝的 BIOS 還是測試版,因此會有一些問題存在,像是 RAM 最高只能上到 3200MHz、PCI 支援度也有問題,無法正常點亮 PCIe 4.0 的 AMD 與 NVIDIA 顯示卡,因此他們只能搭配 GTX 1660Ti。

全核心運行最高時脈是 4.7GHz,單核則可以達到 5.3GHz,跑 CINEBENCH R20 功耗為 230W:

浮點 FPU 壓力測試功耗更來到 260W,每個核心都快到 100 度,理所當然就過熱降頻了:

CPU-Z 測試單核心獲得 708 分,多核心則是 6443 分,對比 i9-10900KF 與競爭對手的 R9 5900X,單核心都獲勝,不過多核心部分就輸了。這其實合理,畢竟 i9-11900K 目前傳出是 8 核心 16 執行緒,而上一代的 i9-10900KF 為 10 核心 20 執行緒,AMD R9 5900X 更是高達 12 核心 24 執行緒:

另外雖然 i9-11900K 的核心數下降且同樣是 14nm 製程,但在第 11 處理器的架構上,Intel 有做許多改進,因此已經可以跟基於 7nm 製程的 AMD Zen 3 核心比拼。

CINEBENCH R20 單核心獲得 363 分,多核心為 5725 分,單核心一樣贏 i9-10900KF 與 R9 5900X:

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

不過遊戲測試就不太理想,R9 5900X 平均有 388 FPS,i9-10900KF 也達到 285 FPS,但 i9-11900K 僅 220FPS:

其他遊戲也一樣:

從 AIDA64 GPGPU Benchmark 測試結果可以看到,VRAM 的數字 i9-11900K 不太正常:

也僅使用 PCIe x16 1.1 通道,難怪跑出這樣的數據:

目前他們還找不出解決辦法,看來只能等正式版 BIOS 推出後,才能真正了解到遊戲表現究竟能不能勝過對手與上一代。

下方是完整影片:

AMD桌上型電腦處理器全球市佔率50.8% 睽違15年終於反超Intel

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

分類
發燒車訊

小米 10i 5G 印度發表:搭載高通 750G 5G 處理器、1.08 億像素主相機、 120Hz更新率螢幕與 4820mAh 大電量_台中搬家

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

台中搬家公司推薦超過30年經驗,首選台中大展搬家

小米在中國、印度以及其他海外市場,經常會有些手機以不同名稱在當地推出,像是昨(5)日才剛於印度發表的「小米 10i 5G」就是很好的例子。昨天小米在印度發表小米 10i 5G 這款新機,眼尖的人或許會發現他有些眼熟,因為它其實就是去年小米在中國推出的 Redmi Note 9 Pro 更名上市新機。

小米 10i 5G 印度發表:搭載高通 750G 5G 處理器、1.08 億像素主相機、 120Hz更新率螢幕與 4820mAh 大電量

去年印度率先發表 Redmi Note 9 Pro ,隨後在中國也推出了 Redmi Note 9 Pro 但在外觀、規格皆與印度版本不同。近日小米於印度發表「小米 10i 5G」新機,也就是中國版的 Redmi Note 9 Pro 。
硬體規格方面,小米 10i 5G 搭載 Qualcomm Snapdragon 750G 5G 處理器、配備最高 8GB LPDDR4X RAM、256GB UFS2.2 ROM :

相機方面,小米 10i 5G 配備 1.08 億像素四鏡頭主相機,鏡頭依序為 1.08 億像素標準鏡頭(Samsung ISOCELL HM2 感光元件)、800 萬像素 120° 超廣角鏡頭、200 萬像素微距鏡頭以及 200 萬像素景深鏡頭,前置鏡頭則配被 1600 萬像素自拍相機。

小米 10i 5G 採用 6.67 吋 FHD+ 解析度螢幕,螢幕具備 120Hz 更新率、240Hz 觸控採樣率,顯示支持 HDR10 格式。此外,螢幕採用康寧第五代大猩猩玻璃保護,小米 10i 5G 也具備 IP53 防潑水等級。

電量方面,小米 10i 5G 內建 4820mAh 大電量電池,支持 33W 功率的有線快充能在 58 分鐘為小米 10i 充電至 100% :

其他方面,小米 10i 5G 亦具備 NFC、保留 3.5mm 耳機孔以及採用側邊指紋辨識器進行指紋辨識解鎖。顏色方面,小米 10i 5G 在印度推出 Midnight Black(午夜黑)、Atlantic Blue(大西洋藍)以及帶有漸層色的 Pacific Sunrise(太平洋日出)等三種配色選擇。

小米 10i 5G 在印度共推出三個版本,價格依序為 6GB+64GB 版本售價 23,999  印度盧比(約合新台幣 9,153 元)、8GB+128GB 版本售價 24,999 印度盧比(約合新台幣 9,534 元)、8GB+256GB 版本售價 27,999 印度盧比(約合新台幣 10,678 元),在當地使用特定銀行卡可享 2,000 印度盧比的折扣。

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

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

圖片/消息來源:Mi India

延伸閱讀:
Redmi K40 Pro 最新渲染圖曝光:可能是最便宜的 S888 旗艦 5G 手機之一

疑似 OPPO Find X3 安兔兔跑分曝光,搭載高通 S888 旗艦處理器測出 77.1 萬分刷新最高分紀錄

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

台中搬家公司推薦超過30年經驗,首選台中大展搬家

分類
發燒車訊

HTC Desire 21 Pro 5G 實機諜照流出!配備 4800 萬像素四鏡頭主相機、支持雙卡雙待_台中搬家公司

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

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

繼去年 HTC U20 5G 之後,也在下半年在台灣推出 HTC Desire 20+ 。在 2021 年初,又傳聞 HTC 即將推出新手機?最近在網路上流傳一組疑似為 HTC Desire 21 Pro 5G 的實機諜照,雖然目前還無法確認其詳細規格,不過從中仍可得知這款未發表的新機配備 4800 萬像素四鏡頭主相機並支持雙卡雙待。

HTC Desire 21 Pro 5G 實機諜照流出!配備 4800 萬像素四鏡頭主相機、支持雙卡雙待

去年底,在中國美篇有人上傳了一系列聲稱是 HTC Desire 21 Pro 5G 的實機諜照,雖然目前原文已經修改隱私權限無法查看,不過稍早也有人將這組照片上傳到 Slashleaks 爆料,也讓 Desire 21 Pro 5G 的實機外觀被更多人看到。
雖然這組 Desire 21 Pro 5G 的外觀照片已經包上一層碳纖紋紋路的包膜,不過在機身背面仍可看到 HTC 的 Logo 以及相機外觀。圖中的 HTC Desire 21 Pro 5G  配備 4800 萬像素四鏡頭主相機,相機旁也有 LED 閃光燈:

從另一張機身正面見到 Desire 21 Pro 5G 採用居中的挖孔全螢幕設計,其型號為 2QAG100 。從關於手機的其他資訊,也能得知這款新機具備雙 SIM 卡插槽支持雙卡雙待、運行 Android 10 作業系統。另外,傳聞 Desire 21 Pro 5G 在機身側邊左側的電源鍵整合側邊指紋感應器,在機身頂部未見到 3.5mm 耳機孔。

回顧 HTC 去年推出第一款手機 U20 5G 搭載高通 Snapdragon 765G 處理器,隨後也針對中階入門手機市場推出 Desire 21+ 4G 手機。單從目前 HTC Desire 系列的產品定位來看,能預期這款 Desire 21 Pro 5G 也機上定位為中階機種。
即便目前仍無法確定它會搭載高通還是聯發科的 5G 處理器,但能大致推測它的價格不會太高,對於有想入手平價 5G 手機的消費者在之後也將多一款選擇。

圖片來源:Slashleaks

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

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

延伸閱讀:
小米 10i 5G 印度發表:搭載高通 750G 5G 處理器、1.08 億像素主相機、 120Hz更新率螢幕與 4820mAh 大電量

疑似 OPPO Find X3 安兔兔跑分曝光,搭載高通 S888 旗艦處理器測出 77.1 萬分刷新最高分紀錄

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

分類
發燒車訊

Nintendo Switch Pro 4K 版本傳聞再現!國外分析師表示今年有望見到_網頁設計公司

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

Nintendo Switch Pro 傳聞又來了!這台幾乎每年都再傳,不過今年看起來確實蠻有機會的,畢竟 Sony PlayStation 5、Xbox Series X 都已經推出,Nintendo 想在重返市場最熱門的遊戲機話題,無疑就是要靠新款 Switch。近日就有國外分析師表示,他預測今年 Nintendo 會推出玩家期待許久的 Switch 升級版,而且將會是 4K 解析度版本。

Nintendo Switch Pro 4K 版本傳聞再現!

根據外媒 Nintendo Life 的報導,國外許多業界專家、分析師都紛紛表示,今年會是 Nintendo 非常重要的一年,他們預測 2021 年賣最好、最暢銷的遊戲主機,不會是 PlayStation 5,也不是 Xbox Series X,而是 Nintendo 的 Switch 系列,現行的 Switch Lite、Switch 與今年有機會見到 Switch Pro 總額加起來,將超越這兩間競爭對手。

Kantan Games 的 Serkan Toto 博士在與 Gamesindustry.biz 訪談中提到:「我預測加強版的任天堂 4K 設備,會在這個年度亮相」。Ampere Analysis 的 Piers Harding-Rolls 也表示贊同,不過他不確定規格會是怎樣,但確實很有機會見到新款 Switch」。

至於是基於什麼原因,他們就沒特別強調。

不過去年確實陸續有相關消息出現,像是夏季時,任天堂曾通知開發人員,建議他們準備好 4K 解析度的遊戲。也有報導指出,任天堂正與 Innolux 公司合作,預計下一代遊戲設備會配備 mini LED 螢幕:

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

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

至於規格,統整目前市場上的傳言,Switch Pro 有望具備類似 DLSS 的技術,為玩家提供更流暢的 4K 遊戲體驗。CPU 可能是基於 NVIDIA Tegra Xavier Soc 客製化版本,效能接近 Xbox One。

下方是國外製作的 Switch Pro 概念影片:

資料來源:Nintendo Life

國外開發者成功在 M1 Mac 上運行 Nintendo Switch 遊戲

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

分類
發燒車訊

Dell 的 CES 新品包括一台 40 吋 5K 超寬曲面螢幕與內建 Windows Hello 視訊鏡頭的螢幕(你懂的)_如何寫文案

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

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

如果你最近有考慮添購或更新家中螢幕的話,也許可以參考這波 Dell 在 CES 展前所推出的一系列最新 UltraSharp 螢幕。其中不僅有號稱世界初 40 吋 5K 超寬曲面螢幕,更有為了居家工作與生活而生,內建有視訊鏡頭的貼心螢幕產品系列。繼續閱讀 Dell 的 CES 新品包括一台 40 吋 5K 超寬曲面螢幕與內建 Windows Hello 視訊鏡頭的螢幕(你懂的)報導內文。

▲圖片來源:Dell

Dell 的 CES 新品包括一台 40 吋 5K 超寬曲面螢幕與內建 Windows Hello 視訊鏡頭的螢幕

來到 CES 展前期間,Dell 直接以全球首款 40 吋 5K Ultrawide 曲面螢幕 UltraSharp 40 Curved Monitor(U4021QW) 譜出序曲。預計在 1 月底就正式開賣(起價 US$2,100 約新台幣 5.9 萬)的這款螢幕解析度為 5,120 x 2,160(144 PPI)。雖說 2500R 的曲度相對其他對手並不算太激進的配置,但包括能涵蓋 98% DCI-P3 色域並內建雙 9W 音箱。

連接埠超豐富有包括 DisplayPort 1.4、2 個 HDMI 2.0、具備 90W 充電的 Thunderbolt 3 連接埠、3.5mm 與 RJ45 有線網路孔;USB 方面則是有充電用的 USB-C、4 + 1(Superspeed)個 USB-A 與 USB-B。

這次也一起發表,在尺寸、色域與解析度等規格都稍稍弱化的 38 吋款 Dell UltraSharp 38 吋 2350R 曲面 USB-C 螢幕,則是也承襲了類似的豐富外接能力 — 重點是價格親民不少,但也要 US$1,500 約新台幣 4.2 萬就是了。

另一系列相當有意思的產品,是直接內建視訊鏡頭的 Dell 新品系列,此系列共有三種螢幕尺寸,最大的版本也有曲面的規格。型號分別為 Dell 24 Video Conferencing Monitor(C2422HE)、Dell 27 Video Conferencing Monitor(C2722DE)與 Dell 34 Curved Video Conferencing Monitor(C3422WE)。他們皆支援一鍵啟動微軟 Teams 的功能,與支援 Windows Hello 的彈出式 5MP IR 視訊鏡頭。

價格方面則是親民許多,起價為 US$520 也就是約 NT$15,000 即可為居家上班上學的視訊會議需求幫上忙。

這波 Dell 還推出了不少新品,包括與高階螢幕同樣擁有很豐富連接機能的 USB-C Hub 螢幕 UltraSharp 27(U2722DE)與 24 USB-C Hub(U2422HE),也具備有不錯的色彩規格。此外更親民價格的選擇中,也有 24 吋與 27 吋的選擇。相信都是考慮購入新螢幕朋友可以參考的新品。

本篇圖片 / 引用來源

延伸閱讀:

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

MagSafe 也能這麼實用!MOFT 磁吸支架開箱體驗

Google 語音助理新增「耍廢模式(Do Nothing Mode)」讓你不得不 Chill

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

分類
發燒車訊

AirPods Pro 推超「牛」限量版,主動式降噪讓你隨時找到專屬自己的空間_網頁設計公司

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

才剛跨進 2021,緊跟著的農曆新年,Apple 也想到可以帶來一些新年禮物的好選擇 — 或該說,好「牛」的選擇。Apple Store 線上商店今天開賣了一款喜氣洋洋的 AirPods Pro 牛年限量版。繼續閱讀 AirPods Pro 推超「牛」限量版,主動式降噪讓你隨時找到專屬自己的空間報導內文。

AirPods Pro 牛年限量版官網(點我)
享最高 12 個月 0% 利率分期

▲圖片來源:Apple

AirPods Pro 推超「牛」限量版,主動式降噪讓你隨時找到專屬自己的空間

▲圖片來源:Apple

新年新希望,紅包很好有禮物也棒。雖說蘋果才剛剛推出更高階耳罩式降噪耳機 AirPods Max。不過對許多人而言,更好攜帶聆聽也相對低價的 AirPods Pro 應該仍是最佳體驗的降噪耳機選擇之一。重點是它也同樣能透過 H1 晶片、各式感測器及耳塞設計,提供主動式降噪搭配優質的通透模式的體驗,也支援「空間音訊(Spatial Audio)」的神奇功能,都是現階段最好的 AirPods 使用體驗。

▲圖片來源:Apple

這次蘋果為了迎接農曆新年,帶來了「很 Emoji」的牛年生肖 Logo 的 AirPods Pro 牛年限量版。在定價依然不變維持在 NT$7,990 的前提下,提供更符合年節主題的鐫刻與紅色 Logo 外包裝,希望有機會為消費者提供新年送禮的好選擇。是說,雖然你也可以自行在非限量版的 AirPods Pro 訂製鐫刻 — 還可以刻不只一個,而且也有 12 生肖可選。

AirPods Pro 牛年限量版官網(點我)
享最高 12 個月 0% 利率分期

不過這牛上加牛的 Happy 牛 year 意涵的特殊 Logo 搭配包裝,顯然還是比較有新年氛圍一些些(吧?)。總之,也趁這個機會先跟大家預祝新年快樂囉!

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

延伸閱讀:

Google 語音助理新增「耍廢模式(Do Nothing Mode)」讓你不得不 Chill

MagSafe 也能這麼實用!MOFT 磁吸支架開箱體驗

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

分類
發燒車訊

Netty源碼學習系列之3-ServerBootstrap的初始化_網頁設計公司

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

前言

    根據前文我們知道,NioEventLoopGroup和NioEventLoop是netty對Reactor線程模型的實現,而本文要說的ServerBootstrap是對上面二者的整合與調用,是一個統籌者和協調者。具體netty使用的是Reactor單線程模型還是多線程模型、抑或者主從多線程模型,都是ServerBootstrap的不同配置決定的。

    下面照例粘貼一下示例demo(以Reactor多線程模式構建),開始正文。

 1 public class NettyDemo1 {
 2     // netty服務端的一般性寫法
 3     public static void main(String[] args) {
 4         EventLoopGroup boss = new NioEventLoopGroup(1);
 5         EventLoopGroup worker = new NioEventLoopGroup();
 6         try {
 7             ServerBootstrap bootstrap = new ServerBootstrap();
 8             bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
 9                     .option(ChannelOption.SO_BACKLOG, 100)
10                     .childHandler(new ChannelInitializer<SocketChannel>() {
11                         @Override
12                         protected void initChannel(SocketChannel socketChannel) throws Exception {
13                             ChannelPipeline pipeline = socketChannel.pipeline();
14                             pipeline.addLast(new StringDecoder());
15                             pipeline.addLast(new StringEncoder());
16                             pipeline.addLast(new NettyServerHandler());
17                         }
18                     });
19             ChannelFuture channelFuture = bootstrap.bind(90);
20             channelFuture.channel().closeFuture().sync();
21         } catch (Exception e) {
22             e.printStackTrace();
23         } finally {
24             boss.shutdownGracefully();
25             worker.shutdownGracefully();
26         }
27     }
28 }

 

一、ServerBootstrap的初始化

    ServerBootstrap的無參構造器啥都沒做,它使用的build模式給屬性賦值,即上面示例中看到的,每執行一個賦值方法都會返回當前對象的引用使得可以繼續鏈式調用。下面挨個方法追蹤。

1 public ServerBootstrap() { }

 

1、ServerBootstrap.group方法

    ServerBootstrap有兩個可用重載group方法(如下的兩個),其中接收一個group入參的方法會調用有兩個入參的group方法,只是兩個參數傳同一個group。這兩個group方法決定了netty使用的Reactor線程模型的類型,一個group入參的方法對應Reactor單線程模型,兩個入參且不是同一個group的方法對應Reactor多線程模型或主從多線程模型(具體是哪一種取決於實例化parentGroup時的線程數)。此處只是提一下,先有個印象,後面會對線程模型進行詳細研究。

1 public ServerBootstrap group(EventLoopGroup group) {
2         return group(group, group);
3     }
1 public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
2         super.group(parentGroup);
3         ObjectUtil.checkNotNull(childGroup, "childGroup");
4         if (this.childGroup != null) {
5             throw new IllegalStateException("childGroup set already");
6         }
7         this.childGroup = childGroup;
8         return this;
9     }

    可以看到上述group方法對兩個入參進行了不同位置的賦值,將第一個參數parentGroup傳給了父類AbstractBootstrap的group方法,如下,即最終賦值給了AbstractBootstrap中的group屬性。第二個參數直接賦值給了ServerBootstrap的childGroup屬性。

1 public B group(EventLoopGroup group) {
2         ObjectUtil.checkNotNull(group, "group");
3         if (this.group != null) {
4             throw new IllegalStateException("group set already");
5         }
6         this.group = group;
7         return self();
8     }

 

2、ServerBootstrap.option/childOption方法和ServerBootstrap.attr/childAttr方法

    這四個方法只是做了屬性的賦值,分別賦值給了AbstractBootstrap的options屬性和attrs屬性以及ServerBootstrap的childOptions屬性和childAttrs屬性。

 1 public <T> B option(ChannelOption<T> option, T value) {
 2         ObjectUtil.checkNotNull(option, "option");
 3         if (value == null) {
 4             synchronized (options) {
 5                 options.remove(option);
 6             }
 7         } else {
 8             synchronized (options) {
 9                 options.put(option, value);
10             }
11         }
12         return self();
13     }
 1 public <T> B attr(AttributeKey<T> key, T value) {
 2         ObjectUtil.checkNotNull(key, "key");
 3         if (value == null) {
 4             synchronized (attrs) {
 5                 attrs.remove(key);
 6             }
 7         } else {
 8             synchronized (attrs) {
 9                 attrs.put(key, value);
10             }
11         }
12         return self();
13     }

 

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

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

3、ServerBootstrap.channel方法

    調用的是父類AbstractBootstrap的channel方法:

1 public B channel(Class<? extends C> channelClass) {
2         return channelFactory(new ReflectiveChannelFactory<C>(
3                 ObjectUtil.checkNotNull(channelClass, "channelClass")
4         ));
5     }

    可以看到先封裝成了一個ReflectiveChannelFactory對象,然後調用channelFactory方法,下面挨個看。ReflectiveChannelFactory的構造器如下,可見就是將傳入class對象的構造器取出來賦值,此時constructor存放的就是NioServerSocketChannel的構造器。

public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                    " does not have a public non-arg constructor", e);
        }
    }

    channelFactory方法的工作是將上面創建的ReflectiveChannelFactory對象賦值給AbstractBootstrap的channelFactory屬性:

1 public B channelFactory(ChannelFactory<? extends C> channelFactory) {
2         ObjectUtil.checkNotNull(channelFactory, "channelFactory");
3         if (this.channelFactory != null) {
4             throw new IllegalStateException("channelFactory set already");
5         }
6 
7         this.channelFactory = channelFactory;
8         return self();
9     }

 

4、ServerBootstrap.handler方法和ServerBootstrap.childHandler方法

    handler方法的入參賦值給了AbstractBootstrap的handler屬性,childHandler方法的入參賦值給了ServerBootstrap的childHandler屬性。看到這裏想必園友們也能看出ServerBootstrap的賦值規律了,凡是child開頭的都放在ServerBootstrap中,而不帶child的大多放在其父類ABstractBootstrap中。

1 public B handler(ChannelHandler handler) {
2         this.handler = ObjectUtil.checkNotNull(handler, "handler");
3         return self();
4     }
1 public ServerBootstrap childHandler(ChannelHandler childHandler) {
2         this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
3         return this;
4     }

 

5、完成賦值后ServerBootstrap的快照圖

 

 

小結

    ServerBootstrap的初始化過程看起來賦了很多值,但都只是做了準備工作,看起來輕鬆又簡單,但請注意,這是暴風雨前寧靜。前面的各種賦值到底有什麼用處?很多屬性分為有child前綴和沒有child前綴,這樣設置又有什麼意圖?下一期將進入ServerBootstrap的bind方法,這是netty的深水區,很多謎底也將在這裏得到揭曉,敬請期待!

 

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

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

分類
發燒車訊

Cypress系列(6)- Cypress 的重試機制_如何寫文案

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

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

如果想從頭學起Cypress,可以看下面的系列文章哦

https://www.cnblogs.com/poloyy/category/1768839.html

 

前言

重試(Retry-ability)是 Cypress 的核心概念之一,有助於我們寫出更加健壯的測試

 

命令和斷言

Cypress 測試中經常被調用的兩種類型,仍以前面說到的 testLogin.js 為栗子

最後的斷言解析

檢查標籤為 h1 的元素是否包含 jane.lane

 

斷言的一般步驟

  1. 用 cy.get() 查詢應用程序的DOM,找到元素
  2. 針對元素或元素列表進行斷言嘗試 ,我們示例中為 .should(“contain”, “jane.lane”) 

 

關於實際工作中的靈魂拷問

現在的 web 應用基本都是異步的,如果出現以下情況又應該怎麼處理呢?

  1. 如果斷言發生時,應用程序尚未更新DOM怎麼辦?
  2. 如果斷言發生時,應用程序正在等待其後端響應,而導致頁面暫無結果怎麼辦?
  3. 如果斷言發生時,應用程序正在進行密集計算,而導致頁面未及時更新怎麼辦?

上述情況再測試中經常會發生,一般處理方法是在斷言前價格固定等待時間(或像 selenium 一樣顯式、隱式等待),但仍有可能會發生測試失敗

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

 

Cypress 如何優美的解決上述問題

  1.  cy.get() 命令之後的斷言通過,則該命令成功執行完成
  2.  cy.get() 命令之後的斷言失敗,則 cy.get() 命令會自動重新查詢 web 應用程序的 DOM 樹,然後 Cypress 將再次嘗試對 cy.get() 返回的元素進行斷言
  3. 如果斷言仍然失敗, cy.get() 仍然會重新查詢 DOM 樹….以此類推
  4. 直到斷言成功 或 cy.get() 命令超時

總結

  • 其實很像selenium 的顯式等待,只不過 Cypress 是全局的,不用針對元素去單獨識別
  • Cypress 這種自動重試機制避免了在測試代碼中編寫硬編碼等待(強制等待),使測試代碼更加健壯

 

多重斷言

  • 在日常測試中,有時候需要多重斷言,即獲取元素後跟多個斷言
  • 在多重斷言中,Cypress 將按順序進行斷言,即當第一個斷言通過後,會進行第二個斷言,通過後進行第三個斷言…以此類推

 

列表的栗子

需求

  • 假設一個下拉列表,存在兩個選項,第一個選項是“iTesting”,第二個選項是“testerTalk”
  • 我們需要驗證兩個選項的存在,並且順序正確,代碼片段如下

代碼解析

  1. 總共有三個斷言:一個 should() ,兩個 expect() 
  2. and() 斷言實際上是 should() 斷言的別名,它是 should() 的自定義回調斷言,其中包含兩個 expect() 斷言
  3. 在測試執行過程中,如果第二個斷言失敗了,那第三個斷言永遠不會執行
  4. 如果導致第二個斷言失敗的原因被找到且修復了,且此時整個命令還沒有超時,則在進行第三個斷言時,還會再次重試第一、第二個斷言

 

重試(Retry-ability)的條件

前言

  • Cypress 並不會重試所有命令,當命令可能改變被測應用程序的狀態時,該命令將不會重試(如: click() ,畢竟要點擊)
  • Cypress 僅會重試那些查詢 DOM 的命令: cy.get() 、 find() 、 contains() 等
  • 可以通過官方文檔 Assertions 部分來檢查是否重試了特定命令:https://docs.cypress.io/zh-cn/guides/references/assertions.html#Chai

 

常用的可重試命令

 

重點啦!

重試的超時時間默認是 4秒,對應的配置項是: defaultCommondTimeout ,如果想改重試的超時時間,在 cypress.json 文件改對應的字段值即可

 

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

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

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

分類
發燒車訊

ASP.NET Core MVC+Layui使用EF Core連接MySQL執行簡單的CRUD操作_網頁設計公司

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

前言:

  本章主要通過一個完整的示例講解ASP.NET Core MVC+EF Core對MySQL數據庫進行簡單的CRUD操作,希望能夠為剛入門.NET Core的小夥伴們提供一個完整的參考實例。關於ASP.NET Core MVC+EF操作MsSQL Server詳情請參考官方文檔(https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-mvc/?view=aspnetcore-3.1)。

示例實現功能預覽:

 博客實例源碼下載地址:

https://github.com/YSGStudyHards/ASP.NET-Core-MVC-Layui-EF-Core-CRUD_Sample

一、創建ASP.NET Core Web應用程序:

注意,本章節主要以APS.NET Core 3.1版本作為博客的樣式實例!

 

二、添加EF Core NuGet包:

  若要在項目中使用EF Core操作MySQL數據庫,需要安裝相應的數據庫驅動包。 本章教程主要使用 MySQL數據庫,所以我們需要安裝相關驅動包MySql.Data.EntityFrameworkCore。

安裝方式:

點擊工具=>NuGet包管理器=>程序包管理器控制台輸入以下命令:

Install-Package MySql.Data.EntityFrameworkCore -Version 8.0.20

點擊工具=>NuGet包管理器=>管理解決方案的NuGet程序包:

搜索:MySql.Data.EntityFrameworkCore  點擊安裝。

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

三、創建對應數據庫表的實體模型:

   注意該篇博客使用的是手動模型優先的方式進行數據庫表字段與模型屬性映射,當然如果大家覺得這樣子比較麻煩的話可以真正意義上的模型優先,直接創建模型在program.cs中配置創建對應模型的數據庫邏輯代碼即可無需手動創建數據庫,可參考官網文檔教程(https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-rp/intro?view=aspnetcore-3.1&tabs=visual-studio#create-the-database)。

創建用戶模型(UserInfo):

注意:屬性大小寫和數據庫中的表字段保持一致,Id 屬性成為此類對應的數據庫表的主鍵列。 默認情況下,EF Core 將名為 Id 或 xxxID 的屬性視為主鍵。 有關詳細信息,請參閱 F Core – 密鑰。

    /// <summary>
    /// 學生信息模型
    /// </summary>
    public class UserInfo
    {
        /// <summary>
        /// 學生編號
        /// </summary>
        [Description("學生編號")]
        public int? Id { get; set; }

        /// <summary>
        /// 學生姓名
        /// </summary>
        [Description("學生姓名")]
        public string UserName { get; set; }

        /// <summary>
        /// 學生性別
        /// </summary>
        [Description("學生性別")]
        public string Sex { get; set; }

        /// <summary>
        /// 學生聯繫電話
        /// </summary>
        [Description("學生聯繫電話")]
        public string Phone { get; set; }

        /// <summary>
        /// 學生描述
        /// </summary>
        [Description("學生描述")]
        public string Description { get; set; }

        /// <summary>
        /// 學生愛好
        /// </summary>
        [Description("學生愛好")]
        public string Hobby { get; set; }
    }

四、將數據庫連接字符串添加到 appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
        "MySqlConnection":"Data Source=127.0.0.1;User ID=root;Password=root;DataBase=SchoolUserInfo_db"
  }
}

五、創建數據庫上下文:

概述:

 數據庫上下文類是為給定數據模型協調 EF Core 功能的主類。 上下文派生自 Microsoft.EntityFrameworkCore.DbContext。 上下文指定數據模型中包含哪些實體。 在此項目中將數據庫上下文類命名為 SchoolUserInfoContext。

創建:

using Microsoft.EntityFrameworkCore;
using Model;

namespace Dal
{
    public class SchoolUserInfoContext : DbContext
    {
        public SchoolUserInfoContext(DbContextOptions<SchoolUserInfoContext> options)
            : base(options)
        {
        }

        /// <summary>
        /// DbSet實體集屬性對應數據庫中的表(注意實體集名必須與表明一致)
        /// </summary>
        public DbSet<UserInfo> UserInfos { get; set; }

        /// <summary>
        /// TODO:當數據庫創建完成后, EF 創建一系列數據表,表名默認和 DbSet 屬性名相同。 集合屬性的名稱一般使用複數形式,但不同的開發人員的命名習慣可能不一樣,
/// 開發人員根據自己的情況確定是否使用複數形式。 在定義 DbSet 屬性的代碼之後,添加下面代碼,對DbContext指定單數的表名來覆蓋默認的表名。
/// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<UserInfo>().ToTable("UserInfo"); } } }

六、將上下文添加到 Startup.cs 中的依賴項注入:

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //注入EF Core數據庫上下文服務
            services.AddDbContext<SchoolUserInfoContext>(options =>
                options.UseMySQL(Configuration.GetConnectionString("MySqlConnection")));

            services.AddControllersWithViews();
        }

七、引入Layui樣式和js:

前往官網下載Layui相關樣式和js包,下載地址:https://www.layui.com/

Layui彈出層插件layer.js(有很多地方需要用到彈窗),下載地址:https://layer.layui.com/

將相關文件存放到wwwroot文件下:

 

將相關文件引入默認布局頁面中:

八、 ASP.NET Core MVC 和 EF Core實現MySQL  CRUD功能:

注意在這裏主要展示的EF Core與數據庫操作的部分代碼,詳細代碼可下載實例源碼查看。

Create:

        /// <summary>
        /// 學生信息添加
        /// </summary>
        /// <param name="addUserInfo"></param>
        /// <returns></returns>
        public async Task<bool> Create(AddUserInfoViewModel addUserInfo)
        {
            try
            {
                var userInfo=new UserInfo()
                {
                    UserName = addUserInfo.UserName,
                    Sex = addUserInfo.Sex,
                    Hobby = addUserInfo.Hobby,
                    Phone = addUserInfo.Phone,
                    Description = addUserInfo.Description
                };

                _shoSchoolUserInfoContext.UserInfos.Add(userInfo);

                await _shoSchoolUserInfoContext.SaveChangesAsync();

                return true;
            }
            catch
            {
                return false;
            }
        }

Retrieve:

        /// <summary>
        /// 獲取用戶信息
        /// </summary>
        /// <param name="page">當前頁碼</param>
        /// <param name="limit">显示條數</param>
        /// <param name="userName">用戶姓名</param>
        /// <returns></returns>
        public async Task<PageSearchModel> GetPageListData(int page = 1, int limit = 15, string userName = "")
        {
            try
            {
                List<UserInfo> listData;
                var totalCount = 0;
                if (!string.IsNullOrWhiteSpace(userName))
                {
                    listData = await _shoSchoolUserInfoContext.UserInfos.Where(x => x.UserName.Contains(userName)).OrderByDescending(x => x.Id).Skip((page - 1) * limit).Take(limit).ToListAsync();

                    totalCount = _shoSchoolUserInfoContext.UserInfos
                        .Count(x => x.UserName.Contains(userName));
                }
                else
                {
                    listData = await _shoSchoolUserInfoContext.UserInfos.OrderByDescending(x => x.Id).Skip((page - 1) * limit).Take(limit).ToListAsync();

                    totalCount = _shoSchoolUserInfoContext.UserInfos.Count();
                }

                return new PageSearchModel()
                {
                    ResultMsg = "success",
                    Code = 200,
                    TotalCount = totalCount,
                    DataList = listData
                };
            }
            catch (Exception e)
            {
                return new PageSearchModel() { Code = 400, ResultMsg = e.Message };
            }
        }

Update:

        /// <summary>
        /// 學生信息修改
        /// </summary>
        /// <param name="userInfo"></param>
        /// <returns></returns>
        public async Task<bool> Update(UserInfo userInfo)
        {

            try
            {
                _shoSchoolUserInfoContext.UserInfos.Update(userInfo);

                await _shoSchoolUserInfoContext.SaveChangesAsync();

                return true;
            }
            catch
            {
                return false;
            }
        }

Delete:

        /// <summary>
        /// 學生信息刪除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<bool> Delete(int? id)
        {
            try
            {
                var searchUserInfo = await _shoSchoolUserInfoContext.UserInfos.FindAsync(id);

                if (searchUserInfo == null)
                {
                    return false;
                }

                _shoSchoolUserInfoContext.UserInfos.Remove(searchUserInfo);
                await _shoSchoolUserInfoContext.SaveChangesAsync();

                return true;
            }
            catch
            {
                return false;
            }
        }

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

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

分類
發燒車訊

SpringAOP使用及源碼分析(SpringBoot下)_網頁設計

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

一、SpringAOP應用

  1. 先搭建一個SpringBoot項目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.mmc</groupId>
	<artifactId>springboot-study</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-study</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  1. 定義一個業務邏輯類,作為切面
public interface CalculationService {

    /**
     * 加法運算
     * @param x
     * @param y
     * @return
     */
    public Integer add(Integer x,Integer y);
}

/**
 * @description:
 * @author: mmc
 * @create: 2020-06-01 14:22
 **/
@Service
public class CalculationServiceImpl implements CalculationService {

    @Override
    public Integer add(Integer x, Integer y) {
        if(x==null||y==null){
            throw  new NullPointerException("參數不能為空");
        }
        return x+y;
    }
}
  1. 定義一個切面類,添加通知方法
  • 前置通知(@Before):logStart:在目標方法(div)運行之前運行
  • 後置通知(@After):logEnd:在目標方法(add)運行結束之後運行(無論方法正常結束還是異常結束)
  • 返回通知(@AfterReturning):logReturn:在目標方法(add)正常返回之後運行
  • 異常通知(@AfterThrowing):logException:在目標方法(add)出現異常以後運行
  • 環繞通知(@Around):動態代理,手動推進目標方法運行(joinPoint.procced())

/**
 * @description:  切面類
 * @author: mmc
 * @create: 2020-06-01 14:24
 **/
@Aspect
@Component
public class LogAspects {

    //抽取公共的切入點表達式
    //1、本類引用
    //2、其他的切面引用
    @Pointcut("execution(public Integer com.mmc.springbootstudy.service.CalculationService.*(..))")
    public void pointCut(){};

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println(""+joinPoint.getSignature().getName()+"運行。。。@Before:參數列表是:{"+Arrays.asList(args)+"}");
    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println(""+joinPoint.getSignature().getName()+"結束。。。@After");
    }


    //JoinPoint一定要出現在參數表的第一位
    @AfterReturning(value="pointCut()",returning="result")
    public void logReturn(JoinPoint joinPoint,Object result){
        System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:運行結果:{"+result+"}");
    }

    @AfterThrowing(value="pointCut()",throwing="exception")
    public void logException(JoinPoint joinPoint,Exception exception){
        System.out.println(""+joinPoint.getSignature().getName()+"異常。。。異常信息:{"+exception+"}");
    }
}
  1. 寫一個controller測試
@RequestMapping("/testaop")
   @ResponseBody
    public Integer testaop(Integer x,Integer y){
       Integer result = calculationService.add(x, y);
       return result;
   }
  1. 測試

add運行。。。@Before:參數列表是:{[2, 3]}
add結束。。。@After
add正常返回。。。@AfterReturning:運行結果:{5}

二、源碼分析

主線流程圖:

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

  1. spring.factories文件里引入了AopAutoConfiguration類
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	//看配置文件,如果配置的spring.aop.proxy-target-class為false則引入JdkDynamicAutoProxyConfiguration
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
			matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	//開啟AspectJAutoProxy
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	//看配置文件,如果配置的spring.aop.proxy-target-class為true則引入CglibAutoProxyConfiguration 
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}

}

在包目錄下找到配置文件,並且發現他的值為true

在上面的方法上有EnableAspectJAutoProxy註解,並傳入了proxyTargetClass=true

  1. 進入@EnableAspectJAutoProxy註解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//引入了AspectJAutoProxyRegistrar
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}
  1. 進入AspectJAutoProxyRegistrar類
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //註冊了自動自動代理類
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }
}
  1. 進入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法裏面
 public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

可以看到返回了一個BeanDefinition,裏面的BeanClass類型是AnnotationAwareAspectJAutoProxyCreator,這個類看名字是一個AOP的動態代理創建類,裏面沒有啥可疑的方法。在IDEA里按Ctrl+H看他的繼承結構。有一個父類AbstractAutoProxyCreator,這個類實現了BeanPostProcessor接口。這個接口是Bean的擴展接口,在bean初始化完成後會調用到他的postProcessAfterInitialization(Object bean, String beanName)方法。

  1. 方法內容如下
 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                //如果有必要,進行包裝  
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
        //獲取切面的方法,第9點那裡展開討論
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                //創建動態代理
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }
  1. 可以看出這裏已經在開始創建動態代理了
  protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
        }
        //動態代理工廠
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        if (!proxyFactory.isProxyTargetClass()) {
            if (this.shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            } else {
                this.evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
        //切面那裡的方法
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        this.customizeProxyFactory(proxyFactory);
        proxyFactory.setFrozen(this.freezeProxy);
        if (this.advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
        //獲取動態代理類
        return proxyFactory.getProxy(this.getProxyClassLoader());
    }
  1. 學過AOP的人都知道動態代理的方式有兩種,一種JDK代理,一種CGLIB動態代理。那麼Spring裏面是怎麼選擇的呢?答案就在這裏:
 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // 1.config.isOptimize()是否使用優化的代理策略,目前使用與CGLIB
        // config.isProxyTargetClass() 是否目標類本身被代理而不是目標類的接口
        // hasNoUserSuppliedProxyInterfaces()是否存在代理接口

        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                //目標類不是接口或不是代理類就使用cglib代理
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }
  1. Cglib的代理類是CglibAopProxy、ObjenesisCglibAopProxy,JDK的代理類是JdkDynamicAopProxy。在這些類裏面對目標類進行了代理,在執行方法的時候就是執行的代理類的方法,而實現了切面編程的效果。
  2. 主線流程就是這些了,還有一個沒說的就是我們如何獲取的切面方法,@Before(“pointCut()”)這些註解又是如何生效的?再回到AbstractAutoProxyCreator的wrapIfNecessary()方法
    裏面有這句代碼:
 Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
 
 
  @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
        List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
        return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
    }
    
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //查找候選的要切面附加的方法,這裏加進去的
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        this.extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
        }

        return eligibleAdvisors;
    }
    
    
    
  1. 他會找到Aspect類,然後遍歷裏面的方法,並獲取Pointcut,然後構造出Advisor,加入到集合List advisors里,供動態代理時使用

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

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

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品