EJB 3.0中@PostConstruct未调用,如何触发定时任务初始化?
在TomEE中触发EJB 3.0无状态Bean初始化定时器的方案
因为EJB 3.0无状态会话Bean默认懒加载,必须被外部调用才会触发@PostConstruct,要在部署时自动初始化定时器,有以下两种可行方案:
方案一:使用Servlet上下文监听器(标准Java EE方式,移植性好)
通过Servlet监听器在Web应用启动时主动获取Bean实例并调用一个空方法,触发Bean初始化:
- 给无状态Bean定义一个本地接口(或利用EJB 3.0的无接口视图),添加一个空方法用于触发初始化
- 实现
ServletContextListener,在上下文初始化时注入Bean并调用空方法
代码示例
无状态Bean及本地接口
import javax.ejb.Stateless; import javax.annotation.Resource; import javax.ejb.PostConstruct; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import java.util.Date; @Stateless public class HourlyTaskBean implements HourlyTaskLocal { @Resource private TimerService timerService; @PostConstruct public void initTimer() { // 检查已存在的定时器,避免重复创建 for (Timer timer : timerService.getTimers()) { if ("HourlyTaskTimer".equals(timer.getInfo())) { return; } } // 创建每小时执行的间隔定时器(延迟0毫秒启动,每3600000毫秒执行一次) TimerConfig config = new TimerConfig("HourlyTaskTimer", false); timerService.createIntervalTimer(0, 3600000, config); } @Timeout public void executeHourlyTask(Timer timer) { // 这里编写定时任务逻辑 System.out.println("执行每小时任务:" + new Date()); } // 空方法,仅用于触发Bean实例化 @Override public void triggerInit() {} } // 本地接口 public interface HourlyTaskLocal { void triggerInit(); }
Servlet上下文监听器
import javax.ejb.EJB; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class EJBStartupListener implements ServletContextListener { @EJB private HourlyTaskLocal hourlyTaskBean; @Override public void contextInitialized(ServletContextEvent sce) { // 调用空方法触发Bean初始化,进而执行@PostConstruct创建定时器 hourlyTaskBean.triggerInit(); } @Override public void contextDestroyed(ServletContextEvent sce) { // 可选:在此清理定时器(如果需要) } }
方案二:使用TomEE专属的@Startup注解(简洁高效)
TomEE提供了扩展注解org.apache.openejb.api.Startup,可以直接标记无状态Bean,让容器在启动时主动实例化Bean,触发@PostConstruct方法:
代码示例
import org.apache.openejb.api.Startup; import javax.ejb.Stateless; import javax.annotation.Resource; import javax.ejb.PostConstruct; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import java.util.Date; @Stateless @Startup // TomEE扩展注解,容器启动时自动实例化Bean public class HourlyTaskBean { @Resource private TimerService timerService; @PostConstruct public void initTimer() { // 避免重复创建定时器 for (Timer timer : timerService.getTimers()) { if ("HourlyTaskTimer".equals(timer.getInfo())) { return; } } timerService.createIntervalTimer(0, 3600000, new TimerConfig("HourlyTaskTimer", false)); } @Timeout public void executeHourlyTask(Timer timer) { System.out.println("执行每小时任务:" + new Date()); } }
注意事项
- 两种方案都需要在
@PostConstruct中检查已有定时器,防止容器重启或热部署时重复创建定时器 - 方案一基于标准Java EE规范,可移植到其他支持EJB 3.0的容器;方案二是TomEE专属,代码更简洁但移植性稍差
内容的提问来源于stack exchange,提问作者ace
相关产品推荐
相关产品推荐

