在时间间隔后使特定托管 bean 实例过期
Expire specific managed bean instance after time interval
我有 2 个 JSF 托管 bean A
和 B
,我需要在 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 和最终用户控制的时间更改)。
我有 2 个 JSF 托管 bean A
和 B
,我需要在 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 和最终用户控制的时间更改)。