From 06820b548d72b23587fade5ed8d20eb6c547402c Mon Sep 17 00:00:00 2001 From: Calvin Date: Thu, 2 Aug 2012 11:29:27 +0800 Subject: [PATCH] =?UTF-8?q?#77=20Scheduler=E9=83=A8=E5=88=86=E6=87=89?= =?UTF-8?q?=E7=94=A8Spring=E7=9A=84Task=20NameSpace=EF=BC=8C=E5=8F=8A?= =?UTF-8?q?=E5=B0=8D=E5=8E=9F=E6=9C=89=E9=83=A8=E5=88=86=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E8=A8=BB=E9=87=8B=E8=88=87=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{JdkExecutorJob.java => JdkTimerJob.java} | 31 ++++------- .../showcase/modules/schedule/QuartzJob.java | 25 --------- .../modules/schedule/SpringCronJob.java | 23 +++----- .../modules/schedule/UserCountScanner.java | 52 +++++++++++++++++++ .../applicationContext-showcases.xml | 2 +- .../schedule/applicationContext-jdk-timer.xml | 2 +- .../applicationContext-quartz-cron-local.xml | 10 ++-- ...pplicationContext-quartz-timer-cluster.xml | 6 +-- .../applicationContext-spring-cron.xml | 15 ------ .../applicationContext-spring-scheduler.xml | 25 +++++++++ .../schedule/quartz-cluster.properties | 1 + .../modules/schedule/JdkTimerJobTest.java | 4 +- .../schedule/QuartzTimerClusterJobTest.java | 2 + .../modules/schedule/SpringTimerJobTest.java | 36 +++++++++++++ .../springside/modules/utils/Reflections.java | 2 +- .../org/springside/modules/utils/Threads.java | 3 +- 16 files changed, 148 insertions(+), 91 deletions(-) rename examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/{JdkExecutorJob.java => JdkTimerJob.java} (65%) delete mode 100644 examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java create mode 100644 examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java delete mode 100644 examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml create mode 100644 examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml create mode 100644 examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java similarity index 65% rename from examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java rename to examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java index 366dfb53..a743a839 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java @@ -8,22 +8,17 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.commons.lang3.Validate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable; import org.springframework.scheduling.support.TaskUtils; -import org.springside.examples.showcase.service.AccountService; import org.springside.modules.utils.Threads; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** - * 被ScheduledThreadPoolExecutor定时执行的任务类, 并强化了退出控制. + * 用JDKScheduledThreadPoolExecutor定时执行的任务。 + * 相比Spring的NameSpace, 不需要反射調用,并强化了退出控制. */ -public class JdkExecutorJob implements Runnable { - - private static Logger logger = LoggerFactory.getLogger(JdkExecutorJob.class); +public class JdkTimerJob implements Runnable { private int initialDelay = 0; @@ -33,17 +28,19 @@ public class JdkExecutorJob implements Runnable { private ScheduledExecutorService scheduledExecutorService; - private AccountService accountService; + @Autowired + private UserCountScanner userCountScanner; @PostConstruct public void start() throws Exception { Validate.isTrue(period > 0); - //任何异常不会中断schedule执行 - Runnable task = new DelegatingErrorHandlingRunnable(this, TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER); + //任何异常不会中断schedule执行, 由Spring TaskUtils的LOG_AND_SUPPRESS_ERROR_HANDLER進行处理 + Runnable task = TaskUtils.decorateTaskWithErrorHandler(this, null, true); + //创建单线程的SechdulerExecutor,并用guava的ThreadFactoryBuilder设定生成线程的名称 scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat( - "JdkExecutorJob-%1$d").build()); + "JdkTimerJob-%1$d").build()); //scheduleAtFixedRatefixRate() 固定任务两次启动之间的时间间隔. //scheduleAtFixedDelay() 固定任务结束后到下一次启动间的时间间隔. @@ -60,8 +57,7 @@ public class JdkExecutorJob implements Runnable { */ @Override public void run() { - long userCount = accountService.getUserCount(); - logger.info("There are {} user in database, printed by jdk timer job.", userCount); + userCountScanner.executeByJdk(); } /** @@ -79,14 +75,9 @@ public class JdkExecutorJob implements Runnable { } /** - * 设置gracefulShutdown的等待时间,单位秒. + * 设置normalShutdown的等待时间, 单位秒. */ public void setShutdownTimeout(int shutdownTimeout) { this.shutdownTimeout = shutdownTimeout; } - - @Autowired - public void setAccountService(AccountService accountService) { - this.accountService = accountService; - } } diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java deleted file mode 100644 index d5c7c58b..00000000 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.springside.examples.showcase.modules.schedule; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springside.examples.showcase.service.AccountService; - -/** - * 被Spring的Quartz MethodInvokingJobDetailFactoryBean定时执行的普通Spring Bean. - */ -public class QuartzJob { - - private static Logger logger = LoggerFactory.getLogger(QuartzJob.class); - - @Autowired - private AccountService accountService; - - /** - * 定时打印当前用户数到日志. - */ - public void execute() { - long userCount = accountService.getUserCount(); - logger.info("There are {} user in database, printed by quartz local job.", userCount); - } -} diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java index 2dbfa5f9..583a3b22 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java @@ -7,28 +7,25 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.commons.lang3.Validate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; -import org.springside.examples.showcase.service.AccountService; import org.springside.modules.utils.Threads; /** - * 使用Spring的ThreadPoolTaskScheduler执行Cron式任务的类, 并强化了退出控制. + * 使用Spring的ThreadPoolTaskScheduler执行Cron式任务的类. + * 相比Spring的NameSpace, 不需要反射調用,并强化了退出控制. */ public class SpringCronJob implements Runnable { - private static Logger logger = LoggerFactory.getLogger(SpringCronJob.class); - private String cronExpression; private int shutdownTimeout = Integer.MAX_VALUE; private ThreadPoolTaskScheduler threadPoolTaskScheduler; - private AccountService accountService; + @Autowired + private UserCountScanner userCountScanner; @PostConstruct public void start() { @@ -44,9 +41,7 @@ public class SpringCronJob implements Runnable { @PreDestroy public void stop() { ScheduledExecutorService scheduledExecutorService = threadPoolTaskScheduler.getScheduledExecutor(); - Threads.normalShutdown(scheduledExecutorService, shutdownTimeout, TimeUnit.SECONDS); - } /** @@ -54,8 +49,7 @@ public class SpringCronJob implements Runnable { */ @Override public void run() { - long userCount = accountService.getUserCount(); - logger.info("There are {} user in database, printed by spring cron job.", userCount); + userCountScanner.executeBySpringCronByJava(); } public void setCronExpression(String cronExpression) { @@ -63,14 +57,9 @@ public class SpringCronJob implements Runnable { } /** - * 设置gracefulShutdown的等待时间,单位秒. + * 设置normalShutdown的等待时间,单位秒. */ public void setShutdownTimeout(int shutdownTimeout) { this.shutdownTimeout = shutdownTimeout; } - - @Autowired - public void setAccountService(AccountService accountService) { - this.accountService = accountService; - } } diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java new file mode 100644 index 00000000..555a061d --- /dev/null +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java @@ -0,0 +1,52 @@ +package org.springside.examples.showcase.modules.schedule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springside.examples.showcase.service.AccountService; + +/** + * 被Spring各种Scheduler反射调用的Service POJO + * + * @author Calvin + */ +@Component +public class UserCountScanner { + + private static Logger logger = LoggerFactory.getLogger(UserCountScanner.class); + + @Autowired + private AccountService accountService; + + public void executeByJdk() { + execute("jdk timer job"); + } + + public void executeBySpringCronByJava() { + execute("spring cron job by java"); + } + + //被Spring的Quartz MethodInvokingJobDetailFactoryBean反射执行 + public void executeByQuartzLocalJob() { + execute("quartz local job"); + } + + //被Spring的Scheduler namespace 反射构造成ScheduledMethodRunnable + public void executeBySpringCronByXml() { + execute("spring cron job by xml"); + } + + //被Spring的Scheduler namespace 反射构造成ScheduledMethodRunnable + public void executeBySpringTimerByXml() { + execute("spring timer job by xml"); + } + + /** + * 定时打印当前用户数到日志. + */ + private void execute(String by) { + long userCount = accountService.getUserCount(); + logger.info("There are {} user in database, printed by {}.", userCount, by); + } +} diff --git a/examples/showcase/src/main/resources/applicationContext-showcases.xml b/examples/showcase/src/main/resources/applicationContext-showcases.xml index 6767c1ad..62402196 100644 --- a/examples/showcase/src/main/resources/applicationContext-showcases.xml +++ b/examples/showcase/src/main/resources/applicationContext-showcases.xml @@ -11,7 +11,7 @@ - + diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml b/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml index d6cbd7a1..588aca96 100644 --- a/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml +++ b/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml @@ -6,7 +6,7 @@ 使用JDK ScheduledExecutorService的定时任务配置 - + diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml index feec548a..0c756404 100644 --- a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml +++ b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml @@ -13,7 +13,7 @@ - + 5 @@ -30,14 +30,12 @@ - + - - + + - - diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml index 40de0b27..ea94ad0e 100644 --- a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml +++ b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml @@ -15,7 +15,7 @@ - + @@ -32,12 +32,12 @@ - + - + diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml b/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml deleted file mode 100644 index ba5f4568..00000000 --- a/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - 使用Spring的 Scheduled的定时任务配置 - - - - - - - - - \ No newline at end of file diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml b/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml new file mode 100644 index 00000000..3f328835 --- /dev/null +++ b/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml @@ -0,0 +1,25 @@ + + + + 使用Spring的 Scheduled的定时任务配置 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/showcase/src/main/resources/schedule/quartz-cluster.properties b/examples/showcase/src/main/resources/schedule/quartz-cluster.properties index af9352b5..7356e540 100644 --- a/examples/showcase/src/main/resources/schedule/quartz-cluster.properties +++ b/examples/showcase/src/main/resources/schedule/quartz-cluster.properties @@ -21,5 +21,6 @@ org.quartz.jobStore.misfireThreshold = 60000 org.quartz.jobStore.useProperties = false org.quartz.jobStore.tablePrefix = QRTZ_ +#Cluster setting org.quartz.jobStore.isClustered = true org.quartz.jobStore.clusterCheckinInterval = 15000 \ No newline at end of file diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java index df2ceb23..0dc69073 100644 --- a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java +++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java @@ -23,7 +23,7 @@ public class JdkTimerJobTest extends SpringTransactionalTestCase { //加载测试用logger appender Log4jMockAppender appender = new Log4jMockAppender(); - appender.addToLogger(JdkExecutorJob.class); + appender.addToLogger(UserCountScanner.class); //等待任务启动 Threads.sleep(3000); @@ -31,5 +31,7 @@ public class JdkTimerJobTest extends SpringTransactionalTestCase { //验证任务已执行 assertEquals(1, appender.getLogsCount()); assertEquals("There are 6 user in database, printed by jdk timer job.", appender.getFirstMessage()); + + appender.removeFromLogger(UserCountScanner.class); } } diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java index edf35068..eb4d790f 100644 --- a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java +++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java @@ -38,5 +38,7 @@ public class QuartzTimerClusterJobTest extends SpringTransactionalTestCase { assertEquals("There are 6 user in database, printed by quartz cluster job on node default.", appender.getFirstMessage()); + + appender.removeFromLogger(QuartzClusterableJob.class); } } diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java new file mode 100644 index 00000000..f96ce5b5 --- /dev/null +++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java @@ -0,0 +1,36 @@ +package org.springside.examples.showcase.modules.schedule; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springside.modules.test.category.UnStable; +import org.springside.modules.test.log.Log4jMockAppender; +import org.springside.modules.test.spring.SpringTransactionalTestCase; +import org.springside.modules.utils.Threads; + +@Category(UnStable.class) +@DirtiesContext +@ContextConfiguration(locations = { "/applicationContext.xml", "/schedule/applicationContext-spring-scheduler.xml" }) +@TransactionConfiguration(transactionManager = "defaultTransactionManager") +public class SpringTimerJobTest extends SpringTransactionalTestCase { + + @Test + public void scheduleJob() throws Exception { + + //加载测试用logger appender + Log4jMockAppender appender = new Log4jMockAppender(); + appender.addToLogger(UserCountScanner.class); + + //等待任务启动 + Threads.sleep(1000); + + //验证任务已执行 + assertEquals(1, appender.getLogsCount()); + assertEquals("There are 6 user in database, printed by spring timer job by xml.", appender.getFirstMessage()); + appender.removeFromLogger(UserCountScanner.class); + } +} diff --git a/modules/core/src/main/java/org/springside/modules/utils/Reflections.java b/modules/core/src/main/java/org/springside/modules/utils/Reflections.java index 8718e774..fb77a404 100644 --- a/modules/core/src/main/java/org/springside/modules/utils/Reflections.java +++ b/modules/core/src/main/java/org/springside/modules/utils/Reflections.java @@ -199,7 +199,7 @@ public class Reflections { } /** - // * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 */ public static void makeAccessible(Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier diff --git a/modules/core/src/main/java/org/springside/modules/utils/Threads.java b/modules/core/src/main/java/org/springside/modules/utils/Threads.java index 3d8d07bc..89d00c7a 100644 --- a/modules/core/src/main/java/org/springside/modules/utils/Threads.java +++ b/modules/core/src/main/java/org/springside/modules/utils/Threads.java @@ -41,6 +41,7 @@ public class Threads { * 按照ExecutorService JavaDoc示例代码编写的Graceful Shutdown方法. * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍人超時,則強制退出. * 另对在shutdown时线程本身被调用中断做了处理. */ public static void gracefulShutdown(ExecutorService pool, int shutdownTimeout, int shutdownNowTimeout, @@ -64,7 +65,7 @@ public class Threads { } /** - * 直接调用shutdownNow的方法, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 直接调用shutdownNow的方法, 有timeout控制.取消在workQueue中Pending的任务,并中断所有阻塞函数. */ public static void normalShutdown(ExecutorService pool, int timeout, TimeUnit timeUnit) { try { -- GitLab