为啥在会话过期之前过期的@ViewScoped bean 不会被销毁

Posted

技术标签:

【中文标题】为啥在会话过期之前过期的@ViewScoped bean 不会被销毁【英文标题】:Why are expired @ViewScoped beans not destroyed until the session expires为什么在会话过期之前过期的@ViewScoped bean 不会被销毁 【发布时间】:2014-01-22 16:50:27 【问题描述】:

我在带有 Java 7 的 GlassFish 4 上使用 Mojarra 2.2.4。

从 BalusC 对How and when is a @ViewScoped bean destroyed in JSF? 的回答中了解到,@ViewScoped bean 应该在三种情况下被销毁:

    具有非空结果的回发 会话到期 已超出会话中的最大逻辑视图数

我的 bean 在前两种情况下被销毁,但在超过最大逻辑视图数时不会。我已经验证了 bean 在超过最大值时会 expire(我得到一个 ViewExpiredException),但在会话本身过期之前它们仍然没有被销毁

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

问题

为什么 bean 过期后没有销毁? 这是错误还是预期行为? 什么是干净的解决方法来确保 bean 被销毁? 更新: OmniFaces ViewScoped annotation 在 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 次,我会在日志中获得 50 个 INFO: Constructing SandboxController 副本。无论我刷新多少次,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】:

我能够通过使用 OmniFaces @ViewScoped 注释 (org.omnifaces.cdi.ViewScoped) 而不是标准的 @ViewScoped (javax.faces.view.ViewScoped) 找到一个干净的解决方法。

OmniFaces ViewScoped 会在 bean 过期后立即正确销毁它们。

更多详情请看这里: http://showcase.omnifaces.org/cdi/ViewScoped

【讨论】:

在我的测试中,我注意到在这种特定情况下的行为存在明显差异。你能指出你所指的具体评论吗?何时销毁过期bean的选择可能取决于实现,这样虽然两个库都符合相同的API并因此具有“相同的生命周期”,当过期的 bean 被销毁时,它们仍然会有所不同。 Mahmoud 只是有个误会。忽略不正确的短语。我从来没有在任何地方说过。

以上是关于为啥在会话过期之前过期的@ViewScoped bean 不会被销毁的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Quickblox iOS SDK 中检查会话是不是有效或过期?

chrome会话cookie显示过期时间为1969-12-31T23:59:59.000Z

Codeigniter 会话已过期 - 更新数据库

Laravel flash 或会话消息未过期 [未维护已更新]

idea都破解了为啥还是弹出过期凭证

为啥刷新令牌会在 14 天后过期