You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat部署的JSF项目如何实现服务端启动自动运行Java Timer任务

问题原因

你把Timer初始化逻辑写在了Job1类的main方法里,这个方法只有你手动执行java Job1指令时才会被JVM调用。当项目打包部署到Tomcat后,服务器只会遵循Web规范初始化Servlet、监听器、JSF托管Bean这类Web组件,不会主动执行任意类的main方法,这就是定时任务在服务端不运行的根本原因。

解决方案

方案1:ServletContextListener(最推荐,无额外依赖,兼容所有Tomcat版本)

这个是Java Web原生提供的扩展点,容器启动/关闭应用时会自动触发对应方法,不需要引入任何第三方框架,最适配你当前的JSF+Tomcat技术栈。

  1. 新建监听器类,代码如下:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.Timer;
import java.util.TimerTask;

// Tomcat7及以上版本支持@WebListener注解,加了就不需要额外配置web.xml
@WebListener
public class TimerInitListener implements ServletContextListener {
    private Timer timer;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 应用启动完成后自动执行该方法,在这里初始化定时任务
        risk riskInstance = new risk();
        // 构造Timer时传入true标记为守护线程,避免阻塞Tomcat正常关闭
        timer = new Timer(true);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    riskInstance.review_date2();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 0, 24 * 60 * 60 * 1000); // 你需要的24小时间隔,测试时可临时改为1分钟
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 应用关闭时主动终止定时器,避免内存泄漏
        if (timer != null) {
            timer.cancel();
        }
    }
}
  1. 如果你的项目用的是Servlet3.0以下版本(不支持注解),在web.xml中添加如下配置注册监听器:
<listener>
    <listener-class>替换成你这个监听器类的全限定类名,比如com.xxx.util.TimerInitListener</listener-class>
</listener>

方案2:JSF 预初始化托管Bean

如果你不想写Servlet监听器,也可以利用JSF的托管Bean生命周期实现启动自动运行:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import java.util.Timer;
import java.util.TimerTask;

// eager=true表示容器启动时立刻初始化Bean,不等用户第一次请求
@ManagedBean(eager = true)
@ApplicationScoped // 整个应用生命周期内只实例化一次
public class RiskScheduleBean {
    private Timer timer;

    @PostConstruct
    public void initSchedule() {
        risk riskInstance = new risk();
        timer = new Timer(true);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    riskInstance.review_date2();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 0, 24 * 60 * 60 * 1000);
    }

    @PreDestroy
    public void destroySchedule() {
        if (timer != null) {
            timer.cancel();
        }
    }
}

注意:必须配置eager=true,否则JSF默认会在Bean第一次被前端/后端请求访问时才初始化,没有请求的话定时任务永远不会启动。

生产环境注意事项
  • 不建议长期用java.util.Timer做生产调度:Timer内部是单线程执行,一旦你的review_date2()方法抛出未捕获的异常,整个Timer线程会直接终止,后续所有定时任务都会停止运行。稳定性要求高的场景可以替换为JDK自带的ScheduledThreadPoolExecutor,或者引入Quartz这类专业调度框架。
  • 如果你的定时逻辑需要调用JSF托管Bean、带数据库事务的服务类,不要直接new实例,要从JSF上下文/容器中获取托管的实例,否则会出现依赖注入失效、事务不生效的问题。
  • 如果后续项目做集群部署(多台Tomcat同时运行),一定要加分布式锁或者用统一调度入口,避免定时任务在多台机器上重复执行。

内容的提问来源于stack exchange,提问作者Loran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:24:35