我的建议是尝试已集成 CDI 和 JPA 的 Tomcat 版本(TomEE)。它带有 OpenJPA,但您可以使用 Hibernate。然后使用这样的类进行缓存:
@Singleton
@Startup
public class CachingBean {
@Resource
private BeanManager beanManager;
@Schedule(minute = "*/10", hour = "*")
private void run() {
// cache things
}
}
该组件将在应用程序启动时自动启动,并每十分钟运行一次上述方法。有关详细信息,请参阅计划文档。
更新
给你举了一个例子。使用良好的 CDI/EJB 组合来安排 CDI 事件。
BeanManager.fireEvent(Object,Annotations...)
实际上,这是对添加ScheduleExpression
到混合中的方法的简单包装。
@Singleton
@Lock(LockType.READ)
public class Scheduler {
@Resource
private TimerService timerService;
@Resource
private BeanManager beanManager;
public void scheduleEvent(ScheduleExpression schedule, Object event, Annotation... qualifiers) {
timerService.createCalendarTimer(schedule, new TimerConfig(new EventConfig(event, qualifiers), false));
}
@Timeout
private void timeout(Timer timer) {
final EventConfig config = (EventConfig) timer.getInfo();
beanManager.fireEvent(config.getEvent(), config.getQualifiers());
}
// Doesn't actually need to be serializable, just has to implement it
private final class EventConfig implements Serializable {
private final Object event;
private final Annotation[] qualifiers;
private EventConfig(Object event, Annotation[] qualifiers) {
this.event = event;
this.qualifiers = qualifiers;
}
public Object getEvent() {
return event;
}
public Annotation[] getQualifiers() {
return qualifiers;
}
}
}
然后使用它,Scheduler
作为 EJB 注入并安排离开。
public class SomeBean {
@EJB
private Scheduler scheduler;
public void doit() throws Exception {
// every five minutes
final ScheduleExpression schedule = new ScheduleExpression()
.hour("*")
.minute("*")
.second("*/5");
scheduler.scheduleEvent(schedule, new TestEvent("five"));
}
/**
* Event will fire every five minutes
*/
public void observe(@Observes TestEvent event) {
// process the event
}
}
完整的源代码和工作示例,在这里。
你必须知道
如果有 10 个观察者,每个观察者需要 7 分钟执行,那么一个事件的总执行时间是 70 分钟。将事件安排为比 70 分钟更频繁地触发对您绝对没有好处。
如果你这样做了会发生什么?取决于@Singleton
@Lock
政策
@Lock(WRITE)
是默认值。在这种模式下,该timeout
方法将基本上被锁定,直到前一次调用完成。即使您每 70 分钟只能处理一次,但每 5 分钟触发一次,最终会导致所有池化计时器线程都在您的 Singleton 上等待。
@Lock(READ)
允许并行执行该timeout
方法。事件将并行触发一段时间。然而,由于它们实际上每个都需要 70 分钟,所以在一个小时左右的时间内,我们将用完计时器池中的线程,就像上面一样。
优雅的解决方案是使用然后在方法上@Lock(WRITE)
指定一些短的超时。当接下来的 5 分钟调用被触发时,它会等到 1 分钟才能访问 Singleton,然后才放弃。这将使您的计时器池不会被备份的作业填满——“溢出”被简单地丢弃。@AccessTimeout(value = 1, unit = TimeUnit.MINUTES)
timeout