在时间间隔后使特定托管 bean 实例过期

Expire specific managed bean instance after time interval

我有 2 个 JSF 托管 bean AB,我需要在 2 分钟后 expire/destruct/destroy A 和 5 分钟后 B。我检查了这个相关问题 Timing out from a bean,但整个会话都将过期。我不想让整个会话过期。

如何使用自定义范围实现此目的?

鉴于您使用的是 JSF bean 管理工具(因此不是 CDI,这需要完全不同的答案),您可以使用 @CustomScoped 实现这一点。 @CustomScoped 值必须在更广泛的(通常是现有的)范围内引用 Map 实施。

类似于:

@ManagedBean
@CustomScoped("#{timeoutScope}")
public class TimeoutBean {}

由于 @CustomScoped 注解不支持传递附加参数,设置超时只能通过如下所示的附加自定义注解来完成:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Timeout {

    /** Minutes. */
    int value();

}
@ManagedBean
@CustomScoped("#{timeoutScope}")
@Timeout(5) // Expires after 5 minutes.
public class TimeoutBean {}

现在,这是 #{timeoutScope} 的启动示例,包括 @PostConstruct 支持(自动)和 @PreDestroy 支持(手动):

@ManagedBean
@SessionScoped
public class TimeoutScope extends HashMap<String, Object> {

    private static final long serialVersionUID = 1L;

    @Override
    public Object put(String name, Object bean) {
        Timeout timeout = bean.getClass().getAnnotation(Timeout.class);

        if (timeout == null) {
            throw new IllegalArgumentException("@Timeout annotation is required on bean " + name);
        }

        Long endtime = System.nanoTime() + (timeout.value() * (long) 6e10);
        Object[] beanAndEndtime = new Object[] { bean, endtime };
        return super.put(name, beanAndEndtime);
    }

    @Override
    public Object get(Object key) {
        Object[] beanAndEndtime = (Object[]) super.get(key);

        if (beanAndEndtime == null) {
            return null;
        }

        Object bean = beanAndEndtime[0];
        Long endtime = (Long) beanAndEndtime[1];

        if (System.nanoTime() > endtime) {
            String name = (String) key;
            ScopeContext scope = new ScopeContext("timeoutScope", Collections.singletonMap(name, bean));
            FacesContext context = FacesContext.getCurrentInstance();
            context.getApplication().publishEvent(context, PreDestroyCustomScopeEvent.class, scope);
            return null;
        }

        return bean;
    }

}

你看,它的会话范围和实现 Map。至于范围,这样它就与特定的用户会话相关联,而不是与整个应用程序相关联。如果您真的想在应用程序中的所有用户会话之间共享该 bean,则改为将其设置为应用程序范围。至于 Map,每当 JSF 需要查找托管 bean 时,它首先尝试 get()。如果它 returns null (即 bean 尚不存在),那么它将自动创建托管 bean 实例并执行 put().

put()内部,是提取和计算超时时间并将其存储在地图中。在 get() 中,您只需检查超时和 return null 以指示 JSF 该 bean 不再存在。然后 JSF 将简单地自动创建它并在 put() 返回,等等

请注意,我使用的是 System#nanoTime() instead of System#currentTimeMillis(),因为后者与 OS(操作系统)时间相关,而不是硬件时间(因此它对 a.o 敏感)。 DST 和最终用户控制的时间更改)。