How are Spring objects represented at runtime?

后端 未结 4 1045
无人及你
无人及你 2020-12-09 04:58

I have an app that uses the \"task:scheduler\" and \"task:scheduled-tasks\" elements (the latter containing \"task:scheduled\" elements). This is all working fine.

相关标签:
4条回答
  • 2020-12-09 05:29

    I have a snippet for pre spring 4.2 since it is still sitting at release candidate level.

    The scheduledFuture interface is implemented by every runnable element in the BlockingQueue.

            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)));
                }
    

    Heres a reflective way to get information about which job class is in the pool as threadPoolNamePrefix didn't return a distinct name for me:

    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");
        }
    
    0 讨论(0)
  • 2020-12-09 05:40

    Every Spring XML element has a corresponding BeanDefinitionReader. For <task:scheduled-tasks>, that's ScheduledTasksBeanDefinitionParser.

    This uses a BeanDefinitionBuilder to create a BeanDefinition for a bean of type ContextLifecycleScheduledTaskRegistrar. Your scheduled tasks will be stored in that bean.

    The tasks will be executing in either a default TaskScheduler or one you provided.

    I've given you the class names so you can look at the source code yourself if you need more fine grained details.

    0 讨论(0)
  • 2020-12-09 05:42

    With @Scheduled based configuration the approach from Tobias M’s answer does not work out-of-the-box.

    Instead of autowiring a ScheduledTaskRegistrar instance (which is not available for annotation based configuration), you can instead autowire a ScheduledTaskHolder which only has a getScheduledTasks() method.

    Background:

    The ScheduledAnnotationBeanPostProcessor used to manage @Scheduled tasks has an internal ScheduledTaskRegistrar that’s not available as a bean. It does implement ScheduledTaskHolder, though.

    0 讨论(0)
  • 2020-12-09 05:43

    This functionality will be available in Spring 4.2

    https://jira.spring.io/browse/SPR-12748 (Disclaimer: I reported this issue and contributed code towards its solution).

    // 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;
    }
    

    If you are using a ScheduledMethodRunnable defined in XML:

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

    You can access the underlying target object:

     ScheduledMethodRunnable scheduledMethodRunnable = (ScheduledMethodRunnable) task.getRunnable();
     TargetClass target = (TargetClass) scheduledMethodRunnable.getTarget();
    
    0 讨论(0)
提交回复
热议问题