關於 Container ,Injection
1.容器的歷史
容器概念始於 1979 年提出的 UNIX chroot,它是一個 UNIX 操作系統的系統調用,將一個進程及其子進程的根目錄改變到文件系統中的一個新位置,讓這些進程只能訪問到這個新的位置,從而達到了進程隔離的目的。
2000 年的時候 FreeBSD 開發了一個類似於 chroot 的容器技術 Jails,這是最早期,也是功能最多的容器技術。Jails 英譯過來是監獄的意思,這個“監獄”(用沙盒更為準確)包含了文件系統、用戶、網絡、進程等的隔離。
2001 Linux 也發布自己的容器技術 Linux VServer,2004 Solaris 也發布了 Solaris Containers,兩者都將資源進行劃分,形成一個個 zones,又叫做虛擬服務器。
2005 年推出 OpenVZ,它通過對 Linux 內核進行補丁來提供虛擬化的支持,每個 OpenVZ 容器完整支持了文件系統、用戶及用戶組、進程、網絡、設備和 IPC 對象的隔離。
2007 年 Google 實現了 Control Groups( cgroups ),並加入到 Linux 內核中,這是劃時代的,為後期容器的資源配額提供了技術保障。
2008 年基於 cgroups 和 linux namespace 推出了第一個最為完善的 Linux 容器 LXC。
2013 年推出到現在為止最為流行和使用最廣泛的容器 Docker,相比其他早期的容器技術,Docker 引入了一整套容器管理的生態系統,包括分層的鏡像模型,容器註冊庫,友好的 Rest API。
2014 年 CoreOS 也推出了一個類似於 Docker 的容器 Rocket,CoreOS 一個更加輕量級的 Linux 操作系統,在安全性上比 Docker 更嚴格。
2016 年微軟也在 Windows 上提供了容器的支持,Docker 可以以原生方式運行在 Windows 上,而不是需要使用 Linux 虛擬機。
基本上到這個時間節點,容器技術就已經很成熟了,再往後就是容器雲的發展,由此也衍生出多種容器雲的平臺管理技術,其中以 kubernetes 最為出眾,有了這樣一些細粒度的容器集群管理技術,也為微服務的發展奠定了基石。因此,對於未來來說,應用的微服務化是一個較大的趨勢。
為什麽需要容器
其一,這是技術演進的一種創新結果,其二,這是人們追求高效生產活動的一種工具。
隨著軟件開發的發展,相比於早期的集中式應用部署方式,現在的應用基本都是采用分布式的部署方式,一個應用可能包含多種服務或多個模塊,因此多種服務可能部署在多種環境中,如虛擬服務器、公有雲、私有雲等,由於多種服務之間存在一些依賴關系,所以可能存在應用在運行過程中的動態遷移問題,那這時如何保證不同服務在不同環境中都能平滑的適配,不需要根據環境的不同而去進行相應的定制,就顯得尤為重要。
就像貨物的運輸問題一樣,如何將不同的貨物放在不同的運輸機器上,減少因貨物的不同而頻繁進行貨物的裝載和卸載,浪費大量的人力物力。
為此人們發明了集裝箱,將貨物根據尺寸形狀等的不同,用不同規格的集裝箱裝載,然後再放到運輸機上運輸,由於集裝箱密封,只有貨物到達目的地才需拆封,在運輸過程能夠再不同運輸機上平滑過渡,所以避免了資源的浪費。
Injection
javava EE CDI主要使用@Inject批註,以便將托管bean的依賴註入執行到其他容器托管資源。
構造函數依賴註入
構造函數依賴註入公共類SomeBean { 私人最終服務; @註入 public SomeBean(服務服務){ this.service = service; } }
當CDI容器實例化SomeBean類型的bean時,它將查找默認(無參數)構造函數並使用它來創建bean實例。這個規則的例外是當我們有另一個用@Inject註釋的構造函數時。如果是這種情況,容器將使用帶註釋的構造函數,並將註入作為構造函數參數傳遞的依賴項。
在上面的示例中,它將獲取一個Service實例並註入SomeBean帶註釋的構造函數。
註意:請記住,它可能只存在一個單一的與@Inject註釋構造函數場依賴註入
場依賴註入公共類SomeBean { @註入 私人服務; }
在這種情況下,當容器初始化類型為SomeBean的bean時,它會將正確的Service bean註入到字段中,即使它是私有的,也不需要任何setter方法。
Initializer方法依賴註入
Initializer方法依賴註入公共類SomeBean { 私人服務; @註入 public void setService(服務服務){ this.service = service; } }
在這種情況下,當容器初始化SomeBean類型的bean時,它將調用所有使用@Inject註釋的方法,並將依賴項註入方法參數。
@Any資格賽
為了提供完全松散耦合的應用程序,我們通常將接口註入托管資源。如果我們為給定的接口提供多個bean實現怎麽辦?我們可以使用@Any限定符以及CDI 實例接口將它們全部註入到托管bean中:
@Any資格賽公共類SomeBean { @註入 public void listServiceImplementations( @Any Instance <Service> serviceList){ for(服務服務:serviceList){ 的System.out.println(service.getClass()另一方面,getCanonicalName()); } } }
該@Any預選賽指示,該註射點可以通過任何可用的依賴性得到滿足的容器,因此容器註入他們。如果我們有多個接口實現並且我們只註入一個 - 沒有任何消除歧義 - 容器會抱怨並且無法初始化組件。我們將在其他教程中看到依賴消歧。
註入生產者方法
生產者方法參數也可以由CDI容器註入。請參閱Java EE CDI Producer方法教程。
CDI代理
本教程將不完整,我們也沒有涵蓋CDI代理機制。當我們註入被以不同於一個範圍內創建一個托管bean @Dependent -到另一個托管的資源- CDI容器也沒有註入直接引用註入豆。
對於CDI bean範圍,請參閱Java EE CDI bean範圍。
為什麽CDI使用代理?因為如果註入直接bean引用,就會產生線程安全或對托管bean的並發訪問等問題。
想象一下,會話範圍的bean被註入到應用程序範圍的bean中。由於應用程序作用域bean在所有客戶端之間共享,如果多個客戶端同時訪問應用程序作用域bean,則存在一個客戶端訪問另一個客戶端直接引用的會話作用域bean的高風險。
要解決此問題,CDI會創建代理並將代理註入註入點。然後,代理將處理對註入的bean的調用,並將調用轉發給正確的bean實例。
CDI創建的代理擴展了註入bean的類。想象一下以下場景:
應用程序和會話範圍的bean@SessionScoped 公共課堂服務{ public void doWork(){ 的System.out.println( “工作......”); } } @ApplicationScoped 公共類SomeBean { @註入 私人服務; public void test(){ service.doWork(); } }
CDI會將會話作用域bean的代理註入到應用程序作用域bean中。對會話範圍bean的每次調用都將通過代理,代理又將調用重定向到正確的會話bean實例:屬於當前HTTP請求會話的實例。
CDI通過擴展bean類並覆蓋所有非私有方法來創建代理。代理的代表性說明可能如下:
說明性的CDI代理公共類服務$ Proxy $ _ $$ _ WeldClientProxy 擴展服務{ @覆蓋 public void doWork(){ Service instance = // ...解析bean實例 instance.doWork(); } }
關於 Container ,Injection