2012-05-16 19 views
7

我使用Spring和JSF 2來創建一個Web應用程序。 業務對象被保存在Spring容器,我使用@ManagedProperty注入他們在管理豆類,像這樣:如何在反序列化時重新注入瞬態@ManagedProperty?

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Getter @Setter 
    @ManagedProperty("#{someService}") 
    private SomeService someService; 
    // ... 

的問題是,我不斷收到一個NotSerializableException從春天一類(ServiceLocatorFactoryBean)它正在使用SomeService bean。

如果我使它transient,我怎麼能在反序列化後重新注入它?

或者,有什麼其他方法可以解決這個問題?

我一直在閱讀其他幾個類似的問題,但找不到任何與此問題完全相關的問題。

+2

僅供參考:當您僅使用Java EE自己的EJB而不是Spring時,此問題不存在。 – BalusC

+0

@BalusC是的,我在其他問題上讀到了這個,不幸的是,我對EJB的使用還不夠了解(我不知道我是否可以說服同事讓我在這個項目中嘗試) 。你能指點我一個很好的資源來了解它,順便說一句嗎? – elias

+0

這並不難。只要確保你的容器已經支持EJB(GlassFish,JBoss,Weblogic等)。使用'@ Stateless'或'@ Stateful'註釋服務類並通過'@ EJB'注入它。而已。沒有getter/setter需要順便說一句。 – BalusC

回答

3

取代在@ManagedProperty註解(在ManagedBean初始化中執行)中通過EL注入Spring bean,獲取在運行時評估EL的bean。

採用這種方法,這是JSF豆應該是什麼樣子:

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return SpringJSFUtil.getBean("someService"); 
    } 
    // ... 

與公用事業類SpringJSFUtil.java是得到通過EL豆:

import javax.faces.context.FacesContext; 

public class SpringJSFUtil { 

    public static <T> T getBean(String beanName) { 
     if (beanName == null) { 
      return null; 
     } 
     return getValue("#{" + beanName + "}"); 
    } 

    @SuppressWarnings("unchecked") 
    private static <T> T getValue(String expression) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     return (T) context.getApplication().evaluateExpressionGet(context, 
       expression, Object.class); 
    } 
} 

這消除Spring bean屬性(以進行更多的EL評估爲代價),從而避免了將屬性放在首位的所有序列化問題。

同樣的方法,用OmniFaces

在我實際的代碼,我使用可用的utility classOmniFacesevaluateExpressionGet(String expression)方法。因此,對於那些你們誰使用過,這是我的代碼真正的樣子:

import static org.omnifaces.util.Faces.evaluateExpressionGet; 

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return evaluateExpressionGet("#{someService}"); 
    } 
    // ... 

請注意,這裏的方法獲取完整的EL(「#{表達式}」),不只是春天bean名稱(或者你會得到一個ClassCastException)。

-1

那麼記住這一點從春手冊( link to spring):

基於構造函數或基於二傳手DI?

由於您可以混合使用基於構造函數和基於Setter的DI,因此對於可選的依賴項,使用強制依賴關係和構造函數的構造函數參數是一個很好的經驗法則。請注意,在setter上使用@Required註釋可以用來使setter需要依賴關係。

Spring團隊通常主張setter注入,因爲大量的構造函數參數可能會變得笨拙,特別是當屬性是可選的。 Setter方法也使該類的對象可以重新配置或稍後重新注入。通過JMX MBeans進行管理是一個引人注目的用例。

一些純粹主義者喜歡基於構造函數的注入。提供所有對象依賴性意味着對象總是以完全初始化的狀態返回給客戶端(調用)代碼。缺點是物體變得不適合重新配置和重新注入。

使用最適合特定類別的DI。有時,在處理您沒有來源的第三方課程時,您可以選擇。遺留類可能不會公開任何setter方法,因此構造函數注入是唯一可用的DI。

+0

對不起,我沒有關注...基於構造函數和基於setter的DI與我的問題有什麼關係? – elias

2

在Spring @Service上試用@Scope(value = BeanDefinition.SCOPE_SINGLETON,proxyMode = ScopedProxyMode.INTERFACES)。這應該將一個可序列化的代理對象注入到您的託管bean中,這將在反序列化後訪問服務時重新定位服務。

0

對於那些關注 - 我有一個注入ResourceBundle類似的問題。使用BalusC的答案的一部分,我做了以下內容:

@ManagedProperty(value="#{myBundle}") 
private transient ResourceBundle myBundle; 

private Object readResolve() { 
    myBundle = FacesContext.getCurrentInstance().getApplication() 
     .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{myBundle}", 
     ResourceBundle.class); 
    return this; 
} 

這樣,EL只有當管理bean被反序列化評估。