Enterprise JavaBean

Enterprise JavaBean

Enterprise JavaBean (EJB)是J2EE的一部分,定義了一個用於開發基於組件的企業多重應用程式的標準。其特點包括網路服務支持和核心開發工具(SDK)。

在J2EE里,Enterprise Java Beans(EJB)稱為Java 企業柄,是Java的核心代碼,分為整體柄和片段柄和訊息柄三個部分,其中的訊息柄將在以後再作討論。現在我們來看看什麼是整體柄和片段柄。

整體柄是一種對象: 標準Java對象由創建它的程式創建,當程式終止時,對象也隨之丟失,這就意味著當再次運行些程式時,將無法找到先前創建的柄,而整體柄會一直存在著直到它被刪除。 一個程式可以創建一個整體柄,並且這個程式可以在被保存後隨時停止和重啟。整體柄將會依然存在。重啟後,程式可以找到與之相對應的整體柄,並且會繼續使用這個整體柄。

EJB實際上是SUN的J2EE中的一套規範,並且規定了一系列的API用來實現把EJB概念轉換成EJB產品.EJB是BEANS,BEANS是什麼概念,那就是得有一個容納她,讓她可勁造騰的地方,就是得有容器.EJB必須生存在EJB容器中.這個容器可是功能強大之極!她首先要包裝你BEAN,EJB的客戶程式實際上從來就不和你編寫的EJB直接打交道,他們之間是通過HOME/REMOTE接口來發生關係的.它負責你的BEAN的所有的吃喝拉薩睡,比如BEAN的持續化,安全性,事務管理...

一.什麼是 EJB?

一個技術規範:EJB 從技術上而言不是一種"產品"
EJB 是一種標準描述了構建套用組件要解決的:
可擴展 (Scalable)
分散式 (Distributed)
事務處理 (Transactional)
數據存儲 (Persistent)
安全性 (Secure)

二.Sun 對 EJB 的期望

提供一個標準的分布的、基於 OO 的組件架構
禁止複雜的系統級功能需求
Write once, run anywhere
與非 Java 套用之間的互操作能力
兼容 CORBA 標準

三.為什麼選擇 EJB?

EJB 伺服器完成"繁雜"的工作:套用開發人員關注於業務邏輯的實現而不是底層的實現機制(似於 4GL 語言設計的目標)
支持事務處理
多個業務操作同時成功,或全部失敗
可以通過在代碼外的描述來定義事務處理級別
可擴展性
EJB 可以根據您套用的增長而擴展
EJB 伺服器往往還提供了負載均衡和
安全性:由 EJB 伺服器提供資源的訪問許可權控制

四.EJB 架構

為了滿足架構的目標,規範中描述了
伺服器 (Server)
容器 (Container)
類 (Class) 和實例 (Instance)
Home 和 Remote 接口
客戶端 (Client)

五. 簡化的編程模型

關注於業務邏輯實現:EJB 負責生命周期 (lifecycle), 數據存儲 (persistence), 事務處理語義 (transactional semantic), 安全(security), ...
通用的編程模型:各種服務的高層 API
Java 是其程式語言

1.EJB 特點

由一個 EJB 容器在運行時創建和管理 EJB
在部署 EJB 時定製其運行方式
由 EJB 容器和伺服器來協調客戶端的訪問
可以部署到任何兼容的 EJB 容器中
客戶端對 EJB 的視圖是由 Bean 開發人員決定的

2.EJB 伺服器

管理 EJB 容器 (它管理%E4%BD%9C%E7%B3%BB%E7%B5%B1 target="_new">作業系統服務的存取
提供 Java 相關的服務,尤其是
通過 JNDI 訪問命名空間
基於 OTS 的事務處理服務

3.EJB 容器

管理 Bean 生命周期:將 EJB 伺服器提供的服務傳遞給 Bean
生成代碼來實現對 Bean 的存取訪問
強制事務處理的限制
創建、初始化和回收 Bean
管理持久數據的存儲
對客戶端而言 EJB 容器是透明的

4.在一個 EJB 伺服器中的容器

目前容器通常是由 EJB 伺服器本身提供的
在 EJB 1.0 或 1.1 規範中沒有定義容器-到-伺服器的接口
各廠商可以根據他們的見解來實現伺服器和容器的各自責任

5.容器提供服務: 數據存儲

容器決定何時載入/儲存狀態
Container-Managed Persistence(容器管理存儲/CMP)
容器負責存儲您的 Bean
容器生成必要的類和代碼
Bean-Managed Persistence(Bean 管理存儲/BMP)
Bean 開發人員提供存儲代碼
開發人員決定 如何存儲, 容器仍然決定 何時進行

6.容器提供服務: 事務處理

可以由容器代理來實現
容器將得到業務邏輯方法的事務處理需求
容器提供事務控制代碼
也可以由程式設計師通過代碼實現

7.容器提供服務: 其它服務

其它服務包括
命名 (Naming)
安全 (Security)
執行緒管理 (Thread management)
這些服務由容器代理完成將減少套用開發人員的負擔

8.分散式對象運算

遠程對象被作為本地對象來處理:傳遞信息的方式不變,但開銷更大
Enterprise JavaBeans 永遠運行在伺服器上:對 Bean 的訪問永遠是遠程調用

9.Stub 和 Skeleton

由 EJB 生成:
"Stub" 對要傳遞出去的信息編碼
"Tie/Skel" 將接受到的信息解碼並傳遞給目標對象

10.分類: Enterprise JavaBeans

+---Entity Beans--CMP/BMP
Ejb--|
+---Session beans--Stateful/stateless

會話 Bean (Session Bean):根據 EJB 規範,一個會話 Bean 是:

代表單個客戶端來執行
可以參與到事務處理中
不直接代表共享於資料庫中的數據,但它能訪問和更新這些數據
相對而言是短暫存在的
當 EJB 容器失效後就不存在---客戶端需要重新建立一個信新的會話對象來繼續運算

實體 Bean (Entity Bean):根據 EJB 規範,一個實體 Bean 是:

提供在資料庫中數據的對象視圖
允許被多個用戶共享存取訪問
可以是長期存在 (只要它存在於資料庫中)
實體 Bean, 它的主鍵對象, 以及它的遠程引用將能跨 EJB 容器的宕機而存在

11.EJB 類和實例

構建 EJB 套用包括來自三方的代碼
開發人員編寫的代碼
由 EJB API 定義的類和接口
由容器自動生成的代碼
開發人員編寫的代碼包括
Bean 類 (定義了業務邏輯)
Home 接口 (如何查找或創建 bean)
Remote 接口 (如何存取 bean)
其它組件,根據 bean 實際要求

12.EJB Home 接口

每個 bean 有一個
用於:創建新的 bean 實例、查找現存的 bean (只能是實體 bean)

Remote 接口:定義 bean 的公共接口---只有在 Remote 接口中定義的方法才能被客戶端訪問

EJB 客戶端

可以為 servlet, JSP, 應用程式或其它 bean
通過 JNDI 來查找 EJB home 接口,步驟為:
創建一個 JNDI Context (initial context)
使用 JNDI Context 來查找 bean home 接口
使用 bean home 接口來創建/查找 bean 實例
使用 bean 實例完成業務操作
實際的存取 (對 EJB) 是通過容器生成的類來完成

EJB 架構

客戶端對 bean 訪問永遠不是直接的
EJBObject (tie) 是由容器自身提供的:用來幫助管理 bean 的生命周期

EJB 中的角色

EJB 伺服器供應商: 開發並銷售 EJB 伺服器
EJB 容器供應商: 開發並銷售 EJB 容器
Enterprise bean 開發人員: 開發並銷售 EJB
套用組裝人員: 將不同的 EJB 搭建成套用

六、EJB的體系結構

目前,EJB最新的標準是2.1,EJB3.0規範正在討論中,預計將於明年推出。EJB2.1定義了三種企業Bean,分別是會話Bean(Session Bean),實體Bean(Entity Bean)和訊息驅動bean(MessageDriven Bean)。

Session Bean用於實現業務邏輯,它可以是有狀態的,也可以是無狀態的。每當客戶端請求時,容器就會選擇一個Session Bean來為客戶端服務。Session Bean可以直接訪問資料庫,但更多時候,它會通過Entity Bean實現數據訪問。

Entity Bean是域模型對象,用於實現O/R映射,負責將資料庫中的表記錄映射為記憶體中的Entity對象,事實上,創建一個Entity Bean對象相當於新建一條記錄,刪除一個Entity Bean會同時從資料庫中刪除對應記錄,修改一個Entity Bean時,容器會自動將Entity Bean的狀態和資料庫同步。

MessageDriven Bean是EJB2.0中引入的新的企業Bean,它基於JMS訊息,只能接收客戶端傳送的JMS訊息然後處理。MDB實際上是一個異步的無狀態Session Bean,客戶端調用MDB後無需等待,立刻返回,MDB將異步處理客戶請求。這適合於需要異步處理請求的場合,比如訂單處理,這樣就能避免客戶端長時間的等待一個方法調用直到返回結果。

調用一個EJB組件要比調用一個JavaBean麻煩些,由於EJB組件可以分布在多台伺服器上,因此必須首?EJB之後就可以調用EJB的方法了。

七、EJB設計模式

常見EJB設計模式

SESsion Facade pattern

通常項目中,客戶端往往需要頻繁的對伺服器端數據進行操作。當採用實體EJB作為數據的抽象層時,如果直接讓客戶端程式與實體EJB互動,會產生實現一個業務需求便需要大量的EJB屬性操作(如下圖1)。這直接導致如下問題:網路負載大(遠程客戶端時)、並發性能低、客戶端與伺服器端關聯度大、可重用性和可維護性差、性能
因此有必要在客戶端與實體EJB層間加入Session EJB層,在Sessino EJB中實現商業邏輯並封裝對實體EJB的操作。(如下圖2)

Enterprise JavaBean
圖1:客戶端直接與實體EJB互動


Enterprise JavaBean
圖2:通過SessionEJB層實現

Session Facade模式的好處是:降低了網路負載,SessionEjb可以調用實體EJB的本地接口;將商業邏輯與商業數據隔離;維護與開發方便;顯著提高性能。

Session Facade模式因其簡單使用,是目前使用很廣的模式。但具體套用過程中應注意:避免將所有的操作封裝到一個很大的SessionEJB內;伺服器端數據結構應由實體EJB實現,除非特例否則避免直接的資料庫操作;SessionEjb內某些系統通用操作的代碼容易重複(比如許可權檢查等,解決辦法是將系統通用服務封裝在Java Class內)。

MesSAge Facade Pattern

很多時候,一次RequeST需要操作多個EJB又不需要得到即時返回。對這種異步調用,通常套用Message Fa?ade Pattern.

這種時候,如採用Session Fa?ade Pattern存在如下問題:
1. 客戶端等待返回的時間過長。一個SessionEjb的實例在完成客戶請求過程中中涉及到的每一次對其他實體Ejb的調用過程中都會被鎖定直到得到實體EJB返回信息後才能進行下一步操作。這樣造成客戶不必要的等待,並很容易因時間導致整個事務失敗。
2. 系統可靠性和容錯性低。如果需要調用不同系統或伺服器上或多個異構數據源的多個EJB時,任何一個環節出錯,均導致客戶請求失敗。
以Message-Driven Bean為基礎的Message Facade Pattern則可以解決上述異步請求需求。具體架構見下圖3

Enterprise JavaBean
圖3:使用Message Facade Pattern

Message Facade Pattern的不足之處在於:
1. Message-Driven Bean沒有返回值。這樣通知客戶執行結果只能依賴於EmAIl或人工等其他手段。
2. Message-Driven Bean執行過程中無法將捕獲的異常直接返回給客戶端,即無法使客戶端直接直到錯誤信息。
3. Message-Driven Bean通過接收Message回響客戶請求,對Message內容的合法性(比如對象的類型等)依賴與客戶端.容易產生運行時錯誤。
Message Facade Pattern經常與Session Facade Pattern在同一個項目里共同使用。

EJB Command Pattern

Session Facade Pattern中將商業邏輯實現封裝在Session EJB中,這種做法帶來諸多益處之外也帶來如下問題:

1. 由於業務經常的變化,導致經常需要更新Session EJB代碼。
2. 客戶端代碼不得不包含大量EJB相關的API,不利於後期項目維護。
3. 項目開發測試需要經常的EJB重部署過程。

引起上述問題的重要根結就是Session EJB本身重量級組件,其開發測試部署工作量較大,開發周期較長。以上不足可以通過EJB Command Pattern克服。

EJB Command Pattern中將商業邏輯實現封裝在普通的Java ClASs(稱之為Command Bean)中。該模式的具體實現有很多種,通常的框架都包括三部分:

1. Command Bean.由套用開發者寫的具體實現某商業操作的Java Class.主要包含getXXX(),setXXX(),exECute()方法。
2. Client-Side Routing Logic.由多個Class組成,用於將請求轉發至Command Sever,這個過程對客戶是透明的。這部分代碼可以跨項目使用。路由規則中可以考慮用XML技術。
3. Remote Command Server.實際執行商業操作請求。通常可以用Session EJB層實現。

整個框架見下圖4:

Enterprise JavaBean


圖4:Command的基本框架

EJB Command Pattern具有如下好處:

1. 適應與需要快速開發環境。因Command Bean是輕量級的Java Class,其編譯和調試比較方便。
2. 將表現層與商業實現層隔離,同時將客戶端代碼與EJB層隔離。
3. 將客戶端代碼開發與伺服器端代碼開發相對清晰。早期可以創建空的Command Bean方便客戶端代碼調試。

EJB Command Pattern的弱處在於:
1. Command Bean中對事務的控制不如Session EJB中。
2. Command Bean是無狀態的。
3. 無法將異常直接返回給客戶。
4. 在大項目中,由於商業邏輯複雜,常導致大數量的Command Bean存在.
5. 作為Command Server的Session EJB打包時必須包含Command Bean以致存在維護上的不便。

EJB Command Pattern的一個實際實現可以參考IBM's Command framework.

Data Transfer object Factory

基於EJB的J2EE項目,經常需要在客戶端與伺服器端傳輸大量數據。數據的組織形式常用的是DTO(Data Transfer Object,伺服器端數據對象的抽象)。但因為客戶端表現層經常是變化的,所需要伺服器端數據也變動頻繁,換句話說,DTO的數量和屬性經常要更改。因此如何以及在何處生成和維護DTO便是需要考慮的問題。

一種解決方案是直接在Entity EJB中直接處理,即在Entity EJB的Bean類中加入getXXXDTO()、setXXXDTO()等。但這樣做導致EJB與DTO層緊緊綁定。一旦DTO更改,與該DTO相關的EJB即需要重編譯打包。EJB層與客戶端層相關聯不僅使維護困難而且導致EJB的重用性大大降低。

更好的解決方案是利用Data Transfer Object Factory封裝對DTO的操作邏輯(如下圖6)。

Enterprise JavaBean
圖6:DTO Factory示例

DTO Factory具體實現方式通常有兩種:

1. 普通Java Class實現,用於Session Facade Pattern使用DTO環境下。
2. Stateless Session EJB實現,用於非EJB客戶端使用DTO環境下(見圖7)。


Enterprise JavaBean
圖7:SessionEJB實現DTOFactory

DTO Factory帶來如下好處:

1. 使Entity EJB的重用成為可能。由於不含DTO處理邏輯,Entity EJB功能單一化,只作為數據源。不通客戶端通過各自的DTO Factory可以從同一個Entity EJB得到各自所需的個性化數據(自定義DTO)。
2. 提高可維護性和性能。
3. 可以根據在DTO Factory層生成很複雜的DTO結構,諸如繼承、關聯關係等,而對客戶端提供一個透明、細化的數據接口。

使用DTO Factory時需要注意的是:不需為每個Entity EJB定義一個Factory。可以為一系列相關的Entity EJB創建一個Factory,或者只創建一個Factory。

Generic Attribute Access

使用Entity EJB作為商業數據層時,我們首先需要從資料庫載入數據,創建對應的Entity EJB實例,之後對記憶體中Entity EJB實例的屬性進行相應操作。對屬性的操作比較直接的做法是:直接調用Entity EJB的getXXX()/setXXX(),通常利用EJB2.0的本地接口;通過DTO Factory生成DTO。但這兩種做法都存在如下問題:

1. 當Entity EJB的屬性特別多時候,以上做法會帶來複雜羅嗦的代碼,使EJB變的龐大無比。
2. 使Entity EJB的客戶端(比如Session EJB)和Entity EJB的接口緊密關聯。Entity EJB屬性的增刪都需要更改客戶端代碼,給項目開發和維護帶來不便。

事實上可以利用更通用的方式訪問Entity EJB的屬性,即定義Generic Attribute Access Interface。見下圖8:


Enterprise JavaBean
圖8:Generic Attribute Access Interface示例

Generic Attribute Access Interface由Entity EJB的本地或遠程接口實現,並利用Hash Maps傳輸數據。實現方式常見如下:

1. BMP類型實體EJB可以在Bean類中定義包含所有屬性的私有成員變數HashMap。
2. CMP類型實體EJB可以在Bean類中可以適用Java Reflection API實現。
3. 建立一個父類,在不同的情況下定義子類重載父類方法。
使用Generic Attribute Access Interface需要在客戶端與伺服器端對屬性以及對應的關鍵字建立統一的命名習慣。常見的做法如下:
1. 建立並保持良好的文檔記錄和命名約定。
2. 在實體EJB的實現類中定義靜態成員映射屬性。
3. 創建共享靜態類,通過成員變數映射實體EJB屬性。
4. 通過JNDI在伺服器端保存屬性映射關係。
Generic Attribute Access Interface的運用帶來一下益處:
1. 接口實現後對不通實體EJB都適用。
2. 對屬性較多實體EJB能精簡代碼,並更具維護性。
3. 使運行中動態增刪實體EJB屬性成為可能。
Generic Attribute Access Interface的缺點在於:
1. 訪問EJB屬性時增加了額外的操作。需要通過關鍵字映射屬性,最後還需進行類型轉換。
2. 需要建立客戶端與伺服器端的命名約定。
3. 因為通過HashMap操作時候需要進行類型轉換,容易產生運行時類型不匹配異常。

Business Interface

EJB規範要求Bean實現類必須實現所有在遠程(或本地)接口中定義的所有方法,同時不允許Bean實現類直接繼承遠程(或本地)接口。這就導致編譯時候很容易產生兩者不一致的問題,即遠程(或本地)接口中定義的某方法為在Bean實現類中被實現等錯誤。為避免抗訴錯誤,可以利用套用伺服器廠商所提供的工具。但也可以套用EJB的設計架構來實現:定義商業接口。
Business Interface即創建自定義商業接口,在接口中定義所有EJB提供的商業方法,並讓Bean實現類和遠程(或本地)接口都實現該商業接口。其繼承關係見下圖9:


Enterprise JavaBean
圖9:商業接口的使用

Business Interface是個普通的Java Class。依賴於使用本地接口與遠程接口的不通,Business Interface的定義略有不同:套用與遠程接口時,在接口中的方法需要拋出java.rmi.RemoteException;而套用與本地接口時候則不需要作任何特別處理。
套用Business Interface時候必須注意一點:EJB規範不允許直接EJB的實例將對自己的引用(this對象)返回給客戶端,否則編譯時候即報錯。但使用Business Interface後,編譯時候無法檢查出有無將this對象返回給客戶端。這一點需要程式設計師自己保證。

三. 內部數據轉換策略

Data Transfer Object

基於EJB的J2EE多層架構套用中,經常涉及的一個問題就是如何在各層之間傳遞批量數據,比如客戶端對伺服器端數據的批量讀寫操作等。比如需要得到實體EJB的屬性,直接的方法是多次調用不通的屬性,如下圖10:

Enterprise JavaBean
圖10:低效的數據傳遞方式

但這種方法容易導致許多問題,比如性能以及代碼的??需要的屬性。所以可以引入Data Transfer Object來封裝所需要的屬性,並在客戶與伺服器端通過傳遞該對象一次實現對數據的操作。如下圖11:

Enterprise JavaBean
圖11:通過DTO傳遞數據

DTO為普通的Java Class,通常是伺服器端數據的快照。由於網路傳輸的需要,DTO應該實現java.io.Serializable接口。
DTO的設計有兩種模型:Domain DTO以及Custom DTO。
Domain DTO僅僅實現對伺服器數據的拷貝,通常與實體EJB為一對一的關係(也存在為多個相關聯的實體EJB對應一個Domain DTO)。Domain DTO通常除用於讀取更改實體EJB屬性外也可用於創建實體EJB時候。實體EJB與Domain DTO對應關係如下圖12:

Enterprise JavaBean
圖12:Account EJB 與 Account DomainDTO

Domain DTO的套用除了DTO所具有的一般優點外,還有別的益處:

1. 開發迅速。因為一旦實體EJB設計好後,很容易轉換得到Domain DTO。
2. 可以利用Domain DTO的setXXX()方法在客戶端進行屬性有效性效驗。
Domain DTO的缺點有:
1. 客戶端綁定了伺服器端數據模型,不利於維護。
2. 不夠靈活,無法處理客戶端的多樣化數據要求。對一個數百個屬性的實體EJB請求一個屬性時候卻返回一個包含所有屬性值的Domain DTO明顯是笨重的實現。
3. 導致代碼的重複。
4. Domain DTO中如果嵌套包含了別的Domain DTO時,一旦需伺服器端數據的更改而需要重定義Domain DTO模型時候異常困難。

Custom DTO則可以克服上述的一些缺點。Customer DTO僅僅封裝用戶感興趣的伺服器數據集即可以根據客戶端需求創建Customer DTO。這樣作的優點是靈活高效;缺點是大項目中可能導致大量的Customer DTO存在。

通常Domain DTO可以用於數據的更新與創建;Customer DTO可以用於客戶用於表現層的數據讀取。兩者可以相輔相成。而且使用DTO一般與DTO Factory同時使用。

Domain Transfer Hash Map

DTO的使用往往缺乏通用性。不通的用戶案例需要創建不同的DTO。當項目很複雜時,從維護性考慮需要更好的數據傳輸的實現方式。
Domain Transfer Hash Map即利用HashMap作為客戶所需數據集的封裝。好處是:

1. 良好的維護性。
2. 較大的通用性。不同的客戶端可以使用相同的數據傳遞方式。
缺點是:
1. 需要維護客戶端與伺服器端在屬性及其對應關鍵字的映射關係。
2. 當需要使用基本類型的數據時候,因為Hash Map的限制必須將基本類型先轉換成對象。
3. 使用得到的數據時,需要進行類型強制轉換。

Data Transfer RowSet

當需要處理直接的JDBC調用得到的結果集時,顯然用DTO/Hash Map已經不合適,因為需要對大量數據進行類型轉換等額外操作是很費資源和不必要的,而且最終用戶常需要以表格式樣顯示數據。

所以對二維表式數據,更好的處理方式是利用Data Transfer RowSet。Data Transfer RowSet通過將ResultSet直接轉換為RowSet傳遞給客戶端。
在Session EJB中使用RowSet的一段示例代碼如下圖13:

Enterprise JavaBean
圖13:使用RowSet

使用RowSet的好處很多:
1. 接口通用於各樣的資料庫查詢操作。
2. 當需要表格式數據顯示時,因為直接從ResultSet得到,所以不需要額外的數據類型轉換。
缺點是:
1. 資料庫結構暴露給客戶端。
2. 不符合面向對象設計思想。
3. 依賴於SQL
Data Transfer RowSet通常用於唯讀式數據的顯示操作,經常和JDBC for Reading Pattern連用。

四.事務和數據持久機制

JDBC for Reading Pattern

基於EJB的J2EE套用中,通過EJB對資料庫的操作可以有兩種方式:實體EJB或者Session EJB中直接利用JDBC訪問。
客戶很多時候取出資料庫中數據並以表格方式顯示。這種情形如果使用實體EJB會導致如下問題:
1. 引用伺服器端頻繁的資料庫查詢和載入操作。因為載入N個實體EJB總需要進行一次find()操作N次數據載入。
2. 如果使用Remote接口,引起頻繁的額外網路操作。
3. 對關聯關係比較複雜的資料庫表結構,很難直接通過Entity EJB表現。

因此建議在只需對資料庫表數據進行唯讀訪問時候,應該採用JDBC for Reading Pattern,即通過JDBC直接訪問資料庫。除了避免上述使用實體EJB的缺點還帶來一下好處:
1. 充分利用資料庫能力,比如資料庫的快取機制。
2. 減少了對事務控制的資源。
3. 利用自定義SQL可以按需要比較靈活的讀取數據。
4. 只需要一次數據查詢,減少了資料庫操作。

缺點是:
1. 於J2EE套用的面向對象設計相違背。
2. 因為Session EJB代碼中包含了自定義SQL,維護性差。
3. Session EJB中不得不包含JDBC的API,並且需要了解資料庫結構。

部屬人員: 使用相應工具在運行環境下配置 EJB
系統管理員: 監視運行時情況

相關詞條

相關搜尋

熱門詞條

聯絡我們