Quartz---3、定时任务开发手册1--基础使用篇

项目结构说明

项目结构图

图p1

**图p1**
整体的后台任务项目结构如上图所述,我们在开发过程中只需要关注job类的编写以及相关调用服务类的编写并且在前台配置好执行计划即可,至于任务的执行计划也就是后台任务日志会由job监听器来自动完成。我们只需要关注业务代码编写即可,这里我们希望按照一种定义好的规范进行自定义job的编写,如下:

job类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***
* 测试job类
*/
@Slf4j
@DisallowConcurrentExecution
public class Job extends QuartzJobBean {

@Autowired
TestService testService;


@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// 业务逻辑 ...
testService.test();
}
}

job类需要继承QuartzJobBean,并且复写executeInternal方法,这里需要注意不要直接在该方法内写业务代码因为可能会导至事务完整性问题,实际的业务代码需要在TestService中进行编写,并将该服务类注入到自定的job中进行业务调用。@DisallowConcurrentExecution注解不可缺少。

Service类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class TestService {


@Transactional(propagation = Propagation.REQUIRED)
public void test(){
System.out.println("test");
}


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test_NEW(){
System.out.println("test");
}

}

如上代码,需要@Service注解让你的服务类被IOC容器管理,并且在你的业务方法上面使用@Transactional注解,至于传播属性需要根据实际业务情况进行使用,如果不存在事务嵌套的情况,上述两种方式基本都可以解决大部分的问题,下一篇文章中我们会详细分析一下遇到事务嵌套情况该如何处理(这是一个比较复杂的问题,可能需要对aop有一定深度的理解,下一篇中我们详细探讨一下)。

事务完整性演示

介绍完基本的开发方法,来检测一下是否按照上述标准事务完整性是正确的。
1、job类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
@DisallowConcurrentExecution
public class Job extends QuartzJobBean {

@Autowired
TestService testService;


@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// 业务逻辑 ...
System.out.println("job start");
testService.test();
}
}

2、service类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class TestService {

@Autowired
JdbcTemplate jdbcTemplate;

@Transactional(propagation = Propagation.REQUIRED)
public void test(){
System.out.println("test");
jdbcTemplate.execute("insert into quartz_test(code,name) values(1,'111') ");
int j = 1/0;//注意这里
jdbcTemplate.execute("insert into quartz_test(code,name) values(2,'222') ");
}


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test_NEW(){
System.out.println("test");
}

}

3、注册任务项目启动后每隔15秒执行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
@Order(value = 1)
public class AgentApplicationRun implements CommandLineRunner {

@Autowired
QuartzService quartzService;

@Override
public void run(String... strings) throws Exception {
HashMap<String,Object> map = new HashMap<>();
map.put("name",3);
quartzService.deleteJob("job3", "icbc");
quartzService.addJob(Job.class, "job3", "icbc", "15 * * * * ?","job任务3", map, new Date(),new Date(new Date().getTime()+1000000));

}
}

4、执行结果
4.1 服务代码中int j = 1/0没有注释掉
图p2

**图p2**
上图我们看到任务执行,并且如期抛出了/0异常,我们再检查数据库中的确一条数据也没有

图p3

**图p3**

4.1 下面我们把服务代码中int j = 1/0注释掉,再看执行结果
图p4

**图p4**
上图我们看到任务执行,并且没有抛出异常,我们再检查数据库中的确两条数据都进去了

图p5

**图p5**

定制任务计划说明

最后需要很强调一点,由于quartz有一个复杂的misfired机制,本人暂时也没有摸的很清楚,希望在前台定制执行计划的时候选择任务开始时间时尽量选择在当前时间之后,也就是你点击保存按钮之后的时间最佳,否则就有可能出现第一次执行任务出现misfired情况,轮到第二次执行任务时会立即执行两次或两次以上,这都是由于quartz内部不同的trigger类型采用了不同的misfired机制导致的。