```xml
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-scheduling-quartz</artifactId>
</dependency>
```


#### 1、描述

调度扩展插件。solon-scheduling 的 quartz 适配，且支持毫秒级的调度。支持 quartz 的持久化。（v1.11.4 后支持）


提示：v3.4.1 后支持 Solon Native

#### 2、使用示例

启动类上，增加启用注解

```java
// 启用 Scheduled 注解的任务
@EnableScheduling
public class JobApp {
    public static void main(String[] args) {
        Solon.start(JobApp.class, args);
    }
}
```

注解在类上

```java
// 基于 Runnable 接口的模式
@Scheduled(fixedRate = 1000 * 3)
public class Job1 implements Runnable {
    @Override
    public void run() {
        System.out.println("我是 Job1 （3s）");
    }
}
```

注解在函数上<mark>（只对使用 @Component 注解的类有效）</mark>

```java
// 基于 Method 的模式（支持 JobExecutionContext 参数注入）
@Component
public class JobBean {
    @Scheduled(fixedRate = 1000 * 3)
    public void job11(JobExecutionContext jobContext){
        System.out.println("我是 job11 （3s）");
    }

    @Scheduled(cron = "0/10 * * * * ? *")
    public void job12(){
        System.out.println("我是 job12 （0/10 * * * * ? *）");
    }

    //cron 表达式，支持时区的模式 
    @Scheduled(cron = "0/10 * * * * ? * +05")
    public void job13(){
        System.out.println("我是 job13 （0/10 * * * * ? *）");
    }

    //时区独立表示的模式
    @Scheduled(cron = "0/10 * * * * ? *", zone = "Asia/Shanghai")
    public void job14(){
        System.out.println("我是 job14 （0/10 * * * * ? *）");
    }
}
```


增加拦截处理（如果有需要？），v2.7.2 后支持：

```java
@Slf4j
@Component
public class JobInterceptorImpl implements JobInterceptor {
    @Override
    public void doIntercept(Job job, JobHandler handler) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            handler.handle(job.getContext());
        } catch (Throwable e) {
            //记录日志
            TagsMDC.tag0("job");
            TagsMDC.tag1(job.getName());
            log.error("{}", e);

            throw e; //别吃掉
        } finally {
            //记录一个内部处理的花费时间
            long timespan = System.currentTimeMillis() - start;
            System.out.println("JobInterceptor: job=" + job.getName());
        }
    }
}
```

#### 3、通过应用配置，可以控制有name的任务

```yaml
# solon.scheduling.job.{job name} #要控制的job需要设置name属性
#
solon.scheduling.job.job1:
  cron: "* * * * * ?"  #重新定义调度表达式
  zone: "+08"
  fixedRate: 0
  enable: true #用任务进行启停控制
```


#### 4、@Scheduled 属性说明

| 属性 | 说明 |  备注 |  
| -------- | -------- |  -------- | 
| cron     | 支持7位（秒，分，时，日期ofM，月，星期ofW，年）     | 将并行执行 |
| zone     | 时区：+08 或 CET 或 Asia/Shanghai     |  配合 cron 使用 |
| | | |
| fixedRate      | 固定频率毫秒数（大于 0 时，cron 会失效）                    | 将并行执行 |
| fixedDelay     |  -    | 不支持 |
| initialDelay     |  -   | 不支持 |

提醒：只能选择 cron、fixedRate 中的其中一个调度方式。


#### 5、Cron 支持的表达式

支持7位（秒，分，时，日期ofM，月，星期ofW，年）

* 例：`0 0/1 * * * ? *`
* 带时区，例：`0 0/1 * * * ? * +05` 或 `0 0/1 * * * ? * -05` 


| 段位     | 段名               |    允许的值                                              |      允许的特殊字符   |
| ----   | --------       | --------                                                | --------              |
| 1段       | 秒                  |   0-59                                                         |    , - * /             |
| 2段       | 分                  |   0-59                                                         |   , - * /              |
| 3段       | 小时               |   0-23                                                         |   , - * /               |
| 4段       | 日                  |   1-31                                                          |   , - * ? / L W C   |
| 5段       | 月                  |   1-12 or JAN-DEC                                        |  , - * /                |
| 6段       | 周几               |   1-7 or SUN-SAT (使用数字时，1代表周日)       |   , - * ? / L C #   |
| 7段       | 年 (可选字段)   |   empty, 1970-2099                                       |   , - * /               |


#### 6、支持对 @Scheduled 注解的函数进行拦截

如果需要别的什么处理？可以加个拦截器。比如，全局异常记录，或者改个线程名：

```java
@EnableScheduling
public class JobApp {
    public static void main(String[] args) {
        Solon.start(JobApp.class, args, app->{
            //只对注解在函数上有效
            app.context().beanInterceptorAdd(Scheduled.class, inv->{
                Thread.currentThread().setName(inv.method().getMethod().getName());
                return inv.invoke();
            });
        });
    }
}
```

#### 7、手动管理接口

详见：[《Scheduled 调度 / 内部管理接口 IJobManager》](/article/572)


#### 8、增加持久化调试支持

* quartz.properties （名字不能改）

```properties
#指定持久化方案
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.acquireTriggersWithinLock=true
org.quartz.jobStore.misfireThreshold=5000

#指定表前前缀（根据自己需要配置，表结构脚本官网找一下）
org.quartz.jobStore.tablePrefix=QRTZ_

#指定数据源（根据自己需要取名）
org.quartz.jobStore.dataSource=demo

org.quartz.dataSource.demo.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.demo.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true
org.quartz.dataSource.demo.user=root
org.quartz.dataSource.demo.password=123456
org.quartz.dataSource.demo.maxConnections=10
org.quartz.datasource.demo.validateOnCheckout=true
org.quartz.datasource.demo.validationQuery=select 1

#指定线程池
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
```

以上仅为参考，具体根据情况而定

#### 具体参考

* [https://gitee.com/noear/solon-examples/tree/main/5.Solon-Job/demo5042-scheduling_quartz](https://gitee.com/noear/solon-examples/tree/main/5.Solon-Job/demo5042-scheduling_quartz)
* [https://gitee.com/noear/solon-examples/tree/main/5.Solon-Job/demo5043-scheduling_quartz_jdbc](https://gitee.com/noear/solon-examples/tree/main/5.Solon-Job/demo5043-scheduling_quartz_jdbc)

