Просмотр области: java.io.NotSerializableException: javax.faces.component.html.HtmlInputText

Каждый раз, когда кнопка вызывает действие из резервной копии - bean, возникает ошибка.  Используется только для beans с областью видимости, и я не нашел способ исправить ее без регрессии над другими модулями в коде.

DefaultFacele E   Exiting serializeView - Could not serialize state: javax.faces.component.html.HtmlInputText 
java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)

Или также:

com.ibm.ws.webcontainer.servlet.ServletWrapper service SRVE0014E: Uncaught service() exception 
root cause Faces Servlet: ServletException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier /jspFiles/jsf/Deployments/editQueue.faces 
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:205) 
Caused by: javax.faces.application.ViewExpiredException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier:  /jspFiles/jsf/Deployments/editQueue.faces
    at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute (RestoreViewExecutor.java:128)

лица-config.xml

<managed-bean>
  <managed-bean-name>pc_EditQueue</managed-bean-name>
  <managed-bean-class>pagecode.jspFiles.jsf.deployments.EditQueue</managed-bean-class>
  <managed-bean-scope>view</managed-bean-scope>
  <managed-property>
    <property-name>queueDeploymentBean</property-name>
    <value>#{queueDeploymentBean}</value>
  </managed-property>
</managed-bean>

web.xml

<context-param>
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
  <param-value>server</param-value>
</context-param>
<context-param>
  <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
  <param-value>true</param-value>
</context-param>

bean:

@ManagedBean
@ViewScoped
public class EditQueue extends PageCodeBase implements Serializable {
  private static final long serialVersionUID = -1L;
  public String doButtonAddAction() {
    // calls manager (long)
    FacesUtil.setViewMapValue("queueDeploymentBean", queueDeploymentBean);
    return "";
}

Я прочитал это предложение чтобы установить SERIALIZE_STATE_IN_SESSION в false, и действительно это решение работает для этой области видимости bean. Однако это исправление стоит дорого: многие существующие модули в приложении больше не работают, поэтому я не могу использовать это исправление. Некоторые наблюдаемые регрессии:

// returns null must be changed with FacesUtil.getSessionMapValue("userId"); 
getSessionScope().get("userId");`

// returns null must be changed with FacesUtil.getViewMapValue("linkerBean");
linkerBean = (Linker) getManagedBean("linkerBean");`

// NPE so must be changed with FacesContext.getCurrentInstance().addMessage(...)
getFacesContext().addMessage(...)`

Итак, мои вопросы:

  • почему исключение NotSerializableException, даже если bean реализует Serializable?
  • существует ли способ применить параметр SERIALIZE_STATE_IN_SESSION только к подмножеству beans или нет?
  • существует ли другое решение для работы моей области видимости bean (без необходимости изменять их для запроса области или иначе)?

    WebSphere 8.0.0.3, Java 1.6.0, JSF 2.0, RichFaces 4.2.3.Final

Ответ 1

почему исключение NotSerializableException, даже если bean реализует Serializable?

Не только bean должен быть сериализуемым, но все его свойства (и все их вложенные свойства и т.д.) также должны быть сериализуемыми. Имя оскорбительного несериализуемого класса можно легко найти в сообщении об исключении:

java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)

Это говорит о том, что вы привязываете компонент <h:inputText> к bean, как показано ниже:

<h:inputText binding="#{bean.fooInput}" ...>
private UIComponent fooInput;

Это действительно незаконно, если bean не находится в области запроса. UIComponent экземпляры являются областями действия запроса и не могут быть разделены между несколькими запросами. Кроме того, UIComponent экземпляры не сериализуемы. Только их состояние есть, но JSF будет беспокоиться об этом сам по себе.

Вы должны удалить свойство fooInput, и вам нужно искать другое решение проблемы, для которого вы неправильно считали, что привязка компонента к области с видимым охватом bean будет правильным решением.

  • Если вы намерены получить доступ к нему в другом месте в представлении, например. #{bean.fooInput.value}, а затем просто привяжите его к области Facelet без использования свойства bean:

    <h:inputText binding="#{fooInput}" ...>
    

    Он будет доступен в другом месте в том же представлении через #{fooInput.xxx}.

    <h:inputText ... required="#{empty fooInput.value}" />
    

  • Если вы намерены программно установить атрибут компонента внутри bean, например. fooInput.setStyleClass("someClass") или fooInput.setDisabled(true), тогда вы должны привязать к нему определенный атрибут вместо всего компонента:

    <h:inputText ... styleClass="#{bean.styleClass}" />
    ...
    <h:inputText ... disabled="#{bean.disabled}" />
    

  • Если вы абсолютно уверены, что вам нужно получить руку целого экземпляра UIComponent в bean по какой-либо причине, тогда вручную захватите его в локальной локализации метода вместо привязки к нему:

    public void someMethod() {
        UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
        UIComponent fooInput = view.findComponent("formId:fooInputId");
        // ...
    }
    

    Но лучше задайте вопрос или найдите ответ, как решить конкретную проблему по-другому, без необходимости захвата целого компонента в базе данных bean.

См. также:


Что касается ViewExpiredException, это имеет разные основания, которые более подробно описаны в javax.faces.application.ViewExpiredException: просмотр не удалось восстановить.