如何在运行时表示Spring <task:scheduled>对象?</task:scheduled>

时间:2014-02-15 00:29:50

标签: spring

我有一个使用“task:scheduler”和“task:scheduled-tasks”元素的应用程序(后者包含“task:scheduled”元素)。这一切都很好。

我正在尝试编写一些内省“应用程序配置”的代码,以获得一些重要信息的简短摘要,例如安排的任务以及他们的日程安排。

我已经有一个有一堆“@Autowired”实例变量的类,所以我可以遍历所有这些。很容易添加“List”来获取所有TaskScheduler对象。我只有两个,我在每个任务中都有一组不同的计划任务。

我在那些TaskScheduler对象中看不到的东西(它们实际上是ThreadPoolTask​​Scheduler对象)看起来像是一个计划任务列表,所以我猜测计划任务列表会记录在其他地方。

我可以使用哪些对象来反省计划任务集以及它们所在的线程池?

4 个答案:

答案 0 :(得分:14)

此功能将在Spring 4.2中提供

https://jira.spring.io/browse/SPR-12748(免责声明:我报告了此问题并为其解决方案提供了代码)。

// Warning there may be more than one ScheduledTaskRegistrar in your
// application context. If this is the case you can autowire a list of 
// ScheduledTaskRegistrar instead.
@Autowired   
private ScheduledTaskRegistrar scheduledTaskRegistrar; 

public List<Task> getScheduledTasks() {
    List<Task> result = new ArrayList<Task>();
    result.addAll(this.scheduledTaskRegistrar.getTriggerTaskList());
    result.addAll(this.scheduledTaskRegistrar.getCronTaskList());
    result.addAll(this.scheduledTaskRegistrar.getFixedRateTaskList());
    result.addAll(this.scheduledTaskRegistrar.getFixedDelayTaskList());
    return result;
}

// You can this inspect the tasks, 
// so for example a cron task can be inspected like this:

public List<CronTask> getScheduledCronTasks() {
    List<CronTask> cronTaskList = this.scheduledTaskRegistrar.getCronTaskList();
    for (CronTask cronTask : cronTaskList) {
        System.out.println(cronTask.getExpression);
    }
    return cronTaskList;
}

如果您使用的是XML中定义的ScheduledMethodRunnable:

<task:scheduled method="run" cron="0 0 12 * * ?" ref="MyObject" />

您可以访问基础目标对象:

 ScheduledMethodRunnable scheduledMethodRunnable = (ScheduledMethodRunnable) task.getRunnable();
 TargetClass target = (TargetClass) scheduledMethodRunnable.getTarget();

答案 1 :(得分:3)

我有一个前弹簧4.2的片段,因为它仍然处于发布候选级别。

scheduledFuture接口由BlockingQueue中的每个runnable元素实现。

        Map<String, ThreadPoolTaskScheduler> schedulers = applicationContext
                .getBeansOfType(ThreadPoolTaskScheduler.class);
        for (ThreadPoolTaskScheduler scheduler : schedulers.values()) {
            ScheduledExecutorService exec = scheduler.getScheduledExecutor();
            ScheduledThreadPoolExecutor poolExec = scheduler
                    .getScheduledThreadPoolExecutor();
            BlockingQueue<Runnable> queue = poolExec.getQueue();
            Iterator<Runnable> iter = queue.iterator();
            while (iter.hasNext()) {
                ScheduledFuture<?> future = (ScheduledFuture<?>) iter.next();
                future.getDelay(TimeUnit.MINUTES);
            Runnable job = iter.next();
            logger.debug(MessageFormat.format(":: Task Class is {0}", JobDiscoverer.findRealTask(job)));
            }

由于 threadPoolNamePrefix 没有为我返回一个不同的名称,因此有一种反射方式可以获取有关哪个工作类在池中的信息:

public class JobDiscoverer {
    private final static Field syncInFutureTask;
    private final static Field callableInFutureTask;
    private static final Class<? extends Callable> adapterClass;
    private static final Field runnableInAdapter;
    private static Field reschedulingRunnable;
    private static Field targetScheduledMethod;

    static {
        try {

            reschedulingRunnable = Class
                    .forName(
                            "org.springframework.scheduling.support.DelegatingErrorHandlingRunnable")
                    .getDeclaredField("delegate");
            reschedulingRunnable.setAccessible(true);

            targetScheduledMethod = Class
                    .forName(
                            "org.springframework.scheduling.support.ScheduledMethodRunnable")
                    .getDeclaredField("target");
            targetScheduledMethod.setAccessible(true);
            callableInFutureTask = Class.forName(
                    "java.util.concurrent.FutureTask$Sync").getDeclaredField(
                    "callable");
            callableInFutureTask.setAccessible(true);
            syncInFutureTask = FutureTask.class.getDeclaredField("sync");
            syncInFutureTask.setAccessible(true);

            adapterClass = Executors.callable(new Runnable() {
                public void run() {
                }
            }).getClass();
            runnableInAdapter = adapterClass.getDeclaredField("task");
            runnableInAdapter.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new ExceptionInInitializerError(e);
        } catch (SecurityException e) {
            throw new PiaRuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new PiaRuntimeException(e);
        }
    }

    public static Object findRealTask(Runnable task) {
        if (task instanceof FutureTask) {
            try {
                Object syncAble = syncInFutureTask.get(task);
                Object callable = callableInFutureTask.get(syncAble);
                if (adapterClass.isInstance(callable)) {
                    Object reschedulable = runnableInAdapter.get(callable);
                    Object targetable = reschedulingRunnable.get(reschedulable);
                    return targetScheduledMethod.get(targetable);
                } else {
                    return callable;
                }
            } catch (IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
        throw new ClassCastException("Not a FutureTask");
    }

答案 2 :(得分:1)

使用基于@Scheduled的配置,Tobias M的答案无法立即使用。

您可以自动装配仅具有ScheduledTaskRegistrar方法的ScheduledTaskHolder来代替自动装配getScheduledTasks()实例(不适用于基于注释的配置)。

背景:

用于管理ScheduledAnnotationBeanPostProcessor任务的@Scheduled有一个内部ScheduledTaskRegistrar,不能作为bean使用。不过,它确实实现了ScheduledTaskHolder

答案 3 :(得分:0)

每个Spring XML元素都有一个对应的BeanDefinitionReader。对于<task:scheduled-tasks>,那是ScheduledTasksBeanDefinitionParser

这使用BeanDefinitionBuilderContextLifecycleScheduledTaskRegistrar类型的bean创建BeanDefinition。您计划的任务将存储在该bean中。

任务将以默认TaskScheduler或您提供的任务执行。

我已经为您提供了类名,因此如果您需要更精细的细节,可以自己查看源代码。