为什么已过期的@ViewScoped Bean直到会话过期才被销毁?

7

我正在使用Java 7上的GlassFish 4和Mojarra 2.2.4。

根据BalusC在How and when is a @ViewScoped bean destroyed in JSF?中的回答,@ViewScoped beans应该在以下三种情况下被销毁:

  1. 后退并且结果不为空
  2. 会话过期
  3. 会话中的逻辑视图达到最大值

我的bean在前两种情况下被销毁了,但是当逻辑视图的最大数量超过时,它们并没有被销毁。我已经验证了当达到最大值时,bean确实会过期(我会得到一个ViewExpiredException),但是直到会话本身过期它们才被销毁。

出于内存消耗的原因,我希望在第三种情况下销毁这些bean,特别是因为它们在过期后无法使用。

问题

  • 为什么bean在过期时没有被销毁?
  • 这是一个bug还是预期行为?
  • 有什么干净的解决方法来确保bean被销毁?

最小示例

这是我的bean:

@javax.inject.Named("sandboxController")
@javax.faces.view.ViewScoped
public class SandboxController implements Serializable {
    private static final Logger log = Logger.getLogger(SandboxController.class.getName());
    @PostConstruct
    public void postConstruct() {
        log.log(Level.INFO, "Constructing SandboxController");
    }
    @PreDestroy
    public void preDestroy() {
        log.log(Level.INFO, "Destroying SandboxController");
    }
    public String getData() {
        return "abcdefg";
    }
}

以及我的sandbox.xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!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">
    <body>
        <h:form>
            <h:outputText value="#{sandboxController.data}"/>
        </h:form>
    </body>
</html>

以下是我的web.xml的一部分:

<context-param>  
    <param-name>com.sun.faces.numberOfLogicalViews</param-name>
    <param-value>3</param-value>
</context-param>
<context-param>  
    <param-name>com.sun.faces.numberOfViewsInSession</param-name>  
    <param-value>3</param-value>
</context-param>

如果我刷新sandbox.xhtml 50次,日志中会出现 INFO: Constructing SandboxController 的50个副本。无论我刷新多少次,这些Bean都不会被销毁。VisualVM证实这些Bean仍然被UIViewRoot的ViewMap引用。在我的完整大小的Bean中,它保留了相当多的状态,很快就会出现OutOfMemoryException。
当我手动过期会话时,会出现50个 INFO: Destroying SandboxController 副本。
如果我向sandbox.xhtml添加一个提交按钮并在4个不同的选项卡中加载它,然后尝试提交第一个选项卡,我会得到一个ViewExpiredException,正如预期的那样,但是这个Bean仍然没有被销毁。
如果我使用javax.faces.bean.ManagedBean和javax.faces.view.ViewScoped进行注释,行为是相同的。然而,OmniFaces注释org.omnifaces.cdi.ViewScoped可以正常工作。
澄清一下...
我的@ViewScoped Bean在会话过期时是会被销毁的,不像相关问题中描述的问题,例如Linked ViewScoped beans lead to memory leaks 我不是在问每个Bean为什么不会立即在后续刷新时被销毁,就像这里提出的问题一样:JSF 2.1 ViewScopedBean @PreDestroy method is not called。我想知道的是,即使它们过期了,并且不再有用,为什么它们仍然没有被销毁,因此继续消耗内存。
1个回答

2
我能够找到一个干净的解决方案,通过使用OmniFaces @ViewScoped注释(org.omnifaces.cdi.ViewScoped)代替标准的@ViewScoped(javax.faces.view.ViewScoped)。
OmniFaces ViewScoped正确地在Bean过期后立即销毁它们。
更多详情请参见此处: http://showcase.omnifaces.org/cdi/ViewScoped

在我的测试中,我注意到在这种特定情况下的行为明显有所不同。你能指出你所参考的具体评论吗?也有可能销毁过期bean的选择取决于实现方式,因此尽管两个库都符合相同的API,因而拥有“相同的生命周期”,它们仍然会在销毁过期bean的时间上有所不同。 - Jonathan Dautrich
2
马赫茂德只是有一个误解。忽略那些不正确的短语。我从来没有在任何地方说过那样的话。 - BalusC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接