美文网首页
领略Quartz源码架构之美——源码实弹之Scheduler(一

领略Quartz源码架构之美——源码实弹之Scheduler(一

作者: 向光奔跑_ | 来源:发表于2018-11-19 11:43 被阅读0次

    本章阅读收获:可了解Quartz框架中的Scheduler部分源码

    看得见的手

    有了需要运行的任务Job,和确定运行时间的Trigger之后,目前可以说这两部分是分开的,互补关联,这时候需要有一只手来操控这一切,那就是Scheduler。

    Schedule是什么

    英文意思大家应该都知道,就是计划、时间表等等。而在Quartz,这就是那只大手。让我们在回顾一下之前的demo:

    /**
     * 演示01任务调度器
     * 2018/11/14 编写
     */
    public class Exemple01Schedule {
      /**
       * 计划工厂类
       */
      private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    
      /**
       * 任务名称
       */
      private static String JOB_GROUP_NAME = "job-group-1";
    
      /**
       * 触发器名称
       */
      private static String TRIGGER_GROUP_NAME = "trigger-group-1";
    
      /**
       * 增加任务
       * @param jobClass 任务实现类
       * @param jobName 任务名称
       * @param interval 间隔时间
       * @param data 数据
       */
      public static void addJob(Class<? extends Job> jobClass, String jobName, int interval, Map<String, Object> data){
        try {
          //获取任务调度器
          Scheduler scheduler = schedulerFactory.getScheduler();
          //获取任务详细信息
          JobDetail jobDetail = JobBuilder.newJob(jobClass)
              //任务名称和组构成任务key
              .withIdentity(jobName, JOB_GROUP_NAME)
              .build();
          jobDetail.getJobDataMap().putAll(data);
          // 触发器
          SimpleTrigger trigger = TriggerBuilder.newTrigger()
              //触发器key唯一标识
              .withIdentity(jobName, TRIGGER_GROUP_NAME)
              //调度开始时间
              .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
              //调度规则
              .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                  .withIntervalInSeconds(interval)
                  .repeatForever())
              .build();
    
          scheduler.scheduleJob(jobDetail, trigger);
          // 启动
          if (!scheduler.isShutdown()) {
            scheduler.start();
          }
        } catch (SchedulerException e) {
          System.out.println(e.getMessage());
        }
      }
    }
    

    第一个进入眼帘的就是SchedulerFactory,下面就来进入分析。

    SchedulerFactory源码分析

    首先我们照常例先看下SchedulerFactory的源码:

    package org.quartz;
    
    import java.util.Collection;
    
    /**
     * 调度器工厂类
     */
    public interface SchedulerFactory {
    
        /**
         * 获取任务调度器
         */
        Scheduler getScheduler() throws SchedulerException;
    
        /**
         * 根据调度器名称获取任务调度器
         */
        Scheduler getScheduler(String schedName) throws SchedulerException;
    
        /**
         * 获取所有调度器
         */
        Collection<Scheduler> getAllSchedulers() throws SchedulerException;
    
    }
    

    大家不知有没有注意到,在我们new的时候,创建的实例其实是StdSchedulerFactory类,那么我们接下来就以这个类来进行分析。

    StdSchedulerFactory源码分析

    这里代码有点长,所有我只能逐步进行分析:首先我们可以把焦点关注于这个类的属性:

        /**
         * 调取器异常
         */
        private SchedulerException initException = null;
    
        /**
         * 配置普京
         */
        private String propSrc = null;
    
        /**
         * 配置属性
         */
        private PropertiesParser cfg;
    

    这里属性其实只有3个,下面我们来看下在demo中的调用方法:

    /**
         * 获取任务Schedule
         */
        @Override
        public Scheduler getScheduler() throws SchedulerException {
            //第一步加载配置文件,System的properties覆盖前面的配置
            if (cfg == null) {
                initialize();
            }
            //本地存储Schedule任务,注:SchedulerRepository是单例模式
            SchedulerRepository schedRep = SchedulerRepository.getInstance();
            //从缓存中查询获取Schedule任务,任务名称从配置中获取,若无指定,则默认指定QuartzScheduler
            Scheduler sched = schedRep.lookup(getSchedulerName());
            //判断若存在且已停止运行,则从缓存中移除
            if (sched != null) {
                if (sched.isShutdown()) {
                    schedRep.remove(getSchedulerName());
                } else {
                    return sched;
                }
            }
            //开始很多初始化对象
            sched = instantiate();
    
            return sched;
        }
    

    由于这个时候cfg为null,所以我们需要跳到initialize方法:

     /**
         * 根据配置文件初始化
         */
        public void initialize() throws SchedulerException {
            // 如果已经存在,直接返回
            if (cfg != null) {
                return;
            }
            if (initException != null) {
                throw initException;
            }
            //PROPERTIES_FILE = org.quartz.properties 是否存在指定读取的配置文件
            String requestedFile = System.getProperty(PROPERTIES_FILE);
            //不主动设置,默认设置为quartz.properties
            String propFileName = requestedFile != null ? requestedFile
                    : "quartz.properties";
            File propFile = new File(propFileName);
    
            Properties props = new Properties();
    
            InputStream in = null;
            //读取配置文件内容,如果都不存在依次读取quartz.properties、/quartz.properties、org/quartz/quartz.properties
            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));
        }
    

    有关于配置的我们放到之后在进行分析,现在我们按照demo的调用方式,很自然的可以读到我们的调用代码块是:

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

    可能大家会好奇,我明明按照代码读下来,我们并没有设置quartz.properties的配置文件,照道理来讲应该会抛出异常呀?但是我这程序为什么还能顺利运行呢?不知大家注意到没有:

      if (in == null) {
                        in = cl.getResourceAsStream(
                                "org/quartz/quartz.properties");
                    }
    

    我们这里会去拿源码里面的默认配置文件,然后就是加载到配置属性中。至于会加载什么属性,目前不做解析。

    然后就是overrideWithSysProps方法,

     /**
         * 添加系统配置,如果跟之前的配置相同则覆盖,以系统配置为主
         */
        private Properties overrideWithSysProps(Properties props) {
            Properties sysProps = null;
            try {
                sysProps = System.getProperties();
            } catch (AccessControlException e) {
                getLog().warn(
                    "Skipping overriding quartz properties with System properties " +
                    "during initialization because of an AccessControlException.  " +
                    "This is likely due to not having read/write access for " +
                    "java.util.PropertyPermission as required by java.lang.System.getProperties().  " +
                    "To resolve this warning, either add this permission to your policy file or " +
                    "use a non-default version of initialize().",
                    e);
            }
    
            if (sysProps != null) {
                props.putAll(sysProps);
            }
    
            return props;
        }
    

    相信大家都看的懂,就是单纯的覆盖配置属性。而之后就是:

    public void initialize(Properties props) throws SchedulerException {
            if (propSrc == null) {
                propSrc = "an externally provided properties instance.";
            }
    
            this.cfg = new PropertiesParser(props);
        }
    

    这次就先看到这,我们之后继续再往下分析。

    结束语

    本文阅读完之后,我们可以大致了解到Schedule的功能,和部分涉及到的源码,下文我们将继续展开分析。

    相关文章

      网友评论

          本文标题:领略Quartz源码架构之美——源码实弹之Scheduler(一

          本文链接:https://www.haomeiwen.com/subject/cxkofqtx.html