2013-12-23 3 views
4

Я прочитал отличный ответ, когда разорванный вид бобов уничтожен. (см. How and when is a @ViewScoped bean destroyed in JSF?), и я автоматически предположил, что уничтоженный компонент также удален из кеша области видимости. Но я мог видеть, что bean все еще находится в кеше, поэтому я хотел бы знать, должен ли быть удален удаленный bebe-объект из области просмотра LRU, если когда-либо?Как и когда элемент видимости вида удален из кеша LRU в JSF?

В нашем приложении мы открываем все детали в вкладках/окнах sepeare. После некоторого открытия/закрытия (зависит от numberOfViewsInSession) мы могли бы увидеть ViewExpiredException в том случае, когда первое подробное окно все еще открыто, и пользователь открывает и закрывает другие окна подробностей, и через некоторое время он хочет выполнить некоторую операцию в первом окне. Я сделал некоторую отладку, и я вижу, что закрытое представление не было удалено из кэша LRU.

Так ожидаемое поведение или что-то не так в моем коде? И если это ожидаемое поведение, есть ли какая-нибудь полезная стратегия, как работать с multitabs/multiwindow без большого количества ViewExpiredException, вызванного кешем LRU ?. Я знаю, что могу изменить numberOfViewsInSession, но это последний выбор, который я хочу использовать.

Я подготовил простую тестовую папку, и когда я открываю/закрываю view.xhtml больше раз, я вижу, что LRUMap растет.

Окружающая среда: JDK7, mojarra 2.2.4, 7.0.47 кот

Заранее спасибо

View.xhtml

<!DOCTYPE html> 
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> 
    <head> 
     <title>View Bean</title> 
     <meta charset="UTF-8"/> 
    </head> 
    <body> 
     <h:form id="viewForm"> 
     <div>#{viewBean.text}</div> 
     <h:commandButton id="closeButton" value="Close" action="/ClosePage.xhtml"/> 
     </h:form> 
    </body> 
</html> 

index.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    <html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:h="http://xmlns.jcp.org/jsf/html"> 
    <h:head> 
     <title>Session bean</title> 
    </h:head> 
    <h:body> 
     <h:form id="sessionForm"> 
      <h:outputText value="#{sessionBean.text}"/> 
      <br/> 
      <h:link id="linkView" value="Open view.xhmtl" outcome="/view.xhtml" target="_blank"/> 
     </h:form> 
    </h:body> 
</html> 

ClosePage.xhmtl

<!DOCTYPE html> 
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">    
    <head><title>Window will be closed</title></head> 
    <body> 
     <script>window.close();</script> 
    </body> 
</html> 

ViewBean.java

package com.mycompany.mavenproject2; 

import com.sun.faces.util.LRUMap; 
import java.io.Serializable; 
import java.util.Map; 
import javax.annotation.PreDestroy; 
import javax.faces.bean.ManagedBean; 
import javax.faces.bean.ViewScoped; 
import javax.faces.context.FacesContext; 

@ManagedBean(name = "viewBean") 
@ViewScoped 
public class ViewBean implements Serializable { 

    private static final long serialVersionUID = 13920902390329L; 

    private int lruMapSize; 

    /** 
    * Creates a new instance of ViewBean 
    */ 
    public ViewBean() { 

     Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap(); 
     LRUMap<String, LRUMap> lruMap = (LRUMap) sessionMap.get("com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap"); 
     lruMapSize = lruMap == null ? 0 : lruMap.size(); 
    } 

    @PreDestroy 
    void destroyed() { 
     System.out.println("View bean destroyed"); 
    } 

    public String getText() { 
     return "ViewBean LRU cache size:" + Integer.toString(lruMapSize); 
    } 

} 

SessionBean.java

package com.mycompany.mavenproject2; 

import java.io.Serializable; 
import javax.faces.bean.ManagedBean; 
import javax.faces.bean.SessionScoped; 

@ManagedBean(name = "sessionBean") 
@SessionScoped 
public class SessionBean implements Serializable { 
    private static final long serialVersionUID = 1777489347L; 

    /** 
    * Creates a new instance of SessionBean 
    */ 
    public SessionBean() { 
    } 

    public String getText() { 
     return "Session bean text"; 
    } 

} 

ответ

1

Я думаю, что каждый разработчик JSF работает против этого в конце концов. Настоящая проблема заключается в том, что вы не можете создать действительно надежную систему с сохранением состояния, в которой браузер будет сообщать о компоненте ViewScoped, что это делается со страницей, позволяя бэк-файлу уничтожать себя. Вот почему в реализациях JSF есть кеши LRU, чтобы ограничить память, используемую сеансом, что является отличным решением для повседневных приложений.

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

Самое простое решение, с которым я столкнулся, - использовать таймер javascript для выполнения обратной передачи ajax на сервер на каждой странице с помощью компонента ViewScoped. (Установка этого таймера на выполнение каждые 30 секунд кажется разумной.) Это приведет к перемещению bean (-ов) ViewScoped, связанных со страницей, в нижней части кеша LRU, гарантируя, что они не истекли.

В частности, я использую компонент опроса "перфорирование", чтобы вытащить его и вставить в шаблон, который будет использоваться всеми компонентами ViewScoped. Поместив этот компонент в свою форму, размер запроса остается небольшим.