Demo实现的功能
使用springBoot集成quartz分布式调度框架和mybatis, 实现定时用数据库做查询数据的功能。
github源码地址
https://github.com/mikewuhao/springBoot-quartz-demo
搭建详细步骤
项目结构
在idea开发工具里面新建maven类型的project, 命名为springBoot-quartz-demo按上图目录结构建包
pom.xml, 引入quartz相关依赖的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wuhao.demo</groupId>
<artifactId>springBoot-quartz-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spring-boot整合mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
mysql数据库和mybatis相关配置
#数据库
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root
#mybatis
mybatis.type-aliases-package=com.wuhao.domain
mybatis.mapper-locations=classpath:mapper/*.xml
编写启动类QuartzApplication, 加上@EnableScheduling开启定时任务的注解
package com.wuhao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @description: quartz启动类
* @author: wuhao
* @create: 2020-06-18 18:27
**/
@SpringBootApplication
@EnableScheduling
public class QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class,args);
}
}
编写Job类 ,自定义1个从数据库查数据的定时任务
package com.wuhao.job;
import com.wuhao.dao.UserMapper;
import com.wuhao.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @description: job类, 自定义任务
* @author: wuhao
* @create: 2020-06-18 21:34
**/
@Slf4j
public class MyJob implements Job {
@Autowired
private UserMapper userMapper;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
User user = userMapper.queryUserById(1L);
log.info("job执行: ---job------->"+user.toString());
}
}
编写quzrtz的核心配置类, 此处按每隔2秒, 重复5次触发定时任务
package com.wuhao.config;
import com.wuhao.job.MyJob;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
/**
* @description: quartz配置类
* @author: wuhao
* @create: 2020-06-19 10:38
**/
@Configuration
public class QuartzConfig {
//1.创建Job对象
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factory = new JobDetailFactoryBean();
//关联自定义的job类
factory.setJobClass(MyJob.class);
return factory;
}
//2. 创建Trigger对象
@Bean
public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
SimpleTriggerFactoryBean factory = new SimpleTriggerFactoryBean();
// 关联jobDetail对象
factory.setJobDetail(jobDetailFactoryBean.getObject());
//任务间隔毫秒数
factory.setRepeatInterval(2000);
// 任务重复次数
factory.setRepeatCount(5);
return factory;
}
//3. 创建Scheduler对象
@Bean
public SchedulerFactoryBean schedulerFactoryBean(SimpleTriggerFactoryBean simpleTriggerFactoryBean, MyAdaptableJobFactory myAdaptableJobFactory){
SchedulerFactoryBean factory= new SchedulerFactoryBean();
//关联 trigger
factory.setTriggers(simpleTriggerFactoryBean.getObject());
factory.setJobFactory(myAdaptableJobFactory);
return factory;
}
}
**编写一个MyAdaptableJobFactory类, 继承父类AdaptableJobFactory, 重写AdaptableJobFactory方法, 目的是解决Dao层的注入时报空指针的问题 **
package com.wuhao.config;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @description: 继承AdaptableFactory类, 重写实例化方法, 把对象手动注入springIOC容器, 实现业务层service的注入
* @author: wuhao
* @create: 2020-06-19 11:15
**/
@Component("myAdaptableJobFactory")
public class MyAdaptableJobFactory extends AdaptableJobFactory {
@Autowired //將對象添加到springIOC的容器中並且完成對象的注入
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object obj = super.createJobInstance(bundle);
//把obj對象加入到springIOC容器中,完成注入
this.autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
User用户实体类
package com.wuhao.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: User 实体类
* @author: wuhao
* @create: 2020-06-19 11:47
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String birthday;
private String sex;
private String address;
}
Dao层的UserMapper接口
package com.wuhao.dao;
import com.wuhao.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
User queryUserById(Long id);
int addUser(User user);
int modifyUser(User user);
int deleteUserById(Long id);
}
执行sql的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wuhao.dao.UserMapper">
<!--按id查询用户-->
<select id="queryUserById" resultType="com.wuhao.domain.User">
select * from `user` where id = #{id}
</select>
<!--用户更新-->
<update id="modifyUser" parameterType="com.wuhao.domain.User" >
update `user` set username=#{username},birthday=#{birthday},sex=#{sex}, address=#{address} where id=#{id}
</update>
<!--删除用户-->
<delete id="deleteUserById" parameterType="long">
delete from `user` where id=#{id}
</delete>
<!--用户添加-->
<insert id="addUser" parameterType="com.wuhao.domain.User">
insert into `user` (username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
演示效果
启动项目, 观察控制台的运行日志, 系统会按每隔2秒, 重复5次执行查数据库操作的定时任务(项目刚启动时执行的不算)
遇到的问题
刚开始在Job任务类里注入Dao层的UserMapper时, 启动报空指针异常, 发现UserMapper没注入进来, 后来查资料得知, Job类实例化是通过AdaptableJobFactory类的createJobInstance方法用反射实体化的, 没被Spring的IOC容器管理
springIOC实例化要求 : 必须是注入对象(UserMapper)和被注入对象(Job类)都在springIOC容器中, 解决方法就是: 自定义MyAdaptableJobFactory类继承父类AdaptableJobFactory, 重写createJobInstance方法, 把Job类對象添加到springIOC容器中並且完成對象的注入
后续优化点
可用quartz实现数据库任务的持久化, 这里时间原因就不再叙述了