Dim*_*man 28 jsf timer scheduled-tasks jsf-2
我想知道是否可以Timer在应用程序范围内使用bean.
例如,假设我想创建一个计时器任务,每天一次向每个注册会员发送一堆电子邮件.我试图尽可能多地使用JSF,我想知道这是否可以接受(我知道它有点奇怪).
到目前为止,我已经使用了上述所有内容了ServletContextListener.(我不想使用任何应用程序服务器或cron作业,我想在Web应用程序中保留上述内容.)
是否有一种聪明的JSF方式,或者我应该坚持使用旧模式?
Bal*_*usC 70
至于从JSF托管bean内部生成一个线程,只有你希望能够在你的视图中#{managedBeanName}或在其他托管bean中引用它时才有意义@ManagedProperty("#{managedBeanName}").您应该只确保实现@PreDestroy以确保在webapp即将关闭时关闭所有这些线程,就像在contextDestroyed()方法中一样ServletContextListener(是的,你做了吗?).另请参阅在JSF托管bean中启动新线程是否安全?
java.util.Timer在Java EE中使用至于java.util.Timer在JSF托管bean中使用,你绝对不应该使用老式的Timer,而是现代的ScheduledExecutorService.它Timer具有以下主要问题,使其不适合在长时间运行的Java EE Web应用程序中使用(引自Java Concurrency in Practice):
Timer对系统时钟的变化很敏感,ScheduledExecutorService不是.Timer只有一个执行线程,因此长时间运行的任务可以延迟其他任务.ScheduledExecutorService可以配置任意数量的线程.TimerTask一个线程中抛出的任何运行时异常Timer都会导致死亡,即计划任务不再运行.ScheduledThreadExecutor不仅可以捕获运行时异常,还可以根据需要处理它们.抛出异常的任务将被取消,但其他任务将继续运行.除了书的引用,我还能想到更多的缺点:
如果你忘了明确cancel()的Timer,那么它保持取消部署后运行.因此,在重新部署后,创建一个新线程,再次执行相同的工作.等等.它已成为一个"火与忘记",你现在不能以编程方式取消它.您基本上需要关闭并重新启动整个服务器以清除以前的线程.
如果Timer线程未标记为守护程序线程,则它将阻止webapp的取消部署和服务器的关闭.你基本上需要硬杀服务器.主要缺点是webapp将无法通过eg contextDestroyed()和@PreDestroy方法执行优雅的清理.
@Schedule如果您的目标是Java EE 6或更新版本(例如JBoss AS,GlassFish,TomEE等,因此不是像Tomcat 这样的准系统JSP/Servlet容器),那么请使用@Singleton带有@Schedule方法的EJB .这样容器就会担心通过池化和销毁线程ScheduledExecutorService.您只需要以下EJB:
@Singleton
public class BackgroundJobManager {
@Schedule(hour="0", minute="0", second="0", persistent=false)
public void someDailyJob() {
// Do your job here which should run every start of day.
}
@Schedule(hour="*/1", minute="0", second="0", persistent=false)
public void someHourlyJob() {
// Do your job here which should run every hour of day.
}
@Schedule(hour="*", minute="*/15", second="0", persistent=false)
public void someQuarterlyJob() {
// Do your job here which should run every 15 minute of hour.
}
}
Run Code Online (Sandbox Code Playgroud)
如果必要,可通过@EJB以下方式在托管bean中使用:
@EJB
private BackgroundJobManager backgroundJobManager;
Run Code Online (Sandbox Code Playgroud)
ScheduledExecutorService没有EJB,您需要手动使用ScheduledExecutorService.应用程序范围的托管bean实现看起来像这样:
@ManagedBean(eager=true)
@ApplicationScoped
public class BackgroundJobManager {
private ScheduledExecutorService scheduler;
@PostConstruct
public void init() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
}
@PreDestroy
public void destroy() {
scheduler.shutdownNow();
}
}
Run Code Online (Sandbox Code Playgroud)
当SomeDailyJob这个样子的:
public class SomeDailyJob implements Runnable {
@Override
public void run() {
// Do your job here.
}
}
Run Code Online (Sandbox Code Playgroud)
如果您根本不需要在视图或其他托管bean中引用它,那么最好只是使用ServletContextListener它来保持它与JSF分离.
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
Run Code Online (Sandbox Code Playgroud)