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

Jun*_*aid 5 jsf scope managed-bean

我有2个JSF托管bean A,B我需要A在2分钟B后和5分钟后过期/销毁/销毁.我检查了这个相关的问题,从一个bean中定时,但是整个会话到期了.我不想让整个会话到期.

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

Bal*_*usC 7

鉴于您正在使用JSF bean管理工具(因此不需要CDI,这需要完全不同的答案),您可以实现此目的@CustomScoped.该@CustomScoped值必须引用Map更广泛的,通常存在的范围中的实现.

就像是:

@ManagedBean
@CustomScoped("#{timeoutScope}")
public class TimeoutBean {}
Run Code Online (Sandbox Code Playgroud)

由于@CustomScoped注释不支持传递其他参数,因此只能通过下面的其他自定义注释来设置超时:

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

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

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

现在,这是一个启动示例#{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;
    }

}
Run Code Online (Sandbox Code Playgroud)

你看,它的会话范围和实现Map.至于范围,这种方式与特定用户会话相关联,而不是与整个应用程序相关联.如果您确实想要在应用程序中的所有用户会话中共享bean,那么请将其作为应用程序作用域.至于,当MapJSF需要找到托管bean时,它首先尝试get().如果它返回null(即bean还不存在),那么它将自动创建托管bean实例并执行put().

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

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

  • 提示:切勿使用“!” 或“ ??”,也不要过度使用正式英语的粗体格式。这样,您像个混蛋一样过来,我很想删除答案,而忽略以后的问题。至于评论中的问题,该代码不会以编程方式触发`@ PostConstruct`,因为JSF已经自动完成了。该代码仅手动触发`@ PreDestroy`,因为JSF不会自动完成。带有`publishEvent()`的行可以做到这一点。在提出新问题之前先尝试一下。 (2认同)