[整理]在 Web App 中使用 Quartz 替换 Java 定时器

来源:互联网 发布:小米笔记本12.5编程 编辑:程序博客网 时间:2024/05/21 21:37

之前的项目里有使用定时器,每隔一段时间去 MQ 服务器拉取新的消息。

使用的是Java 自身的 TimerTask 以及监听器实现的,因为项目需求变更,回头查看代码时,发现原来的实现不是很合理(一个Task里面做了多项操作),就找到Quarts这个开源框架了。

首先,看看原来使用Java提供的TimerTask 来实现定时触发的方法:

1.新建一个继承自java.util.TimerTask的类,重写其run()方法,这就是我们要定时执行的任务了:

import java.util.TimerTask;public class GetMessageTask extends TimerTask {@Overridepublic void run() {System.out.println("Ho ho, GetMessageTask is running @ "+ new java.util.Date());}}

2.新建一个定时器类,在这个类中定义一个java.util.Timer类型的属性用来定义我们的执行任务的开始时间以及,时间间隔:

public class GetMessageTimer {java.util.Timer timer;public void startTimer() {timer = new java.util.Timer();/** * 系统启动1分钟后开始执行任务 */java.util.GregorianCalendar gc = new java.util.GregorianCalendar();gc.setTime(new java.util.Date());gc.add(java.util.Calendar.MINUTE, 1);System.out.println("定时器 @ " + new java.util.Date() + "开启了!");/** * 每30000毫秒执行一次取数任务 */timer.schedule(new GetMessageTask(), gc.getTime(), 30000);}public void stopTimer() {if (timer != null)timer.cancel();System.out.println("定时器 @ " + new java.util.Date() + "关闭了!");}}

3.新建一个监听器,用于应用部署时,启动我们的定时器:

public class GetMessageListener implements ServletContextListener {GetMessageTimer timer = new GetMessageTimer();@Overridepublic void contextDestroyed(ServletContextEvent sce) {timer.stopTimer();}@Overridepublic void contextInitialized(ServletContextEvent sce) {timer.startTimer();}}

4.在web.xml中配置监听器:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><listener><display-name>定时取数</display-name><listener-class>com.abc.web.timertask.GetMessageListener</listener-class></listener><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>

这样我们就完成了,部署至Web 容器,查看控制台输出如下图:

由图所示,定时器开启1分钟以后,我们的任务每隔30秒执行一次。

接下来,我们看一下,怎样使用 Quartz 来实现上面的功能。

1.首先下载 Quartz 不必多说了。

Quartz官网:http://quartz-scheduler.org/

我下的版本为2.1.3

2.新建 java web app,将所需jar包导入项目:


3.我们是要在web app中使用quartz,查看官网文档http://quartz-scheduler.org/documentation/quartz-2.1.x/cookbook/ServletInitScheduler,我们可以看出,可以使用2种方法来启动quartz,这里我们使用servlert的方式来启动,修改web.xml如下:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><servlet><servlet-name>QuartzInitializer</servlet-name><servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class><init-param><param-name>shutdown-on-unload</param-name><param-value>true</param-value></init-param><!-- 加载及启动定时任务 true --><init-param><param-name>start-scheduler-on-load</param-name><param-value>true</param-value></init-param><!-- 启动延迟时间 60秒 --><init-param><param-name>start-delay-seconds</param-name><param-value>60</param-value></init-param><load-on-startup>1</load-on-startup></servlet><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>
4.查看相关文档,我们要先创建我们要执行的Job:
public class GetMessageJob implements Job {@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {System.out.println("Hello, GetMessageJob is Executing @ "+ new java.util.Date());}}
5.接着我们要新建Trigger,来告诉Schedule神马时候执Job,因为是测试没有集成其他框架,所以我就想到了quartz自带的QuartzInitializarServlet,这里我们改写这个Servlet。这个Servlet的源码,可以在下载的文档里找到,下面是我改写的Servlet:
public class InitializerQuartzServlet extends HttpServlet {private static final long serialVersionUID = 1L;public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY";private boolean performShutdown = true;private boolean waitOnShutdown = false;private transient Scheduler scheduler = null;@Overridepublic void init(ServletConfig cfg) throws javax.servlet.ServletException {super.init(cfg);System.out.println("Quartz Initializer Servlet 加载成功, initializing Scheduler...");StdSchedulerFactory factory;try {String configFile = cfg.getInitParameter("config-file");String shutdownPref = cfg.getInitParameter("shutdown-on-unload");if (shutdownPref != null) {performShutdown = Boolean.valueOf(shutdownPref).booleanValue();}String shutdownWaitPref = cfg.getInitParameter("wait-on-shutdown");if (shutdownPref != null) {waitOnShutdown = Boolean.valueOf(shutdownWaitPref).booleanValue();}factory = getSchedulerFactory(configFile);scheduler = factory.getScheduler();String startOnLoad = cfg.getInitParameter("start-scheduler-on-load");int startDelay = 0;String startDelayS = cfg.getInitParameter("start-delay-seconds");try {if (startDelayS != null && startDelayS.trim().length() > 0)startDelay = Integer.parseInt(startDelayS);} catch (Exception e) {log("Cannot parse value of 'start-delay-seconds' to an integer: "+ startDelayS + ", defaulting to 5 seconds.", e);startDelay = 5;}/* * 告诉scheduler要执行的任务,以及执行间隔时间,这里是30秒 */JobDetail getMessageJob = newJob(GetMessageJob.class).withIdentity("getDetailsJob", "group1").build();Trigger getMessageTrigger = newTrigger().withIdentity("getDetailsTrigger", "group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(30).repeatForever()).build();scheduler.scheduleJob(getMessageJob, getMessageTrigger);if (startOnLoad == null|| (Boolean.valueOf(startOnLoad).booleanValue())) {if (startDelay <= 0) {// Start nowscheduler.start();System.out.println("Scheduler has been started...");} else {// Start delayedscheduler.startDelayed(startDelay);System.out.println("Scheduler will start in " + startDelay+ " seconds.");}} else {System.out.println("Scheduler has not been started. Use scheduler.start()");}String factoryKey = cfg.getInitParameter("servlet-context-factory-key");if (factoryKey == null) {factoryKey = QUARTZ_FACTORY_KEY;}System.out.println("Storing the Quartz Scheduler Factory in the servlet context at key: "+ factoryKey);cfg.getServletContext().setAttribute(factoryKey, factory);String servletCtxtKey = cfg.getInitParameter("scheduler-context-servlet-context-key");if (servletCtxtKey != null) {System.out.println("Storing the ServletContext in the scheduler context at key: "+ servletCtxtKey);scheduler.getContext().put(servletCtxtKey,cfg.getServletContext());}} catch (Exception e) {System.out.println("Quartz Scheduler 初始化失败: "+ e.toString());throw new ServletException(e);}}protected StdSchedulerFactory getSchedulerFactory(String configFile)throws SchedulerException {StdSchedulerFactory factory;// get Propertiesif (configFile != null) {factory = new StdSchedulerFactory(configFile);} else {factory = new StdSchedulerFactory();}return factory;}@Overridepublic void destroy() {if (!performShutdown) {return;}try {if (scheduler != null) {scheduler.shutdown(waitOnShutdown);}} catch (Exception e) {System.out.println("Quartz Scheduler 停止失败: "+ e.toString());e.printStackTrace();}System.out.println("Quartz Scheduler 停止了.");}@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.sendError(HttpServletResponse.SC_FORBIDDEN);}@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.sendError(HttpServletResponse.SC_FORBIDDEN);}}

6.这里我们看到初始化时需要一个quartz.properties的配置文件,我们从demo里面copy一个过来,稍作修改:

org.quartz.scheduler.instanceName: ExampleSchedulerorg.quartz.threadPool.threadCount: 3org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.jobStore.class:org.quartz.simpl.RAMJobStore

7.不要忘了把web.xml中的servlet-class改成我们改写的servlet,最后配一个log4j.properties。部署到应用服务器查看控制台输出如下图:



上图所示,定时器在应用部署1分钟后开始启动,任务每隔30秒执行一次。

更高级的使用Quartz的方法,请大家参考cookbook。

原创粉丝点击