Quartzy Scheduler源码解析

本文深入剖析了Quartz框架的初始化过程,包括Scheduler的创建、Trigger和Job的创建及调度,以及Scheduler的启动。Quartz通过SchedulerFactory创建Scheduler,JobStore存储Job和Trigger,Trigger定义调度规则,Scheduler启动后按照Trigger执行Job。文章还展示了Quartz源码的关键部分,如Scheduler、Trigger和Job的创建,以及调度器的启动流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Quartz介绍

Quartz是一个完全由 Java 编写的开源作业调度框架

官网:Quartz Enterprise Job Scheduler

下载:Downloads

API手册:Quartz Enterprise Job Scheduler 2.2.2 API

GitHub:https://github.com/quartz-scheduler/quartz/releases/tag/v2.3.2

Quartz简单使用

public class ScheduleTest {
    
    public static void main(String[] args) {
        try {
            //1.初始化调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.getContext().put("skey", "svalue");
            //2.创建触发器
            SimpleTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger1", "group1")
                    .usingJobData("t1", "tv1")
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(2).repeatForever())
                    .build();
            trigger.getJobDataMap().put("t2", "tv2");
            //3.创建Job
            JobDetail job = JobBuilder.newJob(MyJob.class).usingJobData("j1", "jv1")
                    .withIdentity("myjob", "mygroup")
                    .build();
            job.getJobDataMap().put("j2", "jv2");
            //4.调度器启动
            scheduler.scheduleJob(job, trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("调度任务测试...");
        Object tv1 = context.getTrigger().getJobDataMap().get("t1");
        Object tv2 = context.getTrigger().getJobDataMap().get("t2");
        Object jv1 = context.getJobDetail().getJobDataMap().get("j1");
        Object jv2 = context.getJobDetail().getJobDataMap().get("j2");
        Object skey = null;
        try {
            skey = context.getScheduler().getContext().get("skey");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        System.out.println(tv1 + ":" + tv2);
        System.out.println(jv1 + ":" + jv2);
        System.out.println("skey:" + skey);
        System.out.println("调度时间--" + LocalDateTime.now());
    }
}

Quartz源码解析

类图:

 

  1. 创建Scheduler

    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); 等价于Scheduler scheduler = new StdSchedulerFactory().getScheduler();

    ​
        public Scheduler getScheduler() throws SchedulerException {
            /**
             * cfg即PropertiesParse对象,如果配置文件没被加载,加载配置文件,将其读取到Properties类,然后创建PropertiesParser类,并传入                   * Properties将其初始化,即最终生成了一个包含quartz.properties文件配置属性的PropertiesParse对象
             */
            if (cfg == null) {
                initialize();
            }
            /**
            * 获取一个SchedulerRepository调度器仓库实例,判断名称为"QuartzScheduler"调度器是否存在,存在且非shutdown状态的话,返回仓库中的调度器;
            * 不存在的话,将"QuartzScheduler"放入repository仓库
            */
            SchedulerRepository schedRep = SchedulerRepository.getInstance();
    ​
            Scheduler sched = schedRep.lookup(getSchedulerName());
    ​
            if (sched != null) {
                if (sched.isShutdown()) {
                    schedRep.remove(getSchedulerName());
                } else {
                    return sched;
                }
            }
            //根据PropertiesParser(quartz.properties)属性初始化scheduler,并将scheduler放入ScheudlerRepository仓库
            sched = instantiate();
    ​
            return sched;
        }

    initialize():配置文件没有被加载或PropertiesParser为空的话,会依次根据系统变量、工程classpath、quartz包路径的顺序查找quartz.properties文件

        public void initialize() throws SchedulerException {
            // short-circuit if already initialized
            if (cfg != null) {
                return;
            }
            if (initException != null) {
                throw initException;
            }
    
            String requestedFile = System.getProperty(PROPERTIES_FILE);
            String propFileName = requestedFile != null ? requestedFile
                    : "quartz.properties";
            File propFile = new File(propFileName);
    
            Properties props = new Properties();
    
            InputStream in = null;
    
            try {
                if (propFile.exists()) {
                    try {
                        if (requestedFile != null) {
                            propSrc = "specified file: '" + requestedFile + "'";
                        } else {
                            propSrc = "default file in current working dir: 'quartz.properties'";
                        }
    
                        in = new BufferedInputStream(new FileInputStream(propFileName));
                        props.load(in);
    
                    } catch (IOException ioe) {
                        initException = new SchedulerException("Properties file: '"
                                + propFileName + "' could not be read.", ioe);
                        throw initException;
                    }
                } else if (requestedFile != null) {
                    in =
                        Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile);
    
                    if(in == null) {
                        initException = new SchedulerException("Properties file: '"
                            + requestedFile + "' could not be found.");
                        throw initException;
                    }
    
                    propSrc = "specified file: '" + requestedFile + "' in the class resource path.";
    
                    in = new BufferedInputStream(in);
                    try {
                        props.load(in);
                    } catch (IOException ioe) {
                        initException = new SchedulerException("Properties file: '"
                                + requestedFile + "' could not be read.", ioe);
                        throw initException;
                    }
    
                } else {
                    propSrc = "default resource file in Quartz package: 'quartz.properties'";
    
                    ClassLoader cl = getClass().getClassLoader();
                    if(cl == null)
                        cl = findClassloader();
                    if(cl == null)
                        throw new SchedulerConfigException("Unable to find a class loader on the current thread or class.");
    
                    in = cl.getResourceAsStream(
                            "quartz.properties");
    
                    if (in == null) {
                        in = cl.getResourceAsStream(
                                "/quartz.properties");
                    }
                    if (in == null) {
                        in = cl.getResourceAsStream(
                                "org/quartz/quartz.properties");
                    }
                    if (in == null) {
                        initException = new SchedulerException(
                                "Default quartz.properties not found in class path");
                        throw initException;
                    }
                    try {
                        props.load(in);
                    } catch (IOException ioe) {
                        initException = new SchedulerException(
                                "Resource properties file: 'org/quartz/quartz.properties' "
                                        + "could not be read from the classpath.", ioe);
                        throw initException;
                    }
                }
            } finally {
                if(in != null) {
                    try { in.close(); } catch(IOException ignore) { /* ignore */ }
                }
            }
    
            initialize(overrideWithSysProps(props, getLog()));
        }

    instantiate():

    private Scheduler instantiate() throws SchedulerException {
            if (cfg == null) {
                initialize();
            }
    
            if (initException != null) {
                throw initException;
            }
    		
            JobStore js = null;
            ThreadPool tp = null;
            QuartzScheduler qs = null;
            DBConnectionManager dbMgr = null;
            String instanceIdGeneratorClass = null;
            Properties tProps = null;
            String userTXLocation = null;
            boolean wrapJobInTx = false;
            boolean autoId = false;
            long idleWaitTime = -1;
            long dbFailureRetry = 15000L; // 15 secs
            String classLoadHelperClass;
            String jobFactoryClass;
            ThreadExecutor threadExecutor;
    
    
            SchedulerRepository schedRep = SchedulerRepository.getInstance();
    
            // Get Scheduler Properties
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
            String schedName = cfg.getStringProperty(PROP_SCHED_INSTANCE_NAME,
                    "QuartzScheduler");
    
            String threadName = cfg.getStringProperty(PROP_SCHED_THREAD_NAME,
                    schedName + "_QuartzSchedulerThread");
    
        	// schedInstId生成,如果配置成自动生成,则获取自动生成器去生成;如果配置成系统属性生成,则用相应的类生成
            String schedInstId = cfg.getStringProperty(PROP_SCHED_INSTANCE_ID,
                    DEFAULT_INSTANCE_ID);
        
            if (schedInstId.equals(AUTO_GENERATE_INSTANCE_ID)) {
                autoId = true;
                instanceIdGeneratorClass = cfg.getStringProperty(
                        PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS,
                        "org.quartz.simpl.SimpleInstanceIdGenerator");
            }
            else if (schedInstId.equals(SYSTEM_PROPERTY_AS_INSTANCE_ID)) {
                autoId = true;
                instanceIdGeneratorClass = 
                        "org.quartz.simpl.SystemPropertyInstanceIdGenerator";
            }
    		//获取UserTansaction的Jndi url
            userTXLocation = cfg.getStringProperty(PROP_SCHED_USER_TX_URL,
                    userTXLocation);
            if (userTXLocation != null && userTXLocation.trim().length() == 0) {
                userTXLocation = null;
            }
    
            classLoadHelperClass = cfg.getStringProperty(
                    PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
                    "org.quartz.simpl.CascadingClassLoadHelper");
       		//是否用UserTansaction来执行Job
            wrapJobInTx = cfg.getBooleanProperty(PROP_SCHED_WRAP_JOB_IN_USER_TX,
                    wrapJobInTx);
    
            jobFactoryClass = cfg.getStringProperty(
                    PROP_SCHED_JOB_FACTORY_CLASS, null);
    
            idleWaitTime = cfg.getLongProperty(PROP_SCHED_IDLE_WAIT_TIME,
                    idleWaitTime);
            if(idleWaitTime > -1 && idleWaitTime < 1000) {
                throw new SchedulerException("org.quartz.scheduler.idleWaitTime of less than 1000ms is not legal.");
            }
            
            dbFailureRetry = cfg.getLongProperty(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry);
            if (dbFailureRetry < 0) {
                throw new SchedulerException(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL + " of less than 0 ms is not legal.");
            }
    
            boolean makeSchedulerThreadDaemon =
                cfg.getBooleanProperty(PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON);
    
            boolean threadsInheritInitalizersClassLoader =
                cfg.getBooleanProperty(PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD);
    
            long batchTimeWindow = cfg.getLongProperty(PROP_SCHED_BATCH_TIME_WINDOW, 0L);
            int maxBatchSize = cfg.getIntProperty(PROP_SCHED_MAX_BATCH_SIZE, 1);
    
            boolean interruptJobsOnShutdown = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN, false);
            boolean interruptJobsOnShutdownWithWait = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT, false);
    		//JMX相关配置
            boolean jmxExport = cfg.getBooleanProperty(PROP_SCHED_JMX_EXPORT);
            String jmxObjectName = cfg.getStringProperty(PROP_SCHED_JMX_OBJECT_NAME);
            
            boolean jmxProxy = cfg.getBooleanProperty(PROP_SCHED_JMX_PROXY);
            String jmxProxyClass = cfg.getStringProperty(PROP_SCHED_JMX_PROXY_CLASS);
    		//RMI相关配置
            boolean rmiExport = cfg.getBooleanProperty(PROP_SCHED_RMI_EXPORT, false);
            boolean rmiProxy = cfg.getBooleanProperty(PROP_SCHED_RMI_PROXY, false);
            String rmiHost = cfg.getStringProperty(PROP_SCHED_RMI_HOST, "localhost");
            int rmiPort = cfg.getIntProperty(PROP_SCHED_RMI_PORT, 1099);
            int rmiServerPort = cfg.getIntProperty(PROP_SCHED_RMI_SERVER_PORT, -1);
            String rmiCreateRegistry = cfg.getStringProperty(
                    PROP_SCHED_RMI_CREATE_REGISTRY,
                    QuartzSchedulerResources.CREATE_REGISTRY_NEVER);
            String rmiBindName = cfg.getStringProperty(PROP_SCHED_RMI_BIND_NAME);
    		//JMX跟RMI不能同时开启
            if (jmxProxy && rmiProxy) {
                throw new SchedulerConfigException("Cannot proxy both RMI and JMX.");
            }
            
            boolean managementRESTServiceEnabled = cfg.getBooleanProperty(MANAGEMENT_REST_SERVICE_ENABLED, false);
            String managementRESTServiceHostAndPort = cfg.getStringProperty(MANAGEMENT_REST_SERVICE_HOST_PORT, "0.0.0.0:9889");
    
            Properties schedCtxtProps = cfg.getPropertyGroup(PROP_SCHED_CONTEXT_PREFIX, true);
    
            // If Proxying to remote scheduler, short-circuit here...
            // ~~~~~~~~~~~~~~~~~~
            //如果是RMI代理,则创建RemoteScheduler
            if (rmiProxy) {
    
                if (autoId) {
                    schedInstId = DEFAULT_INSTANCE_ID;
                }
    
                String uid = (rmiBindName == null) ? QuartzSchedulerResources.getUniqueIdentifier(
                        schedName, schedInstId) : rmiBindName;
    
                RemoteScheduler remoteScheduler = new RemoteScheduler(uid, rmiHost, rmiPort);
    
                schedRep.bind(remoteScheduler);
    
                return remoteScheduler;
            }
    
    
            // Create class load helper
            ClassLoadHelper loadHelper = null;
            try {
                loadHelper = (ClassLoadHelper) loadClass(classLoadHelperClass)
                        .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate class load helper class: "
                                + e.getMessage(), e);
            }
            loadHelper.initialize();
    
            // If Proxying to remote JMX scheduler, short-circuit here...
            // ~~~~~~~~~~~~~~~~~~
        	//如果是JMX代理,则创建RemoteMBeanScheduler
            if (jmxProxy) {
                if (autoId) {
                    schedInstId = DEFAULT_INSTANCE_ID;
                }
    
                if (jmxProxyClass == null) {
                    throw new SchedulerConfigException("No JMX Proxy Scheduler class provided");
                }
    
                RemoteMBeanScheduler jmxScheduler = null;
                try {
                    jmxScheduler = (RemoteMBeanScheduler)loadHelper.loadClass(jmxProxyClass)
                            .newInstance();
                } catch (Exception e) {
                    throw new SchedulerConfigException(
                            "Unable to instantiate RemoteMBeanScheduler class.", e);
                }
    
                if (jmxObjectName == null) {
                    jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId);
                }
    
                jmxScheduler.setSchedulerObjectName(jmxObjectName);
    
                tProps = cfg.getPropertyGroup(PROP_SCHED_JMX_PROXY, true);
                try {
                    setBeanProps(jmxScheduler, tProps);
                } catch (Exception e) {
                    initException = new SchedulerException("RemoteMBeanScheduler class '"
                            + jmxProxyClass + "' props could not be configured.", e);
                    throw initException;
                }
    
                jmxScheduler.initialize();
    
                schedRep.bind(jmxScheduler);
    
                return jmxScheduler;
            }
    
            //初始化jobFactory
            JobFactory jobFactory = null;
            if(jobFactoryClass != null) {
                try {
                    jobFactory = (JobFactory) loadHelper.loadClass(jobFactoryClass)
                            .newInstance();
                } catch (Exception e) {
                    throw new SchedulerConfigException(
                            "Unable to instantiate JobFactory class: "
                                    + e.getMessage(), e);
                }
    
                tProps = cfg.getPropertyGroup(PROP_SCHED_JOB_FACTORY_PREFIX, true);
                try {
                    setBeanProps(jobFactory, tProps);
                } catch (Exception e) {
                    initException = new SchedulerException("JobFactory class '"
                            + jobFactoryClass + "' props could not be configured.", e);
                    throw initException;
                }
            }
    
            InstanceIdGenerator instanceIdGenerator = null;
            if(instanceIdGeneratorClass != null) {
                try {
                    instanceIdGenerator = (InstanceIdGenerator) loadHelper.loadClass(instanceIdGeneratorClass)
                        .newInstance();
                } catch (Exception e) {
                    throw new SchedulerConfigException(
                            "Unable to instantiate InstanceIdGenerator class: "
                            + e.getMessage(), e);
                }
    
                tProps = cfg.getPropertyGroup(PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX, true);
                try {
                    setBeanProps(instanceIdGenerator, tProps);
                } catch (Exception e) {
                    initException = new SchedulerException("InstanceIdGenerator class '"
                            + instanceIdGeneratorClass + "' props could not be configured.", e);
                    throw initException;
                }
            }
    
            // Get ThreadPool Properties
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    		
        	//初始化ThreadPool
            String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
    
            if (tpClass == null) {
                initException = new SchedulerException(
                        "ThreadPool class not specified. ");
                throw initException;
            }
    
            try {
                tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException("ThreadPool class '"
                        + tpClass + "' could not be instantiated.", e);
                throw initException;
            }
            tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
            try {
                setBeanProps(tp, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("ThreadPool class '"
                        + tpClass + "' props could not be configured.", e);
                throw initException;
            }
    
            // Get JobStore Properties
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        	//初始化JobStore
            String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
                    RAMJobStore.class.getName());
    
            if (jsClass == null) {
                initException = new SchedulerException(
                        "JobStore class not specified. ");
                throw initException;
            }
    
            try {
                js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException("JobStore class '" + jsClass
                        + "' could not be instantiated.", e);
                throw initException;
            }
    
            SchedulerDetailsSetter.setDetails(js, schedName, schedInstId);
    
            tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
            try {
                setBeanProps(js, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("JobStore class '" + jsClass
                        + "' props could not be configured.", e);
                throw initException;
            }
    		//如果是JobStoreSupport的话,需要设置锁控制器,用于集群的调度同步
            if (js instanceof JobStoreSupport) {
                // Install custom lock handler (Semaphore)
                String lockHandlerClass = cfg.getStringProperty(PROP_JOB_STORE_LOCK_HANDLER_CLASS);
                if (lockHandlerClass != null) {
                    try {
                        Semaphore lockHandler = (Semaphore)loadHelper.loadClass(lockHandlerClass).newInstance();
    
                        tProps = cfg.getPropertyGroup(PROP_JOB_STORE_LOCK_HANDLER_PREFIX, true);
    
                        // If this lock handler requires the table prefix, add it to its properties.
                        if (lockHandler instanceof TablePrefixAware) {
                            tProps.setProperty(
                                    PROP_TABLE_PREFIX, ((JobStoreSupport)js).getTablePrefix());
                            tProps.setProperty(
                                    PROP_SCHED_NAME, schedName);
                        }
    
                        try {
                            setBeanProps(lockHandler, tProps);
                        } catch (Exception e) {
                            initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                                    + "' props could not be configured.", e);
                            throw initException;
                        }
    
                        ((JobStoreSupport)js).setLockHandler(lockHandler);
                        getLog().info("Using custom data access locking (synchronization): " + lockHandlerClass);
                    } catch (Exception e) {
                        initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                                + "' could not be instantiated.", e);
                        throw initException;
                    }
                }
            }
    
            // Set up any DataSources
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    		
        	//获取dataSource
            String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);
            for (int i = 0; i < dsNames.length; i++) {
                PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(
                        PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));
     
                //datasource被封装为ConnectionProvider
                String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);
    
                // custom connectionProvider...
                if(cpClass != null) {
                    ConnectionProvider cp = null;
                    try {
                        cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
                    } catch (Exception e) {
                        initException = new SchedulerException("ConnectionProvider class '" + cpClass
                                + "' could not be instantiated.", e);
                        throw initException;
                    }
    
                    try {
                        // remove the class name, so it isn't attempted to be set
                        pp.getUnderlyingProperties().remove(
                                PROP_CONNECTION_PROVIDER_CLASS);
    
                        if (cp instanceof PoolingConnectionProvider) {
                            populateProviderWithExtraProps((PoolingConnectionProvider)cp, pp.getUnderlyingProperties());
                        } else {
                            setBeanProps(cp, pp.getUnderlyingProperties());
                        }
                        cp.initialize();
                    } catch (Exception e) {
                        initException = new SchedulerException("ConnectionProvider class '" + cpClass
                                + "' props could not be configured.", e);
                        throw initException;
                    }
    
                    dbMgr = DBConnectionManager.getInstance();
                    dbMgr.addConnectionProvider(dsNames[i], cp);
                } else {
                    //JNDI的配置
                    String dsJndi = pp.getStringProperty(PROP_DATASOURCE_JNDI_URL, null);
    
                    if (dsJndi != null) {
                        boolean dsAlwaysLookup = pp.getBooleanProperty(
                                PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP);
                        String dsJndiInitial = pp.getStringProperty(
                                PROP_DATASOURCE_JNDI_INITIAL);
                        String dsJndiProvider = pp.getStringProperty(
                                PROP_DATASOURCE_JNDI_PROVDER);
                        String dsJndiPrincipal = pp.getStringProperty(
                                PROP_DATASOURCE_JNDI_PRINCIPAL);
                        String dsJndiCredentials = pp.getStringProperty(
                                PROP_DATASOURCE_JNDI_CREDENTIALS);
                        Properties props = null;
                        if (null != dsJndiInitial || null != dsJndiProvider
                                || null != dsJndiPrincipal || null != dsJndiCredentials) {
                            props = new Properties();
                            if (dsJndiInitial != null) {
                                props.put(PROP_DATASOURCE_JNDI_INITIAL,
                                        dsJndiInitial);
                            }
                            if (dsJndiProvider != null) {
                                props.put(PROP_DATASOURCE_JNDI_PROVDER,
                                        dsJndiProvider);
                            }
                            if (dsJndiPrincipal != null) {
                                props.put(PROP_DATASOURCE_JNDI_PRINCIPAL,
                                        dsJndiPrincipal);
                            }
                            if (dsJndiCredentials != null) {
                                props.put(PROP_DATASOURCE_JNDI_CREDENTIALS,
                                        dsJndiCredentials);
                            }
                        }
                        JNDIConnectionProvider cp = new JNDIConnectionProvider(dsJndi,
                                props, dsAlwaysLookup);
                        dbMgr = DBConnectionManager.getInstance();
                        dbMgr.addConnectionProvider(dsNames[i], cp);
                    } else {
                        String poolingProvider = pp.getStringProperty(PoolingConnectionProvider.POOLING_PROVIDER);
                        //本地driver配置
                        String dsDriver = pp.getStringProperty(PoolingConnectionProvider.DB_DRIVER);
                        String dsURL = pp.getStringProperty(PoolingConnectionProvider.DB_URL);
    
                        if (dsDriver == null) {
                            initException = new SchedulerException(
                                    "Driver not specified for DataSource: "
                                            + dsNames[i]);
                            throw initException;
                        }
                        if (dsURL == null) {
                            initException = new SchedulerException(
                                    "DB URL not specified for DataSource: "
                                            + dsNames[i]);
                            throw initException;
                        }
                        // we load even these "core" providers by class name in order to avoid a static dependency on
                        // the c3p0 and hikaricp libraries
                        if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) {
                            cpClass = "org.quartz.utils.HikariCpPoolingConnectionProvider";
                        }
                        else {
                            cpClass = "org.quartz.utils.C3p0PoolingConnectionProvider";
                        }
                        log.info("Using ConnectionProvider class '" + cpClass + "' for data source '" + dsNames[i] + "'");
    
                        try {
                            ConnectionProvider cp = null;
                            try {
                                Constructor constructor = loadHelper.loadClass(cpClass).getConstructor(Properties.class);
                                cp = (ConnectionProvider) constructor.newInstance(pp.getUnderlyingProperties());
                            } catch (Exception e) {
                                initException = new SchedulerException("ConnectionProvider class '" + cpClass
                                        + "' could not be instantiated.", e);
                                throw initException;
                            }
                            dbMgr = DBConnectionManager.getInstance();
                            dbMgr.addConnectionProvider(dsNames[i], cp);
    
                            // Populate the underlying C3P0/HikariCP data source pool properties
                            populateProviderWithExtraProps((PoolingConnectionProvider)cp, pp.getUnderlyingProperties());
                        } catch (Exception sqle) {
                            initException = new SchedulerException(
                                    "Could not initialize DataSource: " + dsNames[i],
                                    sqle);
                            throw initException;
                        }
                    }
    
                }
    
            }
    
            // Set up any SchedulerPlugins
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    		
        	//SchedulerPlugin初始化
            String[] pluginNames = cfg.getPropertyGroups(PROP_PLUGIN_PREFIX);
            SchedulerPlugin[] plugins = new SchedulerPlugin[pluginNames.length];
            for (int i = 0; i < pluginNames.length; i++) {
                Properties pp = cfg.getPropertyGroup(PROP_PLUGIN_PREFIX + "."
                        + pluginNames[i], true);
    
                String plugInClass = pp.getProperty(PROP_PLUGIN_CLASS, null);
    
                if (plugInClass == null) {
                    initException = new SchedulerException(
                            "SchedulerPlugin class not specified for plugin '"
                                    + pluginNames[i] + "'");
                    throw initException;
                }
                SchedulerPlugin plugin = null;
                try {
                    plugin = (SchedulerPlugin)
                            loadHelper.loadClass(plugInClass).newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "SchedulerPlugin class '" + plugInClass
                                    + "' could not be instantiated.", e);
                    throw initException;
                }
                try {
                    setBeanProps(plugin, pp);
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "JobStore SchedulerPlugin '" + plugInClass
                                    + "' props could not be configured.", e);
                    throw initException;
                }
    
                plugins[i] = plugin;
            }
    
            // Set up any JobListeners
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    		
        	//JobListener初始化
            Class<?>[] strArg = new Class[] { String.class };
            String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX);
            JobListener[] jobListeners = new JobListener[jobListenerNames.length];
            for (int i = 0; i < jobListenerNames.length; i++) {
                Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "."
                        + jobListenerNames[i], true);
    
                String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);
    
                if (listenerClass == null) {
                    initException = new SchedulerException(
                            "JobListener class not specified for listener '"
                                    + jobListenerNames[i] + "'");
                    throw initException;
                }
                JobListener listener = null;
                try {
                    listener = (JobListener)
                           loadHelper.loadClass(listenerClass).newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "JobListener class '" + listenerClass
                                    + "' could not be instantiated.", e);
                    throw initException;
                }
                try {
                    Method nameSetter = null;
                    try { 
                        nameSetter = listener.getClass().getMethod("setName", strArg);
                    }
                    catch(NoSuchMethodException ignore) { 
                        /* do nothing */ 
                    }
                    if(nameSetter != null) {
                        nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } );
                    }
                    setBeanProps(listener, lp);
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "JobListener '" + listenerClass
                                    + "' props could not be configured.", e);
                    throw initException;
                }
                jobListeners[i] = listener;
            }
    
            // Set up any TriggerListeners
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        	//TriggerListener初始化
            String[] triggerListenerNames = cfg.getPropertyGroups(PROP_TRIGGER_LISTENER_PREFIX);
            TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length];
            for (int i = 0; i < triggerListenerNames.length; i++) {
                Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "."
                        + triggerListenerNames[i], true);
    
                String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);
    
                if (listenerClass == null) {
                    initException = new SchedulerException(
                            "TriggerListener class not specified for listener '"
                                    + triggerListenerNames[i] + "'");
                    throw initException;
                }
                TriggerListener listener = null;
                try {
                    listener = (TriggerListener)
                           loadHelper.loadClass(listenerClass).newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "TriggerListener class '" + listenerClass
                                    + "' could not be instantiated.", e);
                    throw initException;
                }
                try {
                    Method nameSetter = null;
                    try { 
                        nameSetter = listener.getClass().getMethod("setName", strArg);
                    }
                    catch(NoSuchMethodException ignore) { /* do nothing */ }
                    if(nameSetter != null) {
                        nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } );
                    }
                    setBeanProps(listener, lp);
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "TriggerListener '" + listenerClass
                                    + "' props could not be configured.", e);
                    throw initException;
                }
                triggerListeners[i] = listener;
            }
    
            boolean tpInited = false;
            boolean qsInited = false;
    
    
            // Get ThreadExecutor Properties
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    		
        	//ThreadExecutor初始化
            String threadExecutorClass = cfg.getStringProperty(PROP_THREAD_EXECUTOR_CLASS);
            if (threadExecutorClass != null) {
                tProps = cfg.getPropertyGroup(PROP_THREAD_EXECUTOR, true);
                try {
                    threadExecutor = (ThreadExecutor) loadHelper.loadClass(threadExecutorClass).newInstance();
                    log.info("Using custom implementation for ThreadExecutor: " + threadExecutorClass);
    
                    setBeanProps(threadExecutor, tProps);
                } catch (Exception e) {
                    initException = new SchedulerException(
                            "ThreadExecutor class '" + threadExecutorClass + "' could not be instantiated.", e);
                    throw initException;
                }
            } else {
                log.info("Using default implementation for ThreadExecutor");
                threadExecutor = new DefaultThreadExecutor();
            }
    
    
    
            // Fire everything up
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            try {
                    
        		//实例化JobRunShellFactory
                JobRunShellFactory jrsf = null; // Create correct run-shell factory...
        
                if (userTXLocation != null) {
                    UserTransactionHelper.setUserTxLocation(userTXLocation);
                }
        
                if (wrapJobInTx) {
                    jrsf = new JTAJobRunShellFactory();
                } else {
                    jrsf = new JTAAnnotationAwareJobRunShellFactory();
                }
        
                if (autoId) {
                    try {
                      schedInstId = DEFAULT_INSTANCE_ID;
                      if (js.isClustered()) {
                          schedInstId = instanceIdGenerator.generateInstanceId();
                      }
                    } catch (Exception e) {
                        getLog().error("Couldn't generate instance Id!", e);
                        throw new IllegalStateException("Cannot run without an instance id.");
                    }
                }
    
                if (js.getClass().getName().startsWith("org.terracotta.quartz")) {
                    try {
                        String uuid = (String) js.getClass().getMethod("getUUID").invoke(js);
                        if(schedInstId.equals(DEFAULT_INSTANCE_ID)) {
                            schedInstId = "TERRACOTTA_CLUSTERED,node=" + uuid;
                            if (jmxObjectName == null) {
                                jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId);
                            }
                        } else if(jmxObjectName == null) {
                            jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId + ",node=" + uuid);
                        }
                    } catch(Exception e) {
                        throw new RuntimeException("Problem obtaining node id from TerracottaJobStore.", e);
                    }
    
                    if(null == cfg.getStringProperty(PROP_SCHED_JMX_EXPORT)) {
                        jmxExport = true;
                    }
                }
                
                if (js instanceof JobStoreSupport) {
                    JobStoreSupport jjs = (JobStoreSupport)js;
                    jjs.setDbRetryInterval(dbFailureRetry);
                    if(threadsInheritInitalizersClassLoader)
                        jjs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
                    
                    jjs.setThreadExecutor(threadExecutor);
                }
        		
                //将上边初始化或实例化的对象放入到QuartzSchedulerResources,给Scheduler用
                QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
                rsrcs.setName(schedName);
                rsrcs.setThreadName(threadName);
                rsrcs.setInstanceId(schedInstId);
                rsrcs.setJobRunShellFactory(jrsf);
                rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
                rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
                rsrcs.setBatchTimeWindow(batchTimeWindow);
                rsrcs.setMaxBatchSize(maxBatchSize);
                rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
                rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
                rsrcs.setJMXExport(jmxExport);
                rsrcs.setJMXObjectName(jmxObjectName);
    
                if (managementRESTServiceEnabled) {
                    ManagementRESTServiceConfiguration managementRESTServiceConfiguration = new ManagementRESTServiceConfiguration();
                    managementRESTServiceConfiguration.setBind(managementRESTServiceHostAndPort);
                    managementRESTServiceConfiguration.setEnabled(managementRESTServiceEnabled);
                    rsrcs.setManagementRESTServiceConfiguration(managementRESTServiceConfiguration);
                }
        
                if (rmiExport) {
                    rsrcs.setRMIRegistryHost(rmiHost);
                    rsrcs.setRMIRegistryPort(rmiPort);
                    rsrcs.setRMIServerPort(rmiServerPort);
                    rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry);
                    rsrcs.setRMIBindName(rmiBindName);
                }
        
                SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
    
                rsrcs.setThreadExecutor(threadExecutor);
                threadExecutor.initialize();
    			
                rsrcs.setThreadPool(tp);
                if(tp instanceof SimpleThreadPool) {
                    if(threadsInheritInitalizersClassLoader)
                        ((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader);
                }
                tp.initialize();
                tpInited = true;
        
                rsrcs.setJobStore(js);
        
                // add plugins
                for (int i = 0; i < plugins.length; i++) {
                    rsrcs.addSchedulerPlugin(plugins[i]);
                }
        		
                //初始化QuartzScheduler
                qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry);
                qsInited = true;
        
                // Create Scheduler ref...
                Scheduler scheduler = instantiate(rsrcs, qs);
        
                // set job factory if specified
                if(jobFactory != null) {
                    qs.setJobFactory(jobFactory);
                }
        
                // Initialize plugins now that we have a Scheduler instance.
                for (int i = 0; i < plugins.length; i++) {
                    plugins[i].initialize(pluginNames[i], scheduler, loadHelper);
                }
        
                // add listeners
                for (int i = 0; i < jobListeners.length; i++) {
                    qs.getListenerManager().addJobListener(jobListeners[i], EverythingMatcher.allJobs());
                }
                for (int i = 0; i < triggerListeners.length; i++) {
                    qs.getListenerManager().addTriggerListener(triggerListeners[i], EverythingMatcher.allTriggers());
                }
        
                // set scheduler context data...
                for(Object key: schedCtxtProps.keySet()) {
                    String val = schedCtxtProps.getProperty((String) key);    
                    scheduler.getContext().put((String)key, val);
                }
        
                // fire up job store, and runshell factory
        
                js.setInstanceId(schedInstId);
                js.setInstanceName(schedName);
                js.setThreadPoolSize(tp.getPoolSize());
                js.initialize(loadHelper, qs.getSchedulerSignaler());
    			
                //初始化JobRunShellFactory
                jrsf.initialize(scheduler);
                
                //初始化QuartzScheduler
                qs.initialize();
        
                getLog().info(
                        "Quartz scheduler '" + scheduler.getSchedulerName()
                                + "' initialized from " + propSrc);
        
                getLog().info("Quartz scheduler version: " + qs.getVersion());
        
                // prevents the repository from being garbage collected
                qs.addNoGCObject(schedRep);
                // prevents the db manager from being garbage collected
                if (dbMgr != null) {
                    qs.addNoGCObject(dbMgr);
                }
        		
                //将scheduler放入SchedulerRepository仓库
                schedRep.bind(scheduler);
                return scheduler;
            }
            catch(SchedulerException e) {
                shutdownFromInstantiateException(tp, qs, tpInited, qsInited);
                throw e;
            }
            catch(RuntimeException re) {
                shutdownFromInstantiateException(tp, qs, tpInited, qsInited);
                throw re;
            }
            catch(Error re) {
                shutdownFromInstantiateException(tp, qs, tpInited, qsInited);
                throw re;
            }
        }

    new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry):

        public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval)
            throws SchedulerException {
            this.resources = resources;
            if (resources.getJobStore() instanceof JobListener) {
                addInternalJobListener((JobListener)resources.getJobStore());
            }
    
            this.schedThread = new QuartzSchedulerThread(this, resources);
            ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();
            schedThreadExecutor.execute(this.schedThread);
            if (idleWaitTime > 0) {
                this.schedThread.setIdleWaitTime(idleWaitTime);
            }
    
            jobMgr = new ExecutingJobsManager();
            addInternalJobListener(jobMgr);
            errLogger = new ErrorLogger();
            addInternalSchedulerListener(errLogger);
    
            signaler = new SchedulerSignalerImpl(this, this.schedThread);
            
            getLog().info("Quartz Scheduler v." + getVersion() + " created.");
        }

    总结即,判断在仓库中是否存在调度器,存在的话,返回仓库中调度器,不存在的话,创建StdScheduler并初始化

    初始化过程包含,读取配置文件,放到PropertiesParser,然后供相关需要实例化或初始化的对象使用,再把初始化或实例化后的对象放到QuartzSchedulerResources,然后给Scheduler初始化用

  2. 创建触发器

    //TriggerBuilder
    public T build() {
    
            if(scheduleBuilder == null)
                scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
            MutableTrigger trig = scheduleBuilder.build();
            
            trig.setCalendarName(calendarName);
            trig.setDescription(description);
            trig.setStartTime(startTime);
            trig.setEndTime(endTime);
            if(key == null)
                key = new TriggerKey(Key.createUniqueName(null), null);
            trig.setKey(key); 
            if(jobKey != null)
                trig.setJobKey(jobKey);
            trig.setPriority(priority);
            
            if(!jobDataMap.isEmpty())
                trig.setJobDataMap(jobDataMap);
            
            return (T) trig;
        }

  3. 创建Job

        public JobDetail build() {
    
            JobDetailImpl job = new JobDetailImpl();
            
            job.setJobClass(jobClass);
            job.setDescription(description);
            if(key == null)
                key = new JobKey(Key.createUniqueName(null), null);
            job.setKey(key); 
            job.setDurability(durability);
            job.setRequestsRecovery(shouldRecover);
            
            if(!jobDataMap.isEmpty())
                job.setJobDataMap(jobDataMap);
            
            return job;
        }

  4. 调度器启动

    StdScheduler调用QuartzScheduler.scheduleJob()方法

        public Date scheduleJob(JobDetail jobDetail,
                Trigger trigger) throws SchedulerException {
            validateState();
    
            if (jobDetail == null) {
                throw new SchedulerException("JobDetail cannot be null");
            }
            
            if (trigger == null) {
                throw new SchedulerException("Trigger cannot be null");
            }
            
            if (jobDetail.getKey() == null) {
                throw new SchedulerException("Job's key cannot be null");
            }
    
            if (jobDetail.getJobClass() == null) {
                throw new SchedulerException("Job's class cannot be null");
            }
            
            OperableTrigger trig = (OperableTrigger)trigger;
    
            if (trigger.getJobKey() == null) {
                trig.setJobKey(jobDetail.getKey());
            } else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
                throw new SchedulerException(
                    "Trigger does not reference given job!");
            }
    
            trig.validate();
    
            Calendar cal = null;
            if (trigger.getCalendarName() != null) {
                cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
            }
            Date ft = trig.computeFirstFireTime(cal);
    
            if (ft == null) {
                throw new SchedulerException(
                        "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
            }
    
            resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
            notifySchedulerListenersJobAdded(jobDetail);
            notifySchedulerThread(trigger.getNextFireTime().getTime());
            notifySchedulerListenersSchduled(trigger);
    
            return ft;
        }

    resources.getJobStore().storeJobAndTrigger(jobDetail, trig)跟进:

    // JobStoreSupport
    public void storeJobAndTrigger(final JobDetail newJob,
                final OperableTrigger newTrigger) 
            throws JobPersistenceException {
            executeInLock(
                (isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null,
                new VoidTransactionCallback() {
                    public void executeVoid(Connection conn) throws JobPersistenceException {
                        storeJob(conn, newJob, false);
                        storeTrigger(conn, newTrigger, newJob, false,
                                Constants.STATE_WAITING, false, false);
                    }
                });
        }

    storeJob(conn, newJob, false):

    // JobStoreSupport
    protected void storeJob(Connection conn, 
            JobDetail newJob, boolean replaceExisting)
        throws JobPersistenceException {
    
        boolean existingJob = jobExists(conn, newJob.getKey());
        try {
            if (existingJob) {
                if (!replaceExisting) { 
                    throw new ObjectAlreadyExistsException(newJob); 
                }
                getDelegate().updateJobDetail(conn, newJob);
            } else {
                getDelegate().insertJobDetail(conn, newJob);
            }
        } catch (IOException e) {
            throw new JobPersistenceException("Couldn't store job: "
                    + e.getMessage(), e);
        } catch (SQLException e) {
            throw new JobPersistenceException("Couldn't store job: "
                    + e.getMessage(), e);
        }
    }

getDelegate().insertJobDetail(conn, newJob);

// StdJDBCDelegate
public int insertJobDetail(Connection conn, JobDetail job)
        throws IOException, SQLException {
        ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap());

        PreparedStatement ps = null;

        int insertResult = 0;

        try {
            ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL));
            ps.setString(1, job.getKey().getName());
            ps.setString(2, job.getKey().getGroup());
            ps.setString(3, job.getDescription());
            ps.setString(4, job.getJobClass().getName());
            setBoolean(ps, 5, job.isDurable());
            setBoolean(ps, 6, job.isConcurrentExectionDisallowed());
            setBoolean(ps, 7, job.isPersistJobDataAfterExecution());
            setBoolean(ps, 8, job.requestsRecovery());
            setBytes(ps, 9, baos);

            insertResult = ps.executeUpdate();
        } finally {
            closeStatement(ps);
        }

        return insertResult;
    }

job-insert语句:INSERT INTO QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA) VALUES ('DefaultScheduler', ?, ?, ?, ?, ?, ?, ?, ?, ?)

storeTrigger(conn, newTrigger, newJob, false,Constants.STATE_WAITING, false, false):

    // JobStoreSupport
    protected void storeTrigger(Connection conn,
                OperableTrigger newTrigger, JobDetail job, boolean replaceExisting, String state,
                boolean forceState, boolean recovering)
            throws JobPersistenceException {
    
            boolean existingTrigger = triggerExists(conn, newTrigger.getKey());
    
            if ((existingTrigger) && (!replaceExisting)) { 
                throw new ObjectAlreadyExistsException(newTrigger); 
            }
            
            try {
    
                boolean shouldBepaused;
    
                if (!forceState) {
                    shouldBepaused = getDelegate().isTriggerGroupPaused(
                            conn, newTrigger.getKey().getGroup());
    
                    if(!shouldBepaused) {
                        shouldBepaused = getDelegate().isTriggerGroupPaused(conn,
                                ALL_GROUPS_PAUSED);
    
                        if (shouldBepaused) {
                            getDelegate().insertPausedTriggerGroup(conn, newTrigger.getKey().getGroup());
                        }
                    }
    
                    if (shouldBepaused && (state.equals(STATE_WAITING) || state.equals(STATE_ACQUIRED))) {
                        state = STATE_PAUSED;
                    }
                }
    
                if(job == null) {
                    job = retrieveJob(conn, newTrigger.getJobKey());
                }
                if (job == null) {
                    throw new JobPersistenceException("The job ("
                            + newTrigger.getJobKey()
                            + ") referenced by the trigger does not exist.");
                }
    
                if (job.isConcurrentExectionDisallowed() && !recovering) { 
                    state = checkBlockedState(conn, job.getKey(), state);
                }
                
                if (existingTrigger) {
                    getDelegate().updateTrigger(conn, newTrigger, state, job);
                } else {
                    getDelegate().insertTrigger(conn, newTrigger, state, job);
                }
            } catch (Exception e) {
                throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getKey() + "' for '" 
                        + newTrigger.getJobKey() + "' job:" + e.getMessage(), e);
            }
        }
    ```

getDelegate().insertTrigger(conn, newTrigger, state, job); 跟进:

    // StdJDBCDelegate
    public int insertTrigger(Connection conn, OperableTrigger trigger, String state,
            JobDetail jobDetail) throws SQLException, IOException {

        ByteArrayOutputStream baos = null;
        if(trigger.getJobDataMap().size() > 0) {
            baos = serializeJobData(trigger.getJobDataMap());
        }
        
        PreparedStatement ps = null;

        int insertResult = 0;

        try {
            ps = conn.prepareStatement(rtp(INSERT_TRIGGER));
            ps.setString(1, trigger.getKey().getName());
            ps.setString(2, trigger.getKey().getGroup());
            ps.setString(3, trigger.getJobKey().getName());
            ps.setString(4, trigger.getJobKey().getGroup());
            ps.setString(5, trigger.getDescription());
            if(trigger.getNextFireTime() != null)
                ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger
                        .getNextFireTime().getTime())));
            else
                ps.setBigDecimal(6, null);
            long prevFireTime = -1;
            if (trigger.getPreviousFireTime() != null) {
                prevFireTime = trigger.getPreviousFireTime().getTime();
            }
            ps.setBigDecimal(7, new BigDecimal(String.valueOf(prevFireTime)));
            ps.setString(8, state);
            
            TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger);
            
            String type = TTYPE_BLOB;
            if(tDel != null)
                type = tDel.getHandledTriggerTypeDiscriminator();
            ps.setString(9, type);
            
            ps.setBigDecimal(10, new BigDecimal(String.valueOf(trigger
                    .getStartTime().getTime())));
            long endTime = 0;
            if (trigger.getEndTime() != null) {
                endTime = trigger.getEndTime().getTime();
            }
            ps.setBigDecimal(11, new BigDecimal(String.valueOf(endTime)));
            ps.setString(12, trigger.getCalendarName());
            ps.setInt(13, trigger.getMisfireInstruction());
            setBytes(ps, 14, baos);
            ps.setInt(15, trigger.getPriority());
            
            insertResult = ps.executeUpdate();
            
            if(tDel == null)
                insertBlobTrigger(conn, trigger);
            else
                tDel.insertExtendedTriggerProperties(conn, trigger, state, jobDetail);
            
        } finally {
            closeStatement(ps);
        }

        return insertResult;
    }

trigger-insert语句:INSERT_INTO QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, DESCRIPTION, NEXT_FIRE_TIME, PREV_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, START_TIME, END_TIME, CALENDAR_NAME, MISFIRE_INSTR, JOB_DATA, PRIORITY) VALUES ('DefaultScheduler', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

notifySchedulerListenersJobAdded(jobDetail); 跟进:

// 	QuartzScheduler
public void notifySchedulerListenersJobAdded(JobDetail jobDetail) {
        // build a list of all scheduler listeners that are to be notified...
        List<SchedulerListener> schedListeners = buildSchedulerListenerList();

        // notify all scheduler listeners
        for(SchedulerListener sl: schedListeners) {
            try {
                sl.jobAdded(jobDetail);
            } catch (Exception e) {
                getLog().error(
                        "Error while notifying SchedulerListener of JobAdded.",
                        e);
            }
        }
    }
// BroadcastSchedulerListener
public void jobAdded(JobDetail jobDetail) {
        Iterator<SchedulerListener> itr = listeners.iterator();
        while(itr.hasNext()) {
            SchedulerListener l = itr.next();
            l.jobAdded(jobDetail);
        }
    }

notifySchedulerThread(trigger.getNextFireTime().getTime()); 跟进:

// QuartzScheduler
protected void notifySchedulerThread(long candidateNewNextFireTime) {
        if (isSignalOnSchedulingChange()) {
            signaler.signalSchedulingChange(candidateNewNextFireTime);
        }
    }
// SchedulerSignalerImpl
public void signalSchedulingChange(long candidateNewNextFireTime) {
        schedThread.signalSchedulingChange(candidateNewNextFireTime);
    }
// QuartzSchedulerThread
public void signalSchedulingChange(long candidateNewNextFireTime) {
        synchronized(sigLock) {
            signaled = true;
            signaledNextFireTime = candidateNewNextFireTime;
            sigLock.notifyAll();
        }
    }

notifySchedulerListenersSchduled(trigger); 跟进:

// QuartzScheduler
public void notifySchedulerListenersSchduled(Trigger trigger) {
        // build a list of all scheduler listeners that are to be notified...
        List<SchedulerListener> schedListeners = buildSchedulerListenerList();

        // notify all scheduler listeners
        for(SchedulerListener sl: schedListeners) {
            try {
                sl.jobScheduled(trigger);
            } catch (Exception e) {
                getLog().error(
                        "Error while notifying SchedulerListener of scheduled job."
                                + "  Triger=" + trigger.getKey(), e);
            }
        }
    }
// BroadcastSchedulerListener
public void jobScheduled(Trigger trigger) {
        Iterator<SchedulerListener> itr = listeners.iterator();
        while(itr.hasNext()) {
            SchedulerListener l = itr.next();
            l.jobScheduled(trigger);
        }
    }

总结即:验证调度器是否关闭;JobDetail、Trigger是否为空校验;存储Job和Trigger到数据库;通知调度器监听器增加job;通知调度器线程启动;通知调度器监听器增加触发器

  1. 调度启动:

    // StdScheduler
    public void start() throws SchedulerException {
            sched.start();
        }

    sched.start(); 跟进:

    // QuartzScheduler
    public void start() throws SchedulerException {
    
            if (shuttingDown|| closed) {
                throw new SchedulerException(
                        "The Scheduler cannot be restarted after shutdown() has been called.");
            }
    
            // QTZ-212 : calling new schedulerStarting() method on the listeners
            // right after entering start()
            notifySchedulerListenersStarting();
    
            if (initialStart == null) {
                initialStart = new Date();
                this.resources.getJobStore().schedulerStarted();            
                startPlugins();
            } else {
                resources.getJobStore().schedulerResumed();
            }
    
            schedThread.togglePause(false);
    
            getLog().info(
                    "Scheduler " + resources.getUniqueIdentifier() + " started.");
            
            notifySchedulerListenersStarted();
        }

    notifySchedulerListenersStarting();跟进:

    // BroadcastSchedulerListener
    public void schedulerStarting() {
            Iterator<SchedulerListener> itr = listeners.iterator();
            while (itr.hasNext()) {
                SchedulerListener l = itr.next();
                l.schedulerStarting();
            }
        }

    this.resources.getJobStore().schedulerStarted(); 跟进:

    // JobStoreSupport
    public void schedulerStarted() throws SchedulerException {
    
            if (isClustered()) {
                clusterManagementThread = new ClusterManager();
                if(initializersLoader != null)
                    clusterManagementThread.setContextClassLoader(initializersLoader);
                clusterManagementThread.initialize();
            } else {
                try {
                    recoverJobs();
                } catch (SchedulerException se) {
                    throw new SchedulerConfigException(
                            "Failure occured during job recovery.", se);
                }
            }
    
            misfireHandler = new MisfireHandler();
            if(initializersLoader != null)
                misfireHandler.setContextClassLoader(initializersLoader);
            misfireHandler.initialize();
            schedulerRunning = true;
            
            getLog().debug("JobStore background threads started (as scheduler was started).");
        }

    startPlugins(); 跟进:

    // QuartzScheduler
    private void startPlugins() {
            java.util.Iterator<SchedulerPlugin> itr = resources.getSchedulerPlugins().iterator();
            while (itr.hasNext()) {
                SchedulerPlugin plugin = itr.next();
                plugin.start();
            }
        }

    resources.getJobStore().schedulerResumed(); 跟进:

    // JobStoreSupport
    public void schedulerResumed() {
            schedulerRunning = true;
        }

    schedThread.togglePause(false); 跟进:

    // QuartzSchedulerThread
    void togglePause(boolean pause) {
            synchronized (sigLock) {
                paused = pause;
    
                if (paused) {
                    signalSchedulingChange(0);
                } else {
                    sigLock.notifyAll();
                }
            }
        }
    public void signalSchedulingChange(long candidateNewNextFireTime) {
            synchronized(sigLock) {
                signaled = true;
                signaledNextFireTime = candidateNewNextFireTime;
                sigLock.notifyAll();
            }
        }

    notifySchedulerListenersStarted(); 跟进:

    // QuartzScheduler
    public void notifySchedulerListenersStarted() {
            // build a list of all scheduler listeners that are to be notified...
            List<SchedulerListener> schedListeners = buildSchedulerListenerList();
    
            // notify all scheduler listeners
            for(SchedulerListener sl: schedListeners) {
                try {
                    sl.schedulerStarted();
                } catch (Exception e) {
                    getLog().error(
                            "Error while notifying SchedulerListener of startup.",
                            e);
                }
            }
        }
    // BroadcastSchedulerListener
    public void schedulerStarted() {
            Iterator<SchedulerListener> itr = listeners.iterator();
            while(itr.hasNext()) {
                SchedulerListener l = itr.next();
                l.schedulerStarted();
            }
        }

  2. QuartzSchedulerThread.run();

    public void run() {
            int acquiresFailed = 0;
    
            while (!halted.get()) {
                try {
                    // check if we're supposed to pause...
                    synchronized (sigLock) {
                        while (paused && !halted.get()) {
                            try {
                                // wait until togglePause(false) is called...
                                sigLock.wait(1000L);
                            } catch (InterruptedException ignore) {
                            }
    
                            // reset failure counter when paused, so that we don't
                            // wait again after unpausing
                            acquiresFailed = 0;
                        }
    
                        if (halted.get()) {
                            break;
                        }
                    }
    
                    // wait a bit, if reading from job store is consistently
                    // failing (e.g. DB is down or restarting)..
                    if (acquiresFailed > 1) {
                        try {
                            long delay = computeDelayForRepeatedErrors(qsRsrcs.getJobStore(), acquiresFailed);
                            Thread.sleep(delay);
                        } catch (Exception ignore) {
                        }
                    }
    				//获取可用线程的数量
                    int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
                    if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
    				
                        //定义触发器集合
                        List<OperableTrigger> triggers;
    
                        long now = System.currentTimeMillis();
    
                        clearSignaledSchedulingChange();
                        try {
                            //1. 从JobStore中获取将要触发的触发器集 合
                            triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                                    now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
                            acquiresFailed = 0;
                            if (log.isDebugEnabled())
                                log.debug("batch acquisition of " + (triggers == null ? 0 : triggers.size()) + " triggers");
                        } catch (JobPersistenceException jpe) {
                            if (acquiresFailed == 0) {
                                qs.notifySchedulerListenersError(
                                    "An error occurred while scanning for the next triggers to fire.",
                                    jpe);
                            }
                            if (acquiresFailed < Integer.MAX_VALUE)
                                acquiresFailed++;
                            continue;
                        } catch (RuntimeException e) {
                            if (acquiresFailed == 0) {
                                getLog().error("quartzSchedulerThreadLoop: RuntimeException "
                                        +e.getMessage(), e);
                            }
                            if (acquiresFailed < Integer.MAX_VALUE)
                                acquiresFailed++;
                            continue;
                        }
    					//2. 触发触发器
                        if (triggers != null && !triggers.isEmpty()) {
    
                            now = System.currentTimeMillis();
                            long triggerTime = triggers.get(0).getNextFireTime().getTime();
                            long timeUntilTrigger = triggerTime - now;
                            //如果trigger的nextFireTime比当前时间大2ms则循环等待
                            while(timeUntilTrigger > 2) {
                                synchronized (sigLock) {
                                    if (halted.get()) {
                                        break;
                                    }
                                    if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
                                        try {
                                            // we could have blocked a long while
                                            // on 'synchronize', so we must recompute
                                            now = System.currentTimeMillis();
                                            timeUntilTrigger = triggerTime - now;
                                            if(timeUntilTrigger >= 1)
                                                sigLock.wait(timeUntilTrigger);
                                        } catch (InterruptedException ignore) {
                                        }
                                    }
                                }
                                if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) {
                                    break;
                                }
                                now = System.currentTimeMillis();
                                timeUntilTrigger = triggerTime - now;
                            }
    
                            // this happens if releaseIfScheduleChangedSignificantly decided to release triggers
                            if(triggers.isEmpty())
                                continue;
    
                            // set triggers to 'executing'
                            List<TriggerFiredResult> bndles = new ArrayList<TriggerFiredResult>();
    
                            boolean goAhead = true;
                            synchronized(sigLock) {
                                goAhead = !halted.get();
                            }
                            if(goAhead) {
                                try {
                                    List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
                                    if(res != null)
                                        bndles = res;
                                } catch (SchedulerException se) {
                                    qs.notifySchedulerListenersError(
                                            "An error occurred while firing triggers '"
                                                    + triggers + "'", se);
                                    //QTZ-179 : a problem occurred interacting with the triggers from the db
                                    //we release them and loop again
                                    for (int i = 0; i < triggers.size(); i++) {
                                        qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    }
                                    continue;
                                }
    
                            }
    						//执行Job
                            for (int i = 0; i < bndles.size(); i++) {
                                TriggerFiredResult result =  bndles.get(i);
                                TriggerFiredBundle bndle =  result.getTriggerFiredBundle();
                                Exception exception = result.getException();
    
                                if (exception instanceof RuntimeException) {
                                    getLog().error("RuntimeException while firing trigger " + triggers.get(i), exception);
                                    qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    continue;
                                }
    
                                // it's possible to get 'null' if the triggers was paused,
                                // blocked, or other similar occurrences that prevent it being
                                // fired at this time...  or if the scheduler was shutdown (halted)
                                if (bndle == null) {
                                    qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    continue;
                                }
    
                                JobRunShell shell = null;
                                try {
                                    shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
                                    shell.initialize(qs);
                                } catch (SchedulerException se) {
                                    qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                                    continue;
                                }
    
                                if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
                                    // this case should never happen, as it is indicative of the
                                    // scheduler being shutdown or a bug in the thread pool or
                                    // a thread pool being used concurrently - which the docs
                                    // say not to do...
                                    getLog().error("ThreadPool.runInThread() return false!");
                                    qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                                }
    
                            }
    
                            continue; // while (!halted)
                        }
                    } else { // if(availThreadCount > 0)
                        // should never happen, if threadPool.blockForAvailableThreads() follows contract
                        continue; // while (!halted)
                    }
    
                    long now = System.currentTimeMillis();
                    long waitTime = now + getRandomizedIdleWaitTime();
                    long timeUntilContinue = waitTime - now;
                    synchronized(sigLock) {
                        try {
                          if(!halted.get()) {
                            // QTZ-336 A job might have been completed in the mean time and we might have
                            // missed the scheduled changed signal by not waiting for the notify() yet
                            // Check that before waiting for too long in case this very job needs to be
                            // scheduled very soon
                            if (!isScheduleChanged()) {
                              sigLock.wait(timeUntilContinue);
                            }
                          }
                        } catch (InterruptedException ignore) {
                        }
                    }
    
                } catch(RuntimeException re) {
                    getLog().error("Runtime error occurred in main trigger firing loop.", re);
                }
            } // while (!halted)
    
            // drop references to scheduler stuff to aid garbage collection...
            qs = null;
            qsRsrcs = null;
        }

    获取触发器:triggers = qsRsrcs.getJobStore().acquireNextTriggers(now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow()) ;

    //JobStoreSupport
    protected List<OperableTrigger> acquireNextTrigger(Connection conn, long noLaterThan, int maxCount, long timeWindow)
            throws JobPersistenceException {
            if (timeWindow < 0) {
              throw new IllegalArgumentException();
            }
            
            List<OperableTrigger> acquiredTriggers = new ArrayList<OperableTrigger>();
            Set<JobKey> acquiredJobKeysForNoConcurrentExec = new HashSet<JobKey>();
            final int MAX_DO_LOOP_RETRY = 3;
            int currentLoopCount = 0;
            do {
                currentLoopCount ++;
                try {
                    List<TriggerKey> keys = getDelegate().selectTriggerToAcquire(conn, noLaterThan + timeWindow, getMisfireTime(), maxCount);
                    
                    // No trigger is ready to fire yet.
                    if (keys == null || keys.size() == 0)
                        return acquiredTriggers;
    
                    long batchEnd = noLaterThan;
    
                    for(TriggerKey triggerKey: keys) {
                        // If our trigger is no longer available, try a new one.
                        OperableTrigger nextTrigger = retrieveTrigger(conn, triggerKey);
                        if(nextTrigger == null) {
                            continue; // next trigger
                        }
                        
                        // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then
                        // put it back into the timeTriggers set and continue to search for next trigger.
                        JobKey jobKey = nextTrigger.getJobKey();
                        JobDetail job;
                        try {
                            job = retrieveJob(conn, jobKey);
                        } catch (JobPersistenceException jpe) {
                            try {
                                getLog().error("Error retrieving job, setting trigger state to ERROR.", jpe);
                                getDelegate().updateTriggerState(conn, triggerKey, STATE_ERROR);
                            } catch (SQLException sqle) {
                                getLog().error("Unable to set trigger state to ERROR.", sqle);
                            }
                            continue;
                        }
                        
                        if (job.isConcurrentExectionDisallowed()) {
                            if (acquiredJobKeysForNoConcurrentExec.contains(jobKey)) {
                                continue; // next trigger
                            } else {
                                acquiredJobKeysForNoConcurrentExec.add(jobKey);
                            }
                        }
    
                        Date nextFireTime = nextTrigger.getNextFireTime();
    
                        // A trigger should not return NULL on nextFireTime when fetched from DB.
                        // But for whatever reason if we do have this (BAD trigger implementation or
                        // data?), we then should log a warning and continue to next trigger.
                        // User would need to manually fix these triggers from DB as they will not
                        // able to be clean up by Quartz since we are not returning it to be processed.
                        if (nextFireTime == null) {
                            log.warn("Trigger {} returned null on nextFireTime and yet still exists in DB!",
                                nextTrigger.getKey());
                            continue;
                        }
                        
                        if (nextFireTime.getTime() > batchEnd) {
                          break;
                        }
                        // We now have a acquired trigger, let's add to return list.
                        // If our trigger was no longer in the expected state, try a new one.
                        int rowsUpdated = getDelegate().updateTriggerStateFromOtherState(conn, triggerKey, STATE_ACQUIRED, STATE_WAITING);
                        if (rowsUpdated <= 0) {
                            continue; // next trigger
                        }
                        nextTrigger.setFireInstanceId(getFiredTriggerRecordId());
                        //触发信息保存到fired_triggers表
                        getDelegate().insertFiredTrigger(conn, nextTrigger, STATE_ACQUIRED, null);
    
                        if(acquiredTriggers.isEmpty()) {
                            batchEnd = Math.max(nextFireTime.getTime(), System.currentTimeMillis()) + timeWindow;
                        }
                        acquiredTriggers.add(nextTrigger);
                    }
    
                    // if we didn't end up with any trigger to fire from that first
                    // batch, try again for another batch. We allow with a max retry count.
                    if(acquiredTriggers.size() == 0 && currentLoopCount < MAX_DO_LOOP_RETRY) {
                        continue;
                    }
                    
                    // We are done with the while loop.
                    break;
                } catch (Exception e) {
                    throw new JobPersistenceException(
                              "Couldn't acquire next trigger: " + e.getMessage(), e);
                }
            } while (true);
            
            // Return the acquired trigger list
            return acquiredTriggers;
        }

    获取qrtz_triggers表中TRIGGER_STATE是WAITING,且NEXT_FIRE_TIME在参数区间内的触发器: List<TriggerKey> keys = getDelegate().selectTriggerToAcquire(conn, noLaterThan + timeWindow, getMisfireTime(), maxCount);

    //StdJDBCDelegate
    public List<TriggerKey> selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan, int maxCount)
            throws SQLException {
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<TriggerKey> nextTriggers = new LinkedList<TriggerKey>();
            try {
                ps = conn.prepareStatement(rtp(SELECT_NEXT_TRIGGER_TO_ACQUIRE));
                
                // Set max rows to retrieve
                if (maxCount < 1)
                    maxCount = 1; // we want at least one trigger back.
                ps.setMaxRows(maxCount);
                
                // Try to give jdbc driver a hint to hopefully not pull over more than the few rows we actually need.
                // Note: in some jdbc drivers, such as MySQL, you must set maxRows before fetchSize, or you get exception!
                ps.setFetchSize(maxCount);
                
                ps.setString(1, STATE_WAITING);
                ps.setBigDecimal(2, new BigDecimal(String.valueOf(noLaterThan)));
                ps.setBigDecimal(3, new BigDecimal(String.valueOf(noEarlierThan)));
                rs = ps.executeQuery();
                
                while (rs.next() && nextTriggers.size() < maxCount) {
                    nextTriggers.add(triggerKey(
                            rs.getString(COL_TRIGGER_NAME),
                            rs.getString(COL_TRIGGER_GROUP)));
                }
                
                return nextTriggers;
            } finally {
                closeResultSet(rs);
                closeStatement(ps);
            }      
        }

    SELECT_NEXT_TRIGGER_TO_ACQUIRE sql:

    String SELECT_NEXT_TRIGGER_TO_ACQUIRE = "SELECT "
        + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", "
        + COL_NEXT_FIRE_TIME + ", " + COL_PRIORITY + " FROM "
        + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE "
        + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST
        + " AND " + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " <= ? " 
        + "AND (" + COL_MISFIRE_INSTRUCTION + " = -1 OR (" +COL_MISFIRE_INSTRUCTION+ " != -1 AND "+ COL_NEXT_FIRE_TIME + " >= ?)) "
        + "ORDER BY "+ COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC";

    触发触发器

    触发方法:qsRsrcs.getJobStore().triggersFired(triggers);

    //JobStoreSupport
    public List<TriggerFiredResult> triggersFired(final List<OperableTrigger> triggers) throws JobPersistenceException {
            return executeInNonManagedTXLock(LOCK_TRIGGER_ACCESS,
                    new TransactionCallback<List<TriggerFiredResult>>() {
                        public List<TriggerFiredResult> execute(Connection conn) throws JobPersistenceException {
                            List<TriggerFiredResult> results = new ArrayList<TriggerFiredResult>();
    
                            TriggerFiredResult result;
                            for (OperableTrigger trigger : triggers) {
                                try {
                                  TriggerFiredBundle bundle = triggerFired(conn, trigger);
                                  result = new TriggerFiredResult(bundle);
                                } catch (JobPersistenceException jpe) {
                                    result = new TriggerFiredResult(jpe);
                                } catch(RuntimeException re) {
                                    result = new TriggerFiredResult(re);
                                }
                                results.add(result);
                            }
    
                            return results;
                        }
                    },
                    new TransactionValidator<List<TriggerFiredResult>>() {
                        @Override
                        public Boolean validate(Connection conn, List<TriggerFiredResult> result) throws JobPersistenceException {
                            try {
                                List<FiredTriggerRecord> acquired = getDelegate().selectInstancesFiredTriggerRecords(conn, getInstanceId());
                                Set<String> executingTriggers = new HashSet<String>();
                                for (FiredTriggerRecord ft : acquired) {
                                    if (STATE_EXECUTING.equals(ft.getFireInstanceState())) {
                                        executingTriggers.add(ft.getFireInstanceId());
                                    }
                                }
                                for (TriggerFiredResult tr : result) {
                                    if (tr.getTriggerFiredBundle() != null && executingTriggers.contains(tr.getTriggerFiredBundle().getTrigger().getFireInstanceId())) {
                                        return true;
                                    }
                                }
                                return false;
                            } catch (SQLException e) {
                                throw new JobPersistenceException("error validating trigger acquisition", e);
                            }
                        }
                    });
        }

    triggerFired(conn, trigger);

    //JobStoreSupport
    protected TriggerFiredBundle triggerFired(Connection conn,
                OperableTrigger trigger)
            throws JobPersistenceException {
            JobDetail job;
            Calendar cal = null;
    
            // Make sure trigger wasn't deleted, paused, or completed...
            try { // if trigger was deleted, state will be STATE_DELETED
                String state = getDelegate().selectTriggerState(conn,
                        trigger.getKey());
                if (!state.equals(STATE_ACQUIRED)) {
                    return null;
                }
            } catch (SQLException e) {
                throw new JobPersistenceException("Couldn't select trigger state: "
                        + e.getMessage(), e);
            }
    
            try {
                //获取到触发器对应的Job实例
                job = retrieveJob(conn, trigger.getJobKey());
                if (job == null) { return null; }
            } catch (JobPersistenceException jpe) {
                try {
                    getLog().error("Error retrieving job, setting trigger state to ERROR.", jpe);
                    getDelegate().updateTriggerState(conn, trigger.getKey(),
                            STATE_ERROR);
                } catch (SQLException sqle) {
                    getLog().error("Unable to set trigger state to ERROR.", sqle);
                }
                throw jpe;
            }
    
            if (trigger.getCalendarName() != null) {
                cal = retrieveCalendar(conn, trigger.getCalendarName());
                if (cal == null) { return null; }
            }
    
            try {
                // 更新qrtz_fired_triggers表STATE为EXECUTING
                getDelegate().updateFiredTrigger(conn, trigger, STATE_EXECUTING, job);
            } catch (SQLException e) {
                throw new JobPersistenceException("Couldn't insert fired trigger: "
                        + e.getMessage(), e);
            }
    
            Date prevFireTime = trigger.getPreviousFireTime();
    
            // call triggered - to update the trigger's next-fire-time state...
            trigger.triggered(cal);
    
            String state = STATE_WAITING;
            boolean force = true;
            
        	//判断是否允许job并发执行,如果不允许,则把当前QRTZ_TRIGGERS表中TRIGGER_STATE的值为WAITING,ACQUIRED,PAUSED的trigger的该列的值改为BLOCKED
            if (job.isConcurrentExectionDisallowed()) {
                state = STATE_BLOCKED;
                force = false;
                try {
                    getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),
                            STATE_BLOCKED, STATE_WAITING);
                    getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),
                            STATE_BLOCKED, STATE_ACQUIRED);
                    getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),
                            STATE_PAUSED_BLOCKED, STATE_PAUSED);
                } catch (SQLException e) {
                    throw new JobPersistenceException(
                            "Couldn't update states of blocked triggers: "
                                    + e.getMessage(), e);
                }
            } 
            
        	//nextFireTime为null,说明是最后一次触发,将状态设置为COMPLETE
            if (trigger.getNextFireTime() == null) {
                state = STATE_COMPLETE;
                force = true;
            }
    		
        	//更新
            storeTrigger(conn, trigger, job, true, state, force, false);
    
            job.getJobDataMap().clearDirtyFlag();
    
            return new TriggerFiredBundle(job, trigger, cal, trigger.getKey().getGroup()
                    .equals(Scheduler.DEFAULT_RECOVERY_GROUP), new Date(), trigger
                    .getPreviousFireTime(), prevFireTime, trigger.getNextFireTime());
        }

    执行Job

    ThreadPool.runInThread()-->WorkerThread.run()-->JobRunShell.run()

    ThreadPool.runInThread() :

    //SimpleThreadPool
    public boolean runInThread(Runnable runnable) {
            if (runnable == null) {
                return false;
            }
    
            synchronized (nextRunnableLock) {
    
                handoffPending = true;
    
                // Wait until a worker thread is available
                while ((availWorkers.size() < 1) && !isShutdown) {
                    try {
                        nextRunnableLock.wait(500);
                    } catch (InterruptedException ignore) {
                    }
                }
    
                if (!isShutdown) {
                    WorkerThread wt = (WorkerThread)availWorkers.removeFirst();
                    busyWorkers.add(wt);
                    wt.run(runnable);
                } else {
                    // If the thread pool is going down, execute the Runnable
                    // within a new additional worker thread (no thread from the pool).
                    WorkerThread wt = new WorkerThread(this, threadGroup,
                            "WorkerThread-LastJob", prio, isMakeThreadsDaemons(), runnable);
                    busyWorkers.add(wt);
                    workers.add(wt);
                    wt.start();
                }
                nextRunnableLock.notifyAll();
                handoffPending = false;
            }
    
            return true;
        }

    WorkerThread.run():

    //WorkerThread
    try {
                        synchronized(lock) {
                            while (runnable == null && run.get()) {
                                lock.wait(500);
                            }
    
                            if (runnable != null) {
                                ran = true;
                                runnable.run();
                            }
                        }
                    } catch (InterruptedException unblock) {
                        // do nothing (loop will terminate if shutdown() was called
                        try {
                            getLog().error("Worker thread was interrupt()'ed.", unblock);
                        } catch(Exception e) {
                            // ignore to help with a tomcat glitch
                        }
                    } 

    JobRunShell.run():

    //JobRunShell
    try {
        log.debug("Calling execute on job " + jobDetail.getKey());
        job.execute(jec);
        endTime = System.currentTimeMillis();
    } catch (JobExecutionException jee) {
        endTime = System.currentTimeMillis();
        jobExEx = jee;
        getLog().info("Job " + jobDetail.getKey() +
                " threw a JobExecutionException: ", jobExEx);
    } 

    job.execute(jec); 调用MyJob的execute()方法

QuartzSchedulerThread.run()逻辑

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值