海洋升溫致白化 大堡礁25年內半數珊瑚死亡_台中搬家

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

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

摘錄自2020年10月14日自由時報報導

澳洲大堡礁(Great Barrier Reef)是世界最大的珊瑚礁群,但受到地球暖化影響全球生態,最新研究顯示,大堡礁在過去25年內,有半數的珊瑚死亡,科學家也警告,氣候變遷正對海洋生態系統造成不可逆的破壞。

綜合外媒報導,位在澳洲東北海岸的大堡礁1981年被列入世界自然遺產,根據刊登在《皇家學會報告》(Proceedings of the Royal Society Journal)的最新研究,1990年代中期以來,大堡礁各個珊瑚群正在以驚人的速度減少,其中以較大的珊瑚物種影響最大,幾乎從大堡礁北部消失。

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

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

研究合著者、詹姆士庫克大學的狄澤爾(Andy Dietzel)提到,研究發現大堡礁的復原能力和過去相比,受到嚴重損害,因為幼年珊瑚減少,能夠繁衍的珊瑚也減少。休斯則表示,除非各國履行《巴黎協議》的承諾,否則珊瑚將持續消失。休斯指出,對於生長快的物種,恢復時間大約需要10年時間,在溫度持續上升的情況下,會不斷出現白化事件,獲得復育的機率幾乎為零。

生物多樣性
氣候變遷
國際新聞
澳洲
珊瑚白化

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

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

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

微軟 Surface Hub 2S 在台推出,滿足視訊會議、團隊協作與商務需求_台中搬家

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

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

自從 COVID-19 疫情爆發以來,遠距工作、混合式辦公模式已經是全球企業商務的新工作型態,為了因應延伸出來更多與過去工作型態不一樣的需求,微軟在今日(1/21)的發表會中,正式宣佈在台灣推出 Surface Hub 2S 這款將視訊會議與各種商務用途集於一身的新品。

微軟 Surface Hub 2S 在台推出,滿足視訊會議、團隊協作與商務需求

Surface Hub 2S 整合了傳統會議室中的投影設備、會議是白板與視訊通訊解決方案等茸硬體配置,以提供團隊成員線上與實體的參與度,幫助企業解決在疫情期間遠距溝通與團隊協作大幅提升的困難,維持高生產力。

Surface Hub 2S 共推出兩種版本,分別為 50 吋與 85 吋。搭載 4K+ 解析度觸控顯示器與 4K 鏡頭,擁有 90 度的視角,在中型會議室裏面可無死角地呈現會議現場,讓遠端連線的對象可完整而清晰地看見會議中的與會人員。鏡頭還可利用配置在螢幕上、左、右三側的 USB-C 更換裝設位置,內建遠場麥克風可直接對會議現場進行收音,範圍可達 5 – 6 公尺,並且透過立體聲喇叭提供用戶最加聲音體驗,無須另外安裝收音麥克風等設備。網路連線部分可支援有線網路線與 WiFi 網路,讓會議隨時保時連線不中斷。另外,為了提供更靈活的協作,Surface Hub 2S 配備有 Surface Hub 2 手寫筆,在會議中讓你發揮最佳表現。

在螢幕後方即為主機裝置,運行完整如筆電般的 Windows 10 的體驗,讓用戶可以更直覺地操作,讓使用者可流暢地在筆電與 Surface Hub 2S 之間切換,根據官方人員的介紹,位於螢幕後方一體式的主機部分除配有豐富的連接埠外採可抽取式設計,未來用戶可以依照需求升級系統內部配備,因應科技推進的腳步讓生產力蒸蒸日上。

Surface Hub 2S 50 吋版本售價訂為新台幣 298,788 元,即日起開始銷售,85 吋款則即日起同步開放預購。

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

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

同場加映:Surface Pro 7+ 商務版同步上市

近日最新推出的 Surface Pro 7+ 商務版也宣布於今日在台上市,共有 WiFi 版與 LTE Advances 兩種版本。採用了最新的第 11 代Intel Core 處理器,處理效能提升 2.1 倍,續航力最長可達 15 小時,為人們提供了所需的效能,讓他們能夠隨時隨地展開工作。此外, Surface Pro 7+ 商務版配有 USB-A 和 USB-C 兩個連接埠,可外接顯示器與所需的周邊設備打造完整的工作體驗。在 Surface Pro 7+ 商務版上搭載了支援 1080p 高畫質的前置和後置鏡頭以及 Dolby Atmos 喇叭和雙重遠場麥克風,讓大家可以互相看見、聽見。

Surface Pro 7+ 商務版採用了更為輕盈、永續性的商業包裝。此包裝比上一代輕了 23%,由 99% 的天然纖維材料製成,其中 64% 採用了可回收材料。此外,首次提供了從開機就有的 Windows 增強硬體安全功能,從初期部署到整個裝置的生命週期,客戶都可以透過雲端服務來對 Surface 裝置進行管理和更新,無需讓 IT 人員親自接觸裝置,這有助於保護身份資訊,並防禦惡意驅動程式和供應鏈的攻擊。


Surface Pro 7+ 商務版還搭載了可拆卸式固態硬碟來保存資料,以滿足企業和教育組織的安全和隱私需求,此功能與微軟 BitLocker 保護功能相互結合,使客戶可以在裝置發生問題時尚可儲存資料。這款新機售價訂為 29,088 元起,即日起可洽詢經銷購買。

您也許會喜歡:

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

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

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

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

Spring AOP學習筆記01:AOP概述_台中搬家

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

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

1. AOP概述

  軟件開發一直在尋求更加高效、更易維護甚至更易擴展的方式。為了提高開發效率,我們對開發使用的語言進行抽象,走過了從彙編時代到現在各種高級語言繁盛之時期;為了便於維護和擴展,我們對某些相同的功能進行歸類並使之模塊化,衝出了最初的”原始部落”,走過了從過程化編程到面向對象編程(OOP)的”短暫而漫長”的歷程。但不管走過的路有多長,多麼坎坷,我們一直沒有停止尋找更加完美、更加高效的軟件開發方法,過去如此,現在亦然。

  當OOP被提出來,以取代過去基於過程化編程的開發方法時,或許那個時代的人都會以為,面向對象編程和面向對象的軟件開發就是我們一直追求的那顆能夠搞定一切的”銀彈”。但不得不承認的是,即使面向對象的軟件開發模式,依然不能很好地解決軟件開發中的所有問題。

  軟件開發的目的,最終是為了解決各種需求,包括業務需求和系統需求。使用面向對象方法,我們可以對業務需求等普通關注點進行很好的抽象和封裝,並且使之模塊化。但對於系統需求(比如日誌記錄、權限驗證、事務管理等)一類的關注點來說,情況卻有所不同。

  對於業務需求而言,需求與其具體實現之間的關係基本上是一對一的。我們可以在系統中某一個確定的點找到針對這種需求的實現,無論從開發還是維護的角度,都比較方便。比如電商系統中的賬戶管理模塊、訂單模塊、支付模塊等,可以很容易地按照功能劃分模塊並完成開發。

  但是,事情並沒有結束!開發中為了調試或在進入生產環境後為了對系統進行監控,我們需要為這些業務需求的實現對象添加日誌記錄功能;或者,業務方法的執行需要一定的權限限制,那麼方法執行前肯定需要有相應的安全檢查功能。而這些則屬於系統需求的範疇。雖然需求都很明確(加入日誌記錄、加入安全檢查),但是要將這些需求以面向對象的方式實現並集成到整個的系統中去,可就不是一個需求對應一個實現那麼簡單了,系統中的每個業務對象都需要加入日誌記錄,加入相應的安全檢查,那麼,這些需求的實現代碼就會遍及所有業務對象。

  對於系統中普通的業務關注點,OOP可以很好地對其進行分解並使之模塊化,但卻無法更好地避免類似於系統需求的實現在系統中各處散落這樣的問題。所以,我們要尋求一種更好的方法,它可以在OOP的基礎上更上一層樓,提出一套全新的方法論來避免以上問題,也可以提供某種方法對基於OOP的開發模式做一個補足,幫助OOP以更好的方式解決以上問題。迄今為止,我們還找不到比OOP更加有效的軟件開發模式。不過,我們找到了後者,那就是AOP,對OOP的補足。

  AOP全稱為Aspect-Oriented Programming,中文通常翻譯為面向方面編程。使用AOP,我們可以對類似於Logging和Security等系統需求進行模塊化的組織,簡化系統需求與實現之間的對比關係,進而使得整個系統的實現更具模塊化。

  對於一個軟件系統而言,日誌記錄、安全檢查、事務管理等系統需求就像一把把刀“惡狠狠”地橫切到我們組織良好的各個業務功能模塊之上。以AOP的行話來說,這些系統需求是系統中的橫切關注點(cross-cutting concern)。使用傳統方法,我們無法更好地以模塊化的方式,對這些橫切關注點進行組織和實現。所以AOP引入了Aspect的概念,用來以模塊化的形式對系統中的橫切關注點進行封裝。Aspect 之對於AOP,就相當於Class之對於OOP。我們說過AOP僅是對OOP方法的一種補足,當我們把以Class形式模塊化的業務需求和以Aspect形式模塊化的系統需求拼裝到一起的時候,整個系統就算完成了。

 

2. AOP相關概念

  在進一步學習Spring AOP之前,我們還需要了解一下AOP涉及的相關概念:

2.1 切點(JoinPoint)

  在系統運行之前,AOP的功能模塊都需要織入到OOP的功能模塊中。所以,要進行這種織入過程,我們需要知道在系統的哪些執行點上進行織入操作,這些將要在其之上進行織入操作的系統執行點就稱之為切點(Joinpoint)。對應到spring中可以理解為具體攔截的某個業務點。

  以下是一些較為常見的Joinpoint類型

  • 方法調用(Method Call)。當某個方法被調用的時候所處的程序執行點。
  • 方法調用執行(Method Call execution)。也可以稱之為方法執行,該Joinpoint類型代表的是某個方法內部執行開始時點,這需要與上面的方法調用類型的Jointpoint進行區分。方法調用(method call)是在調用對象上的執行點,而方法執行(method execution)則是在被調用到的方法邏輯執行的時點,對於同一對象,方法調用要先於方法執行。
  • 構造方法調用(Constructor Call)。程序執行過程中對某個對象調用其構造方法進行初始化的時點。
  • 構造方法執行(Constructor Call Execution)。構造方法執行和構造方法調用之間的關係類似於方法執行和方法調用之間的關係,指的是某個對象構造方法內部執行的開始時點。
  • 字段設置(Field Set)。對象的某個屬性通過setter方法被設置或者直接被設置的時點。
  • 字段獲取(Field Get)。對象的某個屬性通過getter方法獲取或者直接訪問的時點。
  • 異常處理(Exception Handler Execution)。在某些類型異常拋出后,對應的異常處理邏輯執行的時點。
  • 類初始化(Class initialization)。類中某些靜態類型或者靜態塊的初始化時點。

  基本上程序執行過程中你認為必要的執行時點都可以作為Joinpoint,但是對於一些位置,具體的AOP實現產品在捕捉的時候可能存在一定的困難,或者能夠實現但付出太多卻可能收效甚微。在Spring AOP中最常見的就是前面的方法執行類型的Joinpoint。

2.2 切面(Pointcut)

  Pointcut概念代表的是JointPoint的表述方式。將橫切邏輯織入當前系統的過程中,需要參照Pointcut規定的Jointpoint信息,才可以知道應該往系統的哪些Joinpoint上織入橫切邏輯。

一個Pointcut可以指定系統中符合條件的一組Joinpoint,但是其是如何來指定的呢?通常有如下幾種方式:

  • 直接指定Joinpoint所在方法名稱。這種形式的Pointcut表述方式比較簡單,而且功能單一,通常只限於支持方法級別Joinpoint的AOP框架。並且這種方式只能一個一個指定,所以通常只限於Joinpoint較少且較為簡單的情況。

  • 正則表達式。這是比較普遍的Pointcut表達方式,可以充分利用正則表達式的強大功能來歸納表述符合某種條件的多組Joinpoint。幾乎現在大部分的Java平台的AOP產品都支持這種形式的Pointcut表達形式,包括Jboss AOP、Spring AOP以及AspectWerkz等。

  • 使用特定的Pointcut表述語言。這是一種最為強大的表達Pointcut的方式,很靈活,但具體實現起來可能會很複雜,需要設計該表述語言的語法,實現相應的解釋器等許多工作。AspectJ使用這種方式來指定Pointcut,它提供了一種類似於正則表達式的針對Pointcut的表述語言,在表達Pointcut方面支持比較完善,而且Spring 2.0之後也是支持這種方式。

2.3 通知(Advice)

  Advice是單一橫切關注點邏輯的載體,它代表將會織入到Joinpoint的橫切邏輯。如果將Aspect比作OOP中的Class,那麼Advice就相當於Class中的Method。

  按照Advice在Jointpoint位置執行時機的差異或者完成功能的不同,Advice可以分成多種具體形式。

  • Before Advice

  Before Advice是在Joinpoint指定位置之前執行的Advice類型。通常,它不會中斷程序執行流程,但如果必要,可以通過在Before Advice中拋出異常的方式來中斷當前程序流程。如果當前Before Advice將被織入到方法執行類型的Joinpoint,那麼這個Before Advice就會先於方法執行而執行。   通常,可以使用Before Advice做一些系統的初始化工作,比如設置系統初始值,獲取必要系統資源。

  • After Advice

  顧名思義,After Advice就是在相應連接點之後執行的Advice類型,但該類型的Advice還可以細分為三種:

  After returning Advice。只有當前Joinpoint處執行流程正常完成后,After returning Advice才會執行。

  After throwing Advice。又稱Throws Advice,只有在當前Joinpoint執行過程中拋出異常的情況下,才會執行。比如某個方法執行類型的Joinpoint拋出某異常而沒有正常返回。

  After Advice。或許叫After (Finally) Advice更為確切,該類型Advice不管Joinpoint處執行流程是正常終了還是拋出異常都會執行,就好像Java中的finally塊一樣。

  • Around Advice

  Around Advice對附加其上的Joinpoint進行”包裹”,可以在Joinpoint之前和之後都指定相應的邏輯,甚至於中斷或者忽略Joinpoint處原來程序流程的執行。

2.4 Aspect

  Aspect是對系統中的橫切關注點邏輯進行模塊化封裝的AOP概念實體,可以理解為攔截器類,其中會定義切點以及攔截處理邏輯。通常情況下,Aspect可以包含多個Pointcut以及相關Advice定義。在Spring中,是通過使用@AspectJ註解並結合普通POJO來聲明Aspect的。

@AspectJ
public class AspectClass{
    // pointcut 定義

    // advice 定義
}

2.5 目標對象

  符合Pointcut所指定的條件,將在織入過程中被織入橫切邏輯的對象,稱為目標對象(Target Object)。

 

3. Spring AOP

  AOP只是一種理念,要實現這種理念,通常需要一種現實的方式。Spring AOP就是一款AOP的實現產品,Spring AOP是Spring核心框架的重要組成部分,通常認為它與Spring的IoC容器以及Spring框架對其他JavaEE服務的集成共同組成了Spring框架的”質量三角”,足見其地位之重要。

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

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

  在Java語言的基礎之上,Spring AOP對AOP的概念進行了適當的抽象和實現,使得每個AOP的概念都可以落到實處,在詳細學習Spring AOP概念實體之前,我們有必要先看一下其是如何運作的。

  Spring AOP從最初發布以來,一直延續了最初的設計,也就是採用動態代理機制和字節碼生成技術來實現基於Java語言的簡單而強大的AOP框架。與最初的AspectJ採用編譯器將橫切邏輯織入目標對象不同,動態代理機制和字節碼生成都是在運行期間為目標對象生成一個代理對象,再將橫切邏輯織入到這個代理對象中,系統最終使用的是織入了橫切邏輯的代理對象,而不是真正的目標對象。

  要理解這種差別以及最終可以達到的效果,有必要先從動態代理機制的根源–代理模式(Proxy Pattern)開始說起。。。

 

3.1 設計模式之代理模式

  說到代理,舉幾個簡單的例子,比如房地產中介就是一種代理,我們偶爾使用的網絡代理也是一種代理,類似例子很多,就不一一列舉了。代理處於訪問者與被訪問者之間,可以隔離這兩者之間的直接交互,訪問者與代理打交道就好像在跟被訪問者在打交道一樣,因為代理通常幾乎會全權擁有被代理者的職能,代理能夠處理的訪問請求就不必要勞煩被訪問者來處理了。從這個角度來講,有兩個好處:

  • 代理可以減少被訪問者的負擔;
  • 即使代理最終要將訪問請求轉發給真正的被訪問者,它也可以在轉發訪問請求之前或者之後加入特定的邏輯,比如安全訪問限制;

  在軟件系統中,代理機制的實現有現成的設計模式支持,即代理模式。在代理模式中通常涉及4種角色:

  • ISubject。該接口是對被訪問者或者被訪問資源的抽象。在嚴格的設計模式中,這樣的抽象接口是必須的。
  • SubjectImpl。這是被訪問者或者被訪問資源的具體實現類。如果你要訪問某位明星,那麼SubjectImpl就是你想要訪問的明星;如果你想要買房子,那麼SubjectImpl就是房主。
  • SubjectProxy。這是被訪問者或者被訪問資源的代理實現類,該類持有一個ISubject接口的具體實例。在這個場景中,我們要對SubjectImpl進行代理,那麼SubjectProxy現在持有的就是SubjectImpl的實例。
  • Client。這代表訪問者的抽象角色,Client將會訪問ISubject類型的對象或者資源。在這個場景中,Client將會請求具體的SubjectImpl實例,但Client無法直接請求其真正要訪問的資源SubjectImpl,而是必須通過ISubject資源的訪問代理類SubjectProxy進行。

  SubjectImpl和SubjectProxy都實現了相同的接口ISubject,而SubjectProxy內部持有SubjectImpl的引用。當Client通過request()請求服務的時候,SubjectProxy將轉發該請求給SubjectImpl。從這個角度來說,SubjectProxy反而有多此一舉之嫌了,不過SubjectProxy的作用不只局限於請求的轉發,更多時候是對請求添加更多訪問限制。SubjectImpl和SubjectProxy之間的調用關係如下代碼所示:

public class SubjectProxy implements ISubject{
    private ISubject subject;   // Inject SubjectImpl to SubjectProxy
    public String request(){
        // add pre-process logic if necessary

        String originalResult = subject.request();

        // add post process logic if necessary

        return "Proxy:" + originalResult;
    }
    public ISubject getSubject(){
        return subject;
    }
    public void setSubject(ISubject subject){
        this.subject = subject;
    }
}

public class SubjectImpl implements ISubject{
    public String request(){
        // process logic
        return "OK";
    }
}

  在將請求轉發給被代理對象SubjectImpl之前或者之後,都可以根據情況插入其他處理邏輯,比如在轉發之前記錄方法執行開始時間,在轉發之後記錄結束時間,這樣就能夠對SubjectImpl的request()執行的時間進行檢測。或者,可以只在轉發之後對SubjectImpl的request()方法返回結果進行覆蓋,返回不同的值。甚至,可以不做請求轉發,這樣,就不會有SubjectImpl的訪問發生。

  代理對象SubjectProxy就像是SubjectImpl的影子,只不過這個影子通常擁有更多的功能。如果SubjectImpl是系統中Jointpoint所在的對象(即目標對象),那麼就可以為這個目標對象創建一個代理對象,然後將橫切邏輯添加到這個代理對象中。當系統使用這個代理對象的時候,原有邏輯的實現和橫切邏輯就完全融合到一個系統中。

  Spring AOP本質上就是採用這種代理機制實現的,但是,具體實現細節上有所不同。我們來看一下上面的代理實現,我們是將代理類直接寫好,然後在代碼中手動初始化代理類並通過調用代理類來實現代理功能,發現沒有,如果系統裏面有很多類需要代理相同的能,那麼我們就要寫很多的代理類,儘管它們代理的內容是一樣的,這樣是有問題的。上面這種為對應的目標對象創建靜態代理的方法,原理上是可行的,但具體應用上存在問題,所以要尋找其他方法,那有沒有呢,答案是肯定有的,就是接下來我們要講的動態代理。

 

3.2 動態代理

  JDK1.3之後,引入了動態代理(Dynamic Proxy)機制,可以在運行期間,為相應的接口(Interface)動態生成對應的代理對象,從而幫助我們走出最初使用靜態代理實現AOP的窘境。

  動態代理機制的實現主要由一個類和一個接口組成,即java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。InvacationHandler就是我們實現橫切邏輯的地方,它是橫切邏輯的載體,作用跟Advice是一樣的。所以在使用動態代理機制實現AOP的過程中,我們可以在InvocationHandler的基礎上細化程序結構,根據Advice的類型,分化出對應不同的Advice類型的程序結構。

  所以,我們可以將橫切關注點邏輯封裝到動態代理的InvocationHandler中,然後在系統運行期間,根據橫切關注點需要織入的模塊位置,將橫切邏輯織入到相應的代理類中。以動態代理類為載體的橫切邏輯,現在當然就可以與系統其他實現模塊一起工作了。

  動態代理雖好,但不能滿足所有的需求,這種方式實現的唯一缺點或者說優點就是,所有需要織入橫切關注點邏輯的模塊類都得實現相應的接口,因為動態代理機制只針對接口有效。如果某個類沒有實現任何的接口,就無法使用動態代理機製為其生成相應的動態代理對象。對於沒有實現任何接口的目標對象我們需要尋找其他方式為其動態的生成代理對象。

  默認情況下,Spring AOP發現目標對象實現了相應接口,則採用動態代理機製為其生成代理對象實例。而如果目標對象沒有實現任何接口,Spring AOP則會嘗試使用一個稱為CGLIB(Code Generation Library)的開源的動態字節碼生成類庫,為目標對象生成動態的代理對象實例。

 

3.3 動態字節碼增強

  使用動態字節碼生成技術擴展對象行為的原理是,我們可以對目標對象進行繼承擴展,為其生成相應的子類,而子類可以通過覆寫來擴展父類的行為,只要將橫切邏輯的實現放到子類中,然後讓系統使用擴展后的目標對象的子類,就可以達到與代理模式相同的效果了。

  但是使用繼承的方式來擴展對象定義,也不能像靜態代理模式那樣,為每個不同類型的目標對象都單獨創建相應的擴展子類。所以,我們要藉助於CGLIB這樣的動態字節碼生成庫,在系統運行期間動態地為目標對象生成相應的擴展子類。

  我們知道,Java虛擬機加載的文件都是符合一定規範的,所以,只要交給Java虛擬機運行的文件符合Java class規範,程序的運行就沒有問題。通常的class文件都是從Java源代碼文件使用Javac編譯器編譯而成的,但只要符合Java class規範,我們也可以使用ASM或者CGLiB等Java工具庫,在程序運行期間,動態構建字節碼的class文件。

  在這樣的前提下,我們可以為需要織入橫切邏輯的模塊類在運行期間,通過動態字節碼增強技術,為這些系統模塊類生成相應的子類,而將橫切邏輯加到這些子類中,讓應用程序在執行期間使用從這些動態生成的子類,從而達到將橫切邏輯織入系統的目的。   使用動態字節碼增強技術,即使模塊類沒有實現相應的接口,我們依然可以對其進行擴展,而不用像動態代理那樣受限於接口。不過,這種實現機制依然存在不足,如果需要擴展的類以及類中的實例方法等聲明為final的話,則無法對其進行子類化的擴展。

 

3.4 一個spring aop示例

  上面說了這麼多,下面就來看一個簡單的例子,體會一下aop的魔法吧。如果只是引用了spring-context,那麼還需要引入spring-aspects:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>3.2.18.RELEASE</version>
</dependency>

  這裏我們採用xml配置的方式來開啟aop功能,在resources目錄下添加一個xml配置文件,其中<aop:aspectj-autoproxy/>是用來開啟aop的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop = "http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
     
     <aop:aspectj-autoproxy/>
     
     <bean id = "test" class = "spring.aop.TestAopBean"/>
     <bean class = "spring.aop.AspectJTest"/>
</beans>

  添加Aspect:

@Aspect
public class AspectJTest {

    @Pointcut("execution(* *.test(..))")
    public void test(){
        
    }

    @Before("test()")
    public void beforeTest(){
        System.out.println("beforeTest");
    }
    
    @After("test()")
    public void afterTest(){
        System.out.println("afterTest");
    }

    @Around("test()")
    public Object aroundTest(ProceedingJoinPoint p){
        System.out.println("before1");
        Object o = null;
        try{
            o = p.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
        System.out.println("after1");
        return o;
    }
}

  添加測試類:

public class TestAopBean {

    private String testStr = "testStr";

    public String getTestStr(){
        return testStr;
    }

    public void setTestStr(String testStr){
        this.testStr = testStr;
    }

    public void test(){
        System.out.println("hello test");
    }
    
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("aspectJTest.xml");
        TestAopBean test = (TestAopBean)ctx.getBean("test");
        test.test();
    }
}

  可以看到輸出結果:

before1
beforeTest
hello test
after1
afterTest

  這是一個aop簡單示例,我們寫了一個切面(Pointcut),用來指定Joinpoint的位置在執行test()方法時;同時分別定義了三個Advice(Before、After、Around)用來指定要織入的動作,最後將Pointcut和Advice封裝到一個Aspect中,這樣就完成了橫切邏輯的織入。

 

4. 總結

  在深入學習Spring AOP之前,我們先對AOP的概況進行了介紹,接着一起探索了Spring AOP的實現機制,包括最原始的代理模式,直至最終的動態代理與動態字節碼生成技術。

  • AOP是能夠讓我們在不影響系統原有功能前提下,為軟件系統橫向擴展功能;
  • Spring AOP通過兩種方式實現:JDK動態代理、動態字節碼增強;

  在了解了這些內容之後,我們將繼續深入學習Spring AOP。

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

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

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

日貨輪漏油浩劫清污近完成 重點轉向紅樹林等生態_台中搬家

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

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

摘錄自2020年8月18日中央廣播電台報導

一艘日本散裝貨輪在7月25日觸礁,造成約1000噸燃料油外洩,環保人士稱這是該國史上最嚴重的生態災難,導致許多野生生物死亡並對海域造成破壞。

該小組副組長武市圭司(Keiji Takechi,音譯)在模里西斯東南部城市馬可邦(Mahebourg)的線上記者會上說,「隨著大部分的海上漏油被回收,我們正進入下一階段,重點是清理海岸並最大程度地減少對環境的影響。」他並補充說,「現在需要能提供建議和指導的環境專家。」

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

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

科學家說,漏油事件的後續影響仍持續發酵。先前當模里西斯人民紛紛前往事發當地清除浮油和結塊時,看到海面飄著死亡的鰻魚和魚類,羽毛吸附滿油汙的海鳥吃力地爬上岸。

科學家並稱,本次的生態破壞可能會影響模里西斯及其依賴旅遊產業的經濟數十年。

污染治理
國際新聞
模里西斯
漏油
紅樹林

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

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

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

我終於搞清了啥是 HTTPS 了_台中搬家

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

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

引言

最近上海連續下了一周雨,溫度一夜之間回到解放前,穿夏裝的我被凍得瑟瑟發抖,躲在家裡哪也不想去。

在家百無聊賴的刷着網頁,看到公眾號後台的留言,有同學問我 HTTP 和 HTTPS 有啥區別?

這還用問,當然是 HTTPS 要比 HTTP 更加的安全啊,沒看到後面帶着個 S 呢么,帶着 S 就這麼 NB 。

然後同學的下一個問題把我問懵逼了,為啥帶 S 的更安全呢?能詳細的講講么。

我跟你講嗷,不是我吹,我這麼多年。。。。。。

就沒見過你這麼刨根究底的同學,老問這種我也不是很清楚的問題。

雖然這個問題問的我老臉一紅,但是我有一種不要臉的精神 「我不會,但是我可以學」 。

HTTP

首先先來了解下 HTTP :

HTTP 協議全稱為:Hyper Text Transfer Protocol ,翻譯過來就是超文本傳輸協議,請不要質疑這個翻譯,我專門用百度翻譯翻了一下。

TCP/IP 四層模型應該都知道的,有數據鏈路層,網絡層,傳輸層和應用層:

而 HTTP 協議就是位於 TCP/IP 四層模型的應用層上。

這裏很多人都會混淆 TCP 和 HTTP ,實際上 HTTP 是基於 TCP 連接基礎上的。

簡單的說, TCP 就是單純建立連接,不涉及任何我們需要請求的實際數據,簡單的傳輸。而 HTTP 是用來收發數據,即實際應用上來的。

HTTP 協議通過請求和響應在客戶端和服務端之間收發數據,進行通信:

HTTPS

HTTP 協議看起來好像沒啥問題,唯一的問題就是不夠安全,因為 HTTP 協議的傳輸方式完全是由明文傳輸的,不做任何加密,這就讓一些不懷好意的人有了可乘之機。

這種傳輸方式誘發了一種經典的攻擊方式:中間人攻擊。

對於這種情況,最簡單的我們可以使用加密方案,比如使用 AES 加密,服務端和客戶端先約定一個隨機生成的密鑰 key ,後續的通信中,所有的信息都使用這個密鑰進行 AES 加密:

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

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

這樣雖然後面的通信過程安全了,但是我們在第一發送 AES 密鑰的時候還是存在被中間人攔截的風險,一旦中間人攔截到我們的密鑰,可用對密鑰進行更換或者直接解密請求內容:

這時我們可以使用不對稱加密,來專門對密鑰的傳輸做一次額外的保護。

不對稱加密會有兩個密鑰,一個是公鑰,一個是私鑰。明文可以使用公鑰加密私鑰解密,也可以使用私鑰加密公鑰解密。

現在比較通用的非對稱加密算法有 RSA 。

看到這裏的同學一定在奇怪,既然都使用了不對稱加密,為啥只對 AES 的密鑰做不對稱加密,好像有多此一舉,完全可以對後續所有的通信信息全都使用不對稱加密。

因為不對稱加密相比較對稱加密性能上存在明顯的劣勢,可能你覺得在一個請求中多消耗幾 ms 或者幾 ns 無所謂,但是請求到達服務端是要進行解密,每個請求都多消耗幾 ms 累計起來還是非常可觀的。

上面這個方案看起來已經很安全了,中間人即使攔截到我們的公鑰,由於不知道我們的私鑰貌似也沒辦法解密。

實際上中間人完全不需要解密我們的信息,他可以生成一對新的公私鑰發送給客戶端進行攻擊,後續客戶端的通信中間人使用自己創造的私鑰進行解密,然後通過服務端生成的公鑰進行加密返回給服務端:

CA 證書

上面的問題我們僅通過客戶端和服務端已經沒辦法了,這時候需要引入新的第三方機構,一個頒發 CA 證書的機構。

常見的第三方 CA 機構有:Symantec(賽門鐵克),Comodo(科莫多),GeoTrust(環度網信),GoDaddy,Thawte,daoRapidSSL 等等。

在中間人攻擊中,我們遇到的問題不是加密算法不夠神奇,不是密鑰方式不夠嚴謹,而是我們沒有辦法向我們的客戶端表明我們給他的公鑰是我們的,是不是很像我沒辦法證明我是我的問題。

所以第三方機構應運而生,第三方機構只做一件事情,將服務端的公鑰刻上了我們的名字(CA 證書),客戶端接收到公鑰之後,只需要來第三方機構這裏查詢,就能知道這個公鑰是不是真的服務器,然後再將自己生成的 AES 密鑰使用 CA 證書中解密得到的公鑰進行加密后發送給服務端。

最後服務端使用私鑰解密得到 AES 密鑰,就可以愉快的和客戶端進行通信了。

最後的最後,CA 機構驗證不是每次都要去 CA 機構查詢。這樣做太傻了而且太耗時,尤其是很多 CA 機構的服務都在海外,這樣一來一去消耗的時間太多了。

CA 機構高明的地方就在於,我們去找它註冊公鑰,它會使用另一個來註冊的公司的私鑰對我們的公鑰加密,得到一個我們的公鑰的指紋(全球唯一),然後將這家公司的公鑰信息(其實也是證書)和我們的公鑰以及我們公鑰的指紋打包成一個證書。

當我們使用 HTTPS 將證書下發給客戶端校驗時,客戶端(比如瀏覽器)從證書中看到了上級證書的信息,恰巧這個證書就在瀏覽器(或者本機)中,已經被驗證過是合法的,瀏覽器只要使用這個證書中的公鑰將我們的公鑰指紋進行解密,然後比對我們的公鑰信息就知道我們也是的合法的。因為假證書中的公鑰簽名不可能被合法的上級證書中公鑰解密 。

這段稍微有點繞,慢慢看多看幾次就理解了。

參考

https://www.jianshu.com/p/691b8ba3a70f

https://blog.csdn.net/u010144805/article/details/80803059

https://blog.csdn.net/caofengtao1314/article/details/87912078

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

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

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

真實世界的蒙娜麗莎?藝術家用AI技術「重現」經典名畫 體現現實與想像的差距_台中搬家

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

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

先前曾使用AI StyleGAN技術,完美呈現「真實版」經典動畫角色的美國藝術家Nathan Shipley,最近又出招了!他這次同樣是透過AI StyleGAN技術來進行創作,竟然將一些經典的人物肖像畫做出真實版的模樣,其中包含大家耳熟能詳的蒙娜麗莎。

↑李奧納多·達文西所繪製的《蒙娜麗莎》。

在vfx和動態圖形領域有著豐富經驗的Nathan,先前就曾使用AI StyleGAN技術,透過Pixel2Style2Pixel(pSp)技術轉換框架,將許多動漫作品中知名的經典角色成功「真人化」,其中包含了皮克斯電影《超人特攻隊》與《天外奇蹟》中的角色,由於他所做出來的成果實在太過逼真,在國外引起不小的討論。

 

而他近日再度嘗試透過AI StyleGAN技術,將許多經典的人物肖像畫進行「複刻」,其中包含了大家耳熟能詳、由李奧納多·達文西所繪製的《蒙娜麗莎》,還有美國國父喬治·華盛頓、墨西哥女畫家芙烈達·卡蘿(Frida Kahlo)、有著血腥瑪莉之稱的瑪麗皇后一世(Mary I of England)、有著賢明女王之稱的伊莉莎白一世(Queen Elizabeth I)等等知名的人物。其他人物真的都覺得還好,只是那個蒙娜麗莎….真的跟想像中的出入頗大啊!

↑賢明女王之稱的伊莉莎白一世(Queen Elizabeth I)。

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

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

 

↑美國國父喬治·華盛頓。

 

↑瑪麗皇后一世(Mary I of England)。

 

↑墨西哥女畫家芙烈達·卡蘿(Frida Kahlo)、

您也許會喜歡:

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

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

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

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

過濾器 和 攔截器 6個區別,別再傻傻分不清了_台中搬家

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

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

本文收錄在個人博客:www.chengxy-nds.top,技術資料共享,同進步

周末有個小夥伴加我微信,向我請教了一個問題:老哥,過濾器 (Filter) 和 攔截器 (Interceptor) 有啥區別啊? 聽到題目我的第一感覺就是:簡單

畢竟這兩種工具開發中用到的頻率都相當高,應用起來也是比較簡單的,可當我準備回復他的時候,竟然不知道從哪說起,支支吾吾了半天,場面炒雞尷尬有木有,工作這麼久一個基礎問題答成這樣,丟了大人了。

平時覺得簡單的知識點,但通常都不會太關注細節,一旦被別人問起來,反倒說不出個所以然來。

歸根結底,還是對這些知識了解的不夠,一直停留在會用的階段,以至於現在一看就會一說就廢!這是典型基礎不紮實的表現,哎·~,其實我也就是個虛胖!

知恥而後勇,下邊結合實踐,更直觀的來感受一下兩者到底有什麼不同?

準備環境

我們在項目中同時配置 攔截器過濾器

1、過濾器 (Filter)

過濾器的配置比較簡單,直接實現Filter 接口即可,也可以通過@WebFilter註解實現對特定URL攔截,看到Filter 接口中定義了三個方法。

  • init() :該方法在容器啟動初始化過濾器時被調用,它在 Filter 的整個生命周期只會被調用一次。注意:這個方法必須執行成功,否則過濾器會不起作用。

  • doFilter() :容器中的每一次請求都會調用該方法, FilterChain 用來調用下一個過濾器 Filter

  • destroy(): 當容器銷毀 過濾器實例時調用該方法,一般在方法中銷毀或關閉資源,在過濾器 Filter 的整個生命周期也只會被調用一次

@Component
public class MyFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("Filter 前置");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 處理中");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println("Filter 後置");
    }
}

2、攔截器 (Interceptor)

攔截器它是鏈式調用,一個應用中可以同時存在多個攔截器Interceptor, 一個請求也可以觸發多個攔截器 ,而每個攔截器的調用會依據它的聲明順序依次執行。

首先編寫一個簡單的攔截器處理類,請求的攔截是通過HandlerInterceptor 來實現,看到HandlerInterceptor 接口中也定義了三個方法。

  • preHandle() :這個方法將在請求處理之前進行調用。注意:如果該方法的返回值為false ,將視為當前請求結束,不僅自身的攔截器會失效,還會導致其他的攔截器也不再執行。

  • postHandle():只有在 preHandle() 方法返回值為true 時才會執行。會在Controller 中的方法調用之後,DispatcherServlet 返回渲染視圖之前被調用。 有意思的是postHandle() 方法被調用的順序跟 preHandle() 是相反的,先聲明的攔截器 preHandle() 方法先執行,而postHandle()方法反而會後執行。

  • afterCompletion():只有在 preHandle() 方法返回值為true 時才會執行。在整個請求結束之後, DispatcherServlet 渲染了對應的視圖之後執行。

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("Interceptor 前置");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("Interceptor 處理中");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("Interceptor 後置");
    }
}

將自定義好的攔截器處理類進行註冊,並通過addPathPatternsexcludePathPatterns等屬性設置需要攔截或需要排除的 URL

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
    }
}

我們不一樣

過濾器 和 攔截器 均體現了AOP的編程思想,都可以實現諸如日誌記錄、登錄鑒權等功能,但二者的不同點也是比較多的,接下來一一說明。

1、實現原理不同

過濾器和攔截器 底層實現方式大不相同,過濾器 是基於函數回調的,攔截器 則是基於Java的反射機制(動態代理)實現的。

這裏重點說下過濾器!

在我們自定義的過濾器中都會實現一個 doFilter()方法,這個方法有一個FilterChain 參數,而實際上它是一個回調接口。ApplicationFilterChain是它的實現類, 這個實現類內部也有一個 doFilter() 方法就是回調方法。

public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

ApplicationFilterChain裏面能拿到我們自定義的xxxFilter類,在其內部回調方法doFilter()里調用各個自定義xxxFilter過濾器,並執行 doFilter() 方法。

public final class ApplicationFilterChain implements FilterChain {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
            ...//省略
            internalDoFilter(request,response);
    }
 
    private void internalDoFilter(ServletRequest request, ServletResponse response){
    if (pos < n) {
            //獲取第pos個filter    
            ApplicationFilterConfig filterConfig = filters[pos++];        
            Filter filter = filterConfig.getFilter();
            ...
            filter.doFilter(request, response, this);
        }
    }
 
}

而每個xxxFilter 會先執行自身的 doFilter() 過濾邏輯,最後在執行結束前會執行filterChain.doFilter(servletRequest, servletResponse),也就是回調ApplicationFilterChaindoFilter() 方法,以此循環執行實現函數回調。

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }

2、使用範圍不同

我們看到過濾器 實現的是 javax.servlet.Filter 接口,而這個接口是在Servlet規範中定義的,也就是說過濾器Filter 的使用要依賴於Tomcat等容器,導致它只能在web程序中使用。

而攔截器(Interceptor) 它是一個Spring組件,並由Spring容器管理,並不依賴Tomcat等容器,是可以單獨使用的。不僅能應用在web程序中,也可以用於ApplicationSwing等程序中。

3、觸發時機不同

過濾器攔截器的觸發時機也不同,我們看下邊這張圖。

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

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

過濾器Filter是在請求進入容器后,但在進入servlet之前進行預處理,請求結束是在servlet處理完以後。

攔截器 Interceptor 是在請求進入servlet后,在進入Controller之前進行預處理的,Controller 中渲染了對應的視圖之後請求結束。

4、攔截的請求範圍不同

在上邊我們已經同時配置了過濾器和攔截器,再建一個Controller接收請求測試一下。

@Controller
@RequestMapping()
public class Test {

    @RequestMapping("/test1")
    @ResponseBody
    public String test1(String a) {
        System.out.println("我是controller");
        return null;
    }
}

項目啟動過程中發現,過濾器的init()方法,隨着容器的啟動進行了初始化。

此時瀏覽器發送請求,F12 看到居然有兩個請求,一個是我們自定義的 Controller 請求,另一個是訪問靜態圖標資源的請求。

看到控制台的打印日誌如下:

執行順序 :Filter 處理中 -> Interceptor 前置 -> 我是controller -> Interceptor 處理中 -> Interceptor 處理后

Filter 處理中
Interceptor 前置
Interceptor 處理中
Interceptor 後置
Filter 處理中

過濾器Filter執行了兩次,攔截器Interceptor只執行了一次。這是因為過濾器幾乎可以對所有進入容器的請求起作用,而攔截器只會對Controller中請求或訪問static目錄下的資源請求起作用。

5、注入Bean情況不同

在實際的業務場景中,應用到過濾器或攔截器,為處理業務邏輯難免會引入一些service服務。

下邊我們分別在過濾器和攔截器中都注入service,看看有什麼不同?

@Component
public class TestServiceImpl implements TestService {

    @Override
    public void a() {
        System.out.println("我是方法A");
    }
}

過濾器中注入service,發起請求測試一下 ,日誌正常打印出“我是方法A”

@Autowired
    private TestService testService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 處理中");
        testService.a();
        filterChain.doFilter(servletRequest, servletResponse);
    }
Filter 處理中
我是方法A
Interceptor 前置
我是controller
Interceptor 處理中
Interceptor 後置

在攔截器中注入service,發起請求測試一下 ,竟然TM的報錯了,debug跟一下發現注入的service怎麼是Null啊?

這是因為加載順序導致的問題,攔截器加載的時間點在springcontext之前,而Bean又是由spring進行管理。

攔截器:老子今天要進洞房;
Spring:兄弟別鬧,你媳婦我還沒生出來呢!

解決方案也很簡單,我們在註冊攔截器之前,先將Interceptor 手動進行注入。注意:在registry.addInterceptor()註冊的是getMyInterceptor() 實例。

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
        System.out.println("注入了MyInterceptor");
        return new MyInterceptor();
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
    }
}

6、控制執行順序不同

實際開發過程中,會出現多個過濾器或攔截器同時存在的情況,不過,有時我們希望某個過濾器或攔截器能優先執行,就涉及到它們的執行順序。

過濾器用@Order註解控制執行順序,通過@Order控制過濾器的級別,值越小級別越高越先執行。

@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyFilter2 implements Filter {

攔截器默認的執行順序,就是它的註冊順序,也可以通過Order手動設置控制,值越小越先執行。

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
    }

看到輸出結果發現,先聲明的攔截器 preHandle() 方法先執行,而postHandle()方法反而會後執行。

postHandle() 方法被調用的順序跟 preHandle() 居然是相反的!如果實際開發中嚴格要求執行順序,那就需要特別注意這一點。

Interceptor1 前置
Interceptor2 前置
Interceptor 前置
我是controller
Interceptor 處理中
Interceptor2 處理中
Interceptor1 處理中
Interceptor 後置
Interceptor2 處理后
Interceptor1 處理后

那為什麼會這樣呢? 得到答案就只能看源碼了,我們要知道controller 中所有的請求都要經過核心組件DispatcherServlet路由,都會執行它的 doDispatch() 方法,而攔截器postHandle()preHandle()方法便是在其中調用的。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
        try {
         ...........
            try {
           
                // 獲取可以執行當前Handler的適配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                // 注意: 執行Interceptor中PreHandle()方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 注意:執行Handle【包括我們的業務邏輯,當拋出異常時會被Try、catch到】
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);

                // 注意:執行Interceptor中PostHandle 方法【拋出異常時無法執行】
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
        }
        ...........
    }

看看兩個方法applyPreHandle()applyPostHandle()具體是如何被調用的,就明白為什麼postHandle()preHandle() 執行順序是相反的了。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if(!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

發現兩個方法中在調用攔截器數組 HandlerInterceptor[] 時,循環的順序竟然是相反的。。。,導致postHandle()preHandle() 方法執行的順序相反。

總結

我相信大部分人都能熟練使用濾器和攔截器,但兩者的差別還是需要多了解下,不然開發中使用不當,時不時就會出現奇奇怪怪的問題,以上內容比較簡單,新手學習老鳥複習,有遺漏的地方還望大家积極補充,如有理解錯誤之處,還望不吝賜教。

原創不易,燃燒秀髮輸出內容

整理了幾百本各類技術电子書, 送給小夥伴們, 我的同名公眾號自行領取。和一些小夥伴們建了一個技術交流群,一起探討技術、分享技術資料,旨在共同學習進步,如果感興趣就加入我們吧!

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

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

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

多款旗艦手機電力續航能力實測,三星 Galaxy S21 Ultra 仍敗給 iPhone 12 Pro Max_台中搬家

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

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

上週我們從分享過有國外 YouTube 頻道實測五款旗艦手機的續航表現,這次則來自另一個 YouTube 頻道頻道挑選包括 2020 年底至今的五款代表性旗艦級手機的電力續航能力測試,這次的測試機型名單包括 Apple iPhone 12 Pro Max、華為 Mate40 Pro、小米11、 Samsung Galaxy S21 Ultra 以及 Galaxy Note20 Ultra ,在看之前大家不妨猜猜看名次吧!

▲圖片來源:TechNick(YouTube)

多款旗艦手機電力續航能力實測,三星 Galaxy S21 Ultra 仍敗給 iPhone 12 Pro Max

日前三星 Galaxy S21 系列終於發表,其中最大尺寸的 Galaxy S21 Ultra 搭載 Qualcomm Snapdragon 888/Exynos 2100 處理器、內建 5000mAh 大電量,不過實測續能力究竟如何呢?這次 YouTube 頻道 TechNick 找來包括三星 Galaxy S21 Ultra、Galaxy Note20 Ultra、小米11、華為 Mate40 Pro 以及 Apple iPhone 12 Pro Max 這些從 2020 下半年至 2021 年目前代表性的旗艦手機。
在開始之前,先簡述一下這次五款參與測試的手機「背景」。首先,Galaxy S21 Ultra 內建 5000mAh 電池並挑選台灣上市、續航表現相對更長的 Snapdragon 888 版本, Galaxy Note20 Ultra 則為搭載 Snapdragon 865+ 處理器並內建 4500mAh 電池、Mate40 Pro 搭載 Kirin 9000並內建 4400mAh 電池、 iPhone 12 Pro Max 搭載 A14 仿生處理器和 3687mAh 電池。
這次測試將五款手機皆設定為 60Hz 螢幕更新率,而測試出來的結果也相當值得期待。

為了確保電力續航能力測試的公平,在測試開始前除了先將電量充滿,也將所有手機設定為相同的亮度並測量溫度:
▲圖片來源:TechNick(YouTube)

這次使用不同的應用程式目標將從 100% 電量消耗至 0% ,除了比較手機實際電量耗盡的時間,最後也會換算若在5000mAh 電池為基準的電力續航排行。

▲圖片來源:TechNick(YouTube)

在這五款測試的手機中,小米11 的電力率先耗盡,時間為 6 小時 23 分鐘:

▲圖片來源:TechNick(YouTube)

最終電量測試結果,續航能力排行依序為華為 Mate40 Pro(第一名:9 小時 06 分鐘)、Apple iPhone 12 Pro Max(第二名:9 小時 06 分鐘)、三星 Galaxy S21 Ultra(第三名:9 小時 02 分)、Galaxy Note20 Ultra(第四名:7 小時 04 分)以及小米11(第五名:6 小時 23 分),其中 iPhone 12 Pro Max 以些微差距比 Mate40 Pro 電量更早消耗殆盡。
若以每分鐘消耗的電量計算,並換算為 5000mAh 電池容量的話,iPhone 12 Pro Max 電力續航約有 12 小時 20 分(第一名)、華為 Mate40 Pro 續航約有 10 小時 21 分(第二名)、三星 Galaxy S21 Ultra 續航約 9 小時 02 分(第三名)、Galaxy Note20 Ultra 續航約 7 小時 51 分(第四名)、小米11 續航約 6 小時 56 分(第五名)。

▲圖片來源:TechNick(YouTube)

最後, TechNick 也附上其頻道目前測試過的所有手機的電力續航能力排行榜如下表:

▲圖片來源:TechNick(YouTube)

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

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

完整影片

 

延伸閱讀:
國外 YouTube 頻道實測五款旗艦手機續航力表現,iPhone 12 Pro Max 勝過 S21 Ultra 與小米 11

小米隔空充電技術正式發表:手持也能隔空充電,「真」無線充電時代來臨!

您也許會喜歡:

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

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

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

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

關於C#委託三種調用的分享_台中搬家公司

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

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

一、同步調用

1、同步調用會按照代碼順序來執行
2、同步調用會阻塞線程,如果是要調用一項繁重的工作(如大量IO操作),可能會讓程序停頓很長時間,造成糟糕的用戶體驗,這時候異步調用就很有必要了。

舉個栗子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;

namespace Test
{
    public delegate int AddHandler(int a, int b);
    public class Calc
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("開始計算:" + a + "+" + b);
            Thread.Sleep(3000); //模擬該方法運行三秒
            Console.WriteLine("計算完成!");
            return a + b;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
              Console.WriteLine("===== 同步調用 SyncInvokeTest =====");
            AddHandler handler = new AddHandler(Calc.Add);
            int result = handler.Invoke(1, 2);
            Console.WriteLine("繼續做別的事情。。。");
            Console.WriteLine(result);
            Console.ReadKey();
        }    
    }
}

* 問:為什麼Invoke的參數和返回值和AddHandler委託是一樣的呢?
* 答:Invoke方法的參數很簡單,一個委託,一個參數表(可選),
 而Invoke方法的主要功能就是幫助你在UI線程上調用委託所指定的方法。
Invoke方法首先檢查發出調用的線程(即當前線程)是不是UI線程,
 如果是,直接執行委託指向的方法,如果不是,它將切換到UI線程,
 然後執行委託指向的方法。不管當前線程是不是UI線程,
 Invoke都阻塞直到委託指向的方法執行完畢,然後切換回發出調用的
 線程(如果需要的話),返回。
 所以Invoke方法的參數和返回值和調用他的委託應該是一致的。

二、異步調用

1、異步調用不阻塞線程,而是把調用塞到線程池中,
2、程序主線程或UI線程可以繼續執行。
3、委託的異步調用通過BeginInvoke和EndInvoke來實現。

舉個栗子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;

namespace Test
{
    public delegate int AddHandler(int a, int b);
    public class Calc
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("開始計算:" + a + "+" + b);
            Thread.Sleep(3000); //模擬該方法運行三秒
            Console.WriteLine("計算完成!");
            return a + b;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 異步調用 AsyncInvokeTest =====");
            AddHandler handler1 = new AddHandler(Calc.Add);
            //IAsyncResult: 異步操作接口(interface)
            //BeginInvoke: 委託(delegate)的一個異步方法的開始
            IAsyncResult result1 = handler1.BeginInvoke(1, 2, null, null);
 
            Console.WriteLine("繼續做別的事情1。。。");
            //異步操作返回
            Console.WriteLine(handler1.EndInvoke(result1));//會等待加法類計算,如果沒計算好就堵塞線程
            Console.WriteLine("繼續做別的事情2。。。");
            Console.ReadKey();
        }    
    }
}

注意:
* BeginInvoke : 開始一個異步的請求,調用線程池中一個線程來執行,
* 返回IAsyncResult 對象(異步的核心). IAsyncResult 簡單的說,
* 它存儲異步操作的狀態信息的一個接口,也可以用他來結束當前異步。
* 注意: BeginInvoke和EndInvoke必須成對調用.即使不需要返回值,
* 但EndInvoke還是必須調用,否則可能會造成內存泄漏。

結果:

可以看到,主線程並沒有等待,而是直接向下運行了。
但是問題依然存在,當主線程運行到EndInvoke時,如果這時調用沒有結束(這種情況很可能出現),這時為了等待調用結果,線程依舊會被阻塞。

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

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

三、異步回調

用回調函數,當調用結束時會自動調用回調函數,解決了為等待調用結果,而讓線程依舊被阻塞的局面。

 舉個栗子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;

namespace Test
{
    public delegate int AddHandler(int a, int b);
    public class Calc
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("開始計算:" + a + "+" + b);
            Thread.Sleep(3000); //模擬該方法運行三秒
            Console.WriteLine("計算完成!");
            return a + b;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
         Console.WriteLine("===== 異步回調 AsyncInvokeTest =====");
            AddHandler handler2 = new AddHandler(Calc.Add);
            //異步操作接口(注意BeginInvoke方法的不同!)
            IAsyncResult result2 = handler2.BeginInvoke(1, 2, new AsyncCallback(MyCallBack),
                "AsycState:OK");
            Console.WriteLine("繼續做別的事情。。。");
            Console.ReadKey();
        }    
        static void MyCallBack(IAsyncResult result)
        {
            //result 是“加法類.Add()方法”的返回值
            //AsyncResult 是IAsyncResult接口的一個實現類,空間:System.Runtime.Remoting.Messaging
            //AsyncDelegate 屬性可以強制轉換為用戶定義的委託的實際類。
            AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;
            Console.WriteLine(handler.EndInvoke(result));
            Console.WriteLine(result.AsyncState);
        }      
    }
}

委託的類型為AddHandler,則為了訪問 AddHandler.EndInvoke,

result 是“加法calc.Add()方法”的返回值

AsyncResult 是IAsyncResult接口的一個實現類,空間:System.Runtime.Remoting.Messaging

必須將異步委託強制轉換為 AddHandler。可以在異步回調函數(類型為 AsyncCallback)中調用 AddHandler.EndInvoke,以獲取最初提交的 AddHandler.BeginInvoke 的結果。

 

ok,三種委託調用的分享就到這裏了,有疑問的歡迎指正!

 

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

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

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

國外大神爆料第二代 AirPods Pro 最新渲染圖,採無柄設計、更好的音質_台中搬家

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

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

隨著供應鏈傳出 AirPods Pro 2 第二代很可能就快要推出的消息,最近國外爆料大神就透露了一張疑似介紹 AirPods Pro 2 的渲染圖,雖然沒有規格資訊,但卻顯示一個非常重要的全新「無柄」設計,變成只有上面那個耳機頭而已。

國外大神爆料第二代 AirPods Pro 最新渲染圖

稍早國外爆料大神 theapplehub 分享下面這張圖片,可以看到名稱寫著 AirPods Pro (2nd gen),下面標註三個特色重點「全心設計 – 無柄、更加圓潤」、「H1 晶片」與「提升音質」,左側則有 AirPods Pro 2 的渲染圖:

The next generation AirPods Pro are expected to feature a new design and could launch as early as March/April pic.twitter.com/GaDJtpduai

— Apple Hub (@theapplehub) February 2, 2021

theapplehub 也表示這款 AirPods Pro 2 預計會在今年三月或四月推出,先前 DigiTimes 報告僅指出將於上半年發表,並沒有註明哪一個月。另外晶片部分除了 H1,也有傳可能會搭載 W2 晶片。

現行的 AirPods 2、AirPods Pro 與 AirPods Max 都是搭載 H1 晶片,W2 則是 AirPods 第一代 W1 晶片的下一代版本。

Mr-White 分享的新款 AirPods Pro 零件圖片:

New AirPods Pro Mabey Two Sizes Still W2 Chips 🤨 pic.twitter.com/R5MpzUrUlg

— Mr·White (@laobaiTD) December 29, 2020

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

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

如果光看渲染圖,改成無柄設計後外型總覺得不是很討喜,而且戴起來感覺也沒有很舒適,大多數國外網友的評價也普普,很多人都覺得為什麼要取消耳機柄?

不過這也不是第一次發生,想當初 AirPods 剛推出時,一堆人不斷嘲笑那水管造型,結果開賣之後依舊被賣翻,所以還是要等到最終實品才知道,搞不好 Apple 把這造型設計的非常美型。

無論如何,離預告的時間只剩下兩個月左右時間,沒意外應該陸續有更多消息出現,最近有打算入手 AirPods 或 AirPods Pro 也不妨等一下。

至於價格,AirPods Pro 2 有可能會保持一樣的 249 美元,並不會調高或降價。

資料來源:theapplehub

AirPods Pro 2 零件現蹤,可能將像 Apple Watch 一樣有雙尺寸?

您也許會喜歡:

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

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

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

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