簡體   English   中英

EJB和CDI bean序列化的最佳實踐

[英]Best practice for serialization for EJB and CDI beans

我還沒有遇到任何序列化相關的問題。 但是PMD和Findbugs發現了一系列關於序列化的潛在問題。 典型情況是注入的記錄器被檢測為不可序列化。 但還有更多 - EntityManager和幾個CDI bean。

我沒有找到任何關於如何正確處理序列化的最佳實踐。

  • @Inject@PersistenceContext注入的字段重新注入反序列化?
  • 它們應該標記為transient嗎?
  • 或者我應該忽略/關閉代碼檢查?
  • PMD建議我應該真正提供所有這些領域的訪問者嗎?

我意識到這是一個老問題,但我相信提供的唯一答案是不正確的。

將@Inject和@PersistenceContext注入的字段重新注入反序列化?

不,他們不會。 我個人在集群環境中使用JBoss體驗過這一點。 如果bean具有鈍化能力,那么容器必須注入可序列化的代理。 該代理被序列化和反序列化。 一旦反序列化,它將找到正確的注入並重新連接它。 但是,如果將字段標記為瞬態,則代理不會序列化,並且在訪問注入的資源時您將看到NPE。

應該注意,注入的資源或bean不必是Serializable,因為代理將是。 唯一的例外是@Dependent范圍的bean,它們必須是可序列化的或者是注入瞬態的。 這是因為在這種情況下不使用代理。

它們應該標記為瞬態嗎?

不,見上文。

或者我應該忽略/關閉代碼檢查?

這取決於你,但這就是我要做的。

PMD建議我應該真正提供所有這些領域的訪問者嗎?

我不會。 在我們的項目中,當我們知道我們正在使用CDI時,我們會禁用此檢查。

這個答案將詳細介紹EJB 3.2( JSR 345 ),JPA 2.1( JSR 338 )和CDI 1.2( JSR 346 )的序列化/鈍化語義。 值得注意的是,Java EE 7傘規范( JSR 342 ),Managed Beans 1.0規范( JSR 316 )和Commons Annotations規范1.2( JSR 250 )沒有任何關於序列化/我們感興趣的內容/鈍化。

我還將討論靜態代碼分析器的主題。

EJB

相關章節是“4.2有狀態會話Bean的會話狀態”和“4.2.1實例鈍化和會話狀態”。

@Stateless@Singleton實例永遠不會被鈍化。

@Stateful實例可能被鈍化。 從EJB 3.2開始,類開發人員可以使用@Stateful(passivationCapable=false)選擇退出鈍化。

EJB規范明確指出,容器會處理對諸如UserTransactionEntityManagerFactory和容器管理的EntityManager類的事物的引用。 除非持久化上下文和EntityManager實現中的所有實體都是可序列化的,否則不會鈍化使用擴展持久性上下文的@Stateful實例。

請注意,應用程序管理的EntityManager始終使用擴展的持久性上下文。 此外,@ Stateful實例是EJB會話實例的唯一類型,它可以使用容器管理的EntityManager和擴展的持久化上下文。 此持久性上下文將綁定到@Stateful實例的生命周期,而不是單個JTA事務。

EJB規范沒有明確地解決具有擴展持久化上下文的容器管理的EntityManager所發生的情況。 我的理解是這樣的:如果有一個擴展的持久化上下文,那么根據之前定義的規則,這個人必須被認為是可序列化的,如果是,則進行鈍化。 如果鈍化繼續進行,那么@Stateful類開發人員只需關注自己對應用程序管理的實體管理器的引用。

除了描述開發人員應該做出的假設之外,EJB規范沒有規定瞬態字段會發生什么。

第4.2.1節說:

Bean Provider必須假設在PrePassivate和PostActivate通知之間可能會丟失瞬態字段的內容。

[...]

雖然容器不需要使用Java編程語言的序列化協議來存儲鈍化會話實例的狀態,但它必須實現相同的結果。 一個例外是容器在激活期間不需要重置瞬態字段的值。 一般來說,不鼓勵將會話bean的字段聲明為瞬態。

要求容器“實現與Javas序列化協議相同的結果”,同時讓它完全沒有說明瞬態字段會發生什么,這是非常可悲的,說實話。 帶回家的教訓是,任何東西都不應該被標記為短暫的。 對於容器無法處理的字段,請使用@PrePassivate寫入null@PostActivate進行還原。

JPA

JPA規范中沒有出現“鈍化”一詞。 JPA也沒有為EntityManagerFactoryEntityManagerQueryParameter等類型定義序列化語義。 規范中與​​我們相關的唯一一句是(“6.9查詢執行”部分):

CriteriaQuery,CriteriaUpdate和CriteriaDelete對象必須是可序列化的。

CDI

“6.6.4。鈍化作用域”一節將鈍化作用域定義為顯式注釋@NormalScope(passivating=true)的作用域。 此屬性默認為false。

一個含義是@Dependent - 這是一個偽范圍 - 不是一個具有鈍化能力的范圍。 另外值得注意的是, javax.faces.view.ViewScoped不是一個具有鈍化能力的范圍,無論出於何種原因,大多數互聯網似乎都相信這一范圍。 例如,“Java 9 Recipes:A Problem-Solution Approach”一書中的“17-2。開發JSF應用程序”一節。

具有鈍化能力的范圍要求聲明“具有范圍的類的實例具有鈍化能力”(“6.6.4。鈍化范圍”部分)。 “6.6.1。具有鈍化功能的bean”一節將這樣的對象實例定義為可轉移到二級存儲的對象實例。 特殊的類注釋或接口不是明確的要求。

EJB實例:@Stateless和@Singleton不是“具有鈍化功能的bean”。 @Stateful可能是(有狀態是唯一允許CDI管理生命周期的EJB會話類型 - 即,永遠不會將CDI范圍放在@Stateless或@Singleton上)。 如果它們及其攔截器和裝飾器都是可序列化的,那么其他“托管bean”只是“具有鈍化功能的bean”。

未被定義為“具有鈍化功能的bean”並不意味着諸如無狀態,單例,EntityManagerFactory,EntityManager,Event和BeanManager之類的東西不能用作您創作的具有鈍化功能的實例中的依賴項。 這些東西被定義為“具有鈍化能力的依賴性”(參見“6.6.3。能夠依賴鈍化的依賴性”和“3.8。附加內置bean”一節)。

CDI通過使用具有鈍化能力的代理來使這些依賴性能夠被鈍化(參見“5.4。客戶端代理”一節中的最后一個項目符號項和“7.3.6。資源的生命周期”一節)。 請注意,對於要使其具有鈍化能力的Java EE資源(例如EntityManagerFactory和EntityManager),必須將它們聲明為CDI生成器字段(“3.7.1。聲明資源”一節),它們不支持除@Dependent之外的任何其他范圍(參見“3.7。資源”一節),必須使用@Inject在客戶端查找它們。

其他@Dependent實例 - 雖然沒有聲明具有正常范圍且不需要由CDI“客戶端代理”前端 - 如果實例可以轉移到輔助存儲,即可序列化,也可以用作具有鈍化能力的依賴關系。 這個人將與客戶端一起序列化(請參閱“5.4。客戶端代理”部分中的最后一個項目符號項)。

要非常明確並提供一些例子; @Stateless實例,對CDI生成的EntityManager的引用和可序列化的@Dependent實例都可以用作類中的實例字段,並使用具有鈍化功能的范圍進行注釋。

靜態代碼分析器

靜態代碼分析器是愚蠢的。 我認為對於高級開發人員來說,他們更多的是引起關注而不是助手。 這些分析器針對可疑的序列化/鈍化問題引發的虛假標志肯定是非常有限的價值,因為CDI要求容器驗證實例“真正具有鈍化能力,此外,它的依賴性是能夠鈍化的”或者“拋出” javax.enterprise.inject.spi.DeploymentException的子類“(”6.6.5。驗證具有鈍化能力的bean和依賴關系“和”2.9。容器自動檢測到的問題“)。

最后,正如其他人所指出的那樣,值得重復一遍:我們應該永遠不要將一個領域標記為transient

PMD和FindBugs只檢查接口,也沒有關於代碼運行環境的信息。 要使工具安靜,可以將它們標記為瞬態,但是在反序列化時它們都將被正確地重新注入,並且無論transient關鍵字如何都可以首次使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM