领先一步
VMware 提供培训和认证,帮助您快速提升进度。
了解更多延续由Keith和Chris启动的 Spring 3.0“简化系列”,我想快速概述一下 Spring 3.0 中在调度和任务执行方面所实现的简化。
我将逐步介绍一个基本的示例应用程序,您可以从 spring-samples 的 Subversion 存储库中检出。它被设计得尽可能简单,同时展示了在 Spring 3.0 中使用基于注解和基于 XML 的方法来调度任务。
让我们从基于注解的方法开始。您可以通过 AnnotationDemo 中的 main() 方法直接运行它。如果您仔细查看,您会发现它只不过是 Spring ApplicationContext 的一个引导程序。
public static void main(String[] args) {
new ClassPathXmlApplicationContext("config.xml", AnnotationDemo.class);
}
无需其他操作的原因是 ApplicationContext 包含一个“活动”组件,我们稍后会看到。由于该组件的存在,main() 方法不会退出。config.xml 也非常简洁,只包含两个元素。
<context:component-scan base-package="org/springframework/samples/task/basic/annotation"/>
<task:annotation-driven/>
“component-scan”元素指向包含我们“bean”的包。其中有两个:ScheduledProcessor 和 AsyncWorker。我们稍后会查看它们,但首先请查看“annotation-driven”元素。这是 Spring 3.0 中的新元素,它驱动两个注解:@Scheduled 和 @Async。您可以分别使用“scheduler”和“executor”属性提供对 Spring TaskScheduler 和 TaskExecutor 的引用,但对于此示例,我们将仅依赖于默认值。
ScheduledProcessor 在一个方法上包含 @Scheduled 注解,因此它是上面提到的“活动”组件。由于配置中存在“annotation-driven”元素,因此此方法将向一个 Spring TaskScheduler 实例注册,该实例将定期以 30 秒的固定延迟执行该方法。
@Service
public class ScheduledProcessor implements Processor {
private final AtomicInteger counter = new AtomicInteger();
@Autowired
private Worker worker;
@Scheduled(fixedDelay = 30000)
public void process() {
System.out.println("processing next 10 at " + new Date());
for (int i = 0; i < 10; i++) {
worker.work(counter.incrementAndGet());
}
}
}
如您在前面的代码摘录中看到的,Worker 由 ScheduledProcessor 在循环中调用。但是,AsyncWorker 实现在其 work(..) 方法上包含 @Async 注解,并且由于配置中存在“annotation-driven”元素,因此它将被包装在一个代理中,以便该方法实际上由 TaskExecutor 实例调用。为了验证这一点,当前线程名称在该方法中显示。同样,为了明确工作正在并发执行,sleep(..) 调用被用来模拟耗时的工作。
@Component
public class AsyncWorker implements Worker {
@Async
public void work(int i) {
String threadName = Thread.currentThread().getName();
System.out.println(" " + threadName + " beginning work on " + i);
try {
Thread.sleep(5000); // simulates work
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(" " + threadName + " completed work on " + i);
}
}
如果您运行 AnnotationDemo main() 方法,输出应该类似于以下内容: processing next 10 at Mon Jan 04 18:20:52 EST 2010 SimpleAsyncTaskExecutor-1 beginning work on 1 SimpleAsyncTaskExecutor-2 beginning work on 2 SimpleAsyncTaskExecutor-3 beginning work on 3 SimpleAsyncTaskExecutor-5 beginning work on 5 SimpleAsyncTaskExecutor-4 beginning work on 4 SimpleAsyncTaskExecutor-6 beginning work on 6 SimpleAsyncTaskExecutor-7 beginning work on 7 SimpleAsyncTaskExecutor-8 beginning work on 8 SimpleAsyncTaskExecutor-9 beginning work on 9 SimpleAsyncTaskExecutor-10 beginning work on 10 SimpleAsyncTaskExecutor-1 completed work on 1 SimpleAsyncTaskExecutor-2 completed work on 2 SimpleAsyncTaskExecutor-3 completed work on 3 SimpleAsyncTaskExecutor-5 completed work on 5 SimpleAsyncTaskExecutor-6 completed work on 6 SimpleAsyncTaskExecutor-7 completed work on 7 SimpleAsyncTaskExecutor-8 completed work on 8 SimpleAsyncTaskExecutor-4 completed work on 4 SimpleAsyncTaskExecutor-10 completed work on 10 SimpleAsyncTaskExecutor-9 completed work on 9
关于该输出需要注意一些事项。首先,每次处理 10 行数据将每 30 秒重复一次(由于 @Scheduled)。其次,工作项由不同的线程并发处理(由于 @Async)。在最后一个“beginning work”消息和第一个“completed work”消息之间应该有大约 5 秒的暂停。如果所有工作程序都在单个线程中运行,我们将会看到连续的 beginning/completed 对,并且整个过程将花费大约 50 秒。当然,时间方面的内容无法在此博客文章中体现,因此您确实应该下载并自己运行该示例以获得完整的效果(该项目可以直接导入到SpringSource Tool Suite或其他具有 Maven 支持的基于 Eclipse 的环境中)。
最后我想展示的是 @Scheduled 注解的基于 XML 的替代方案。该示例包含另一个类 SimpleProcessor,它在其 process() 方法上不包含 @Scheduled。
@Service
public class SimpleProcessor implements Processor {
private final AtomicInteger counter = new AtomicInteger();
public void process() {
System.out.println("processing next 10 at " + new Date());
for (int i = 0; i < 10; i++) {
System.out.println(" processing " + counter.incrementAndGet());
}
}
}
XML 比基于注解的版本略微冗长一些,因为 Spring 3.0 现在提供了一个“task”命名空间来使配置简洁。
<context:component-scan base-package="org/springframework/samples/task/basic/xml"/>
<task:scheduled-tasks>
<task:scheduled ref="simpleProcessor" method="process" cron="3/10 * * * * ?"/>
</task:scheduled-tasks>
如果您运行 XmlDemo 中的 main() 方法,您将看到 process 每 10 秒执行一次。为了多样性,此示例使用 cron 表达式而不是简单的固定延迟。因此,您会注意到时间基于 3 秒的偏移量(:13、:23 等),但当然 cron 表达式可以比这强大得多(我只是不想创建一个仅在某些日期或时间运行的示例)。值得指出的是,对基于 cron 的调度的支持直接包含在 Spring 3.0 本身中。
请务必查看 Spring 3.0参考手册的任务执行和调度章节,以了解有关新的 TaskScheduler 抽象和 Trigger 策略的更多信息,这些策略为这里您所看到的内容提供了基础。参考手册还讨论了“task”命名空间提供的其他元素,用于使用特定的线程池设置配置 TaskScheduler 和 TaskExecutor 实例。
希望这篇文章对这些新功能提供了一个有用的概述。敬请关注 SpringSource 团队博客,获取更多与 Spring 3.0 相关的內容。