在开发中有很多定时任务都不是写死的而是可以人为配置并且写到数据库中的,下面简单的分享一个SpringBoot整合QUARTZ并嵌入可视化界面的demo。

Step1. 在数据库中建立有关quartz的表,我用的是 mySql 数据库,建表语句如下,如果是其他的数据库可以自己去网上下载:

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;CREATE TABLE QRTZ_JOB_DETAILS(SCHED_NAME VARCHAR(120) NOT NULL,JOB_NAME  VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,JOB_CLASS_NAME   VARCHAR(250) NOT NULL,IS_DURABLE VARCHAR(1) NOT NULL,IS_NONCONCURRENT VARCHAR(1) NOT NULL,IS_UPDATE_DATA VARCHAR(1) NOT NULL,REQUESTS_RECOVERY VARCHAR(1) NOT NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,JOB_NAME  VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,NEXT_FIRE_TIME BIGINT(13) NULL,PREV_FIRE_TIME BIGINT(13) NULL,PRIORITY INTEGER NULL,TRIGGER_STATE VARCHAR(16) NOT NULL,TRIGGER_TYPE VARCHAR(8) NOT NULL,START_TIME BIGINT(13) NOT NULL,END_TIME BIGINT(13) NULL,CALENDAR_NAME VARCHAR(200) NULL,MISFIRE_INSTR SMALLINT(2) NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_SIMPLE_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,REPEAT_COUNT BIGINT(7) NOT NULL,REPEAT_INTERVAL BIGINT(12) NOT NULL,TIMES_TRIGGERED BIGINT(10) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CRON_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,CRON_EXPRESSION VARCHAR(200) NOT NULL,TIME_ZONE_ID VARCHAR(80),PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_SIMPROP_TRIGGERS(          SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,STR_PROP_1 VARCHAR(512) NULL,STR_PROP_2 VARCHAR(512) NULL,STR_PROP_3 VARCHAR(512) NULL,INT_PROP_1 INT NULL,INT_PROP_2 INT NULL,LONG_PROP_1 BIGINT NULL,LONG_PROP_2 BIGINT NULL,DEC_PROP_1 NUMERIC(13,4) NULL,DEC_PROP_2 NUMERIC(13,4) NULL,BOOL_PROP_1 VARCHAR(1) NULL,BOOL_PROP_2 VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_BLOB_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,BLOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CALENDARS(SCHED_NAME VARCHAR(120) NOT NULL,CALENDAR_NAME  VARCHAR(200) NOT NULL,CALENDAR BLOB NOT NULL,PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_GROUP  VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_FIRED_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,ENTRY_ID VARCHAR(95) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,FIRED_TIME BIGINT(13) NOT NULL,SCHED_TIME BIGINT(13) NOT NULL,PRIORITY INTEGER NOT NULL,STATE VARCHAR(16) NOT NULL,JOB_NAME VARCHAR(200) NULL,JOB_GROUP VARCHAR(200) NULL,IS_NONCONCURRENT VARCHAR(1) NULL,REQUESTS_RECOVERY VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);CREATE TABLE QRTZ_SCHEDULER_STATE(SCHED_NAME VARCHAR(120) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,LAST_CHECKIN_TIME BIGINT(13) NOT NULL,CHECKIN_INTERVAL BIGINT(13) NOT NULL,PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);CREATE TABLE QRTZ_LOCKS(SCHED_NAME VARCHAR(120) NOT NULL,LOCK_NAME  VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);commit;

Step2.新建一个springBoot项目并导入依赖

pom.xml 文件的内容如下:

<?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.example.demo</groupId><artifactId>QuartzDemo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>demo-quartz</name><description>Demo project for quartz</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELEASE</version> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency>    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency>      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId></dependency>                                    <dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.3.4</version></dependency> <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version></dependency>                    <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.9</version></dependency>        <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.9</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.ds.tech</groupId><artifactId>dstech3-utility</artifactId><version>3.1.7</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.1.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.2.8</version></dependency><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>2.2.1</version></dependency> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.0.0</version></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.*</include></includes><excludes><exclude>**/*.java</exclude></excludes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources> <plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

其中下面的这个依赖是Quartz 所必须的

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>

Step3. 在配置文件中进行配置,application.properties 的内容如下:

#服务端口号
server.port=8082
#以Tomcat为web容器时的字符编码
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force=true
spring.http.encoding.enabled=true# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/AthenaDB?useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456#连接池配置
#spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.connectionProperties=characterEncoding=utf8;druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true
#spring.datasource.filters=stat,wall,log4j
#mybaits mapper位置设置
mybatis.mapper-locations=classpath*:com/example/demo/mapper/rmdb/*.xml
#mybatis
mybatis.type-aliases-package=com.example.demo.entity#访问日志路径
#server.tomcat.accesslog.directory=
#出现错误时, 直接抛出异常,自定义异常页面使用
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射,自定义异常页面使用
spring.resources.add-mappings=false
#应用名称,一般就是项目名称,这个名称在SpringCloud中比较关键
spring.application.name=dscms
#http请求的字符编码
spring.http.encoding.charset=UTF-8
#配置在使用Thymeleaf做页面模板时的前缀,即页面所在路径
spring.thymeleaf.prefix=classpath:/templates
#设置在使用Thymeleaf做页面模板时的后缀,其实默认就是html
spring.thymeleaf.suffix=.html
#spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.mode=HTML
#设置在使用Thymeleaf做页面模板时是否启用缓存
spring.thymeleaf.cache=false
#设置编码格式
spring.thymeleaf.encoding=UTF-8
#设置静态资源的请求路径
#spring.mvc.static-path-pattern=/**
#指定静态资源的路径
#spring.resources.static-locations=classpath:/static/,classpath:/public/
#禁止对外提供Spring MBeans
spring.jmx.enabled=false

Step4. 在resources文件夹下新建一个 quartz.properties 的配置文件,这样就不会走它自带的quartz.properties的配置文件,配置文件的内容如下:


#作业实例名称
org.quartz.scheduler.instanceName: DefaultQuartzScheduler  #如果使用集群,instanceId必须唯一,设置成AUTO
org.quartz.scheduler.instanceId = AUTO  org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false  org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true  org.quartz.jobStore.misfireThreshold: 60000
#============================================================================
# Configure JobStore
#============================================================================  #计划任务存储在内存中
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore  #存储方式使用JobStoreTX(持久化到数据库中)
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#使用自己的配置文件
org.quartz.jobStore.useProperties:true
#数据库中quartz表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:qzDS
#是否使用集群(如果项目只部署到 一台服务器,就不用了)
org.quartz.jobStore.isClustered = false  #============================================================================
# Configure Datasources
#============================================================================
#配置数据源
org.quartz.dataSource.qzDS.connectionProvider.class:com.example.demo.common.duird.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/AthenaDB?characterEncoding=utf-8
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:123456
org.quartz.dataSource.qzDS.maxConnection:10

Step5. 自定义数据源,因为要持久化到数据库,所以要用ConnectionProvider自定以数据源,代码如下

package com.example.demo.common.duird;import java.sql.Connection;
import java.sql.SQLException;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;import com.alibaba.druid.pool.DruidDataSource;public class DruidConnectionProvider implements ConnectionProvider {//JDBC驱动public String driver;//JDBC连接串public String URL;//数据库用户名public String user;//数据库用户密码public String password;//数据库最大连接数public int maxConnection;//数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。public String validationQuery;private boolean validateOnCheckout;private int idleConnectionValidationSeconds;public String maxCachedStatementsPerConnection;private String discardIdleConnectionsSeconds;public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;//Druid连接池private DruidDataSource datasource;/** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~** 接口实现** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/public Connection getConnection() throws SQLException {return datasource.getConnection();}public void shutdown() throws SQLException {datasource.close();}public void initialize() throws SQLException{if (this.URL == null) {throw new SQLException("DBPool could not be created: DB URL cannot be null");}if (this.driver == null) {throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");}if (this.maxConnection < 0) {throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");}datasource = new DruidDataSource();try{datasource.setDriverClassName(this.driver);} catch (Exception e) {try {throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);} catch (SchedulerException e1) {}}datasource.setUrl(this.URL);datasource.setUsername(this.user);datasource.setPassword(this.password);datasource.setMaxActive(this.maxConnection);datasource.setMinIdle(1);datasource.setMaxWait(0);datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);if (this.validationQuery != null) {datasource.setValidationQuery(this.validationQuery);if(!this.validateOnCheckout)datasource.setTestOnReturn(true);elsedatasource.setTestOnBorrow(true);datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);}}/** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~** 提供get set方法** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getURL() {return URL;}public void setURL(String URL) {this.URL = URL;}public String getUser() {return user;}public void setUser(String user) {this.user = user;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getMaxConnection() {return maxConnection;}public void setMaxConnection(int maxConnection) {this.maxConnection = maxConnection;}public String getValidationQuery() {return validationQuery;}public void setValidationQuery(String validationQuery) {this.validationQuery = validationQuery;}public boolean isValidateOnCheckout() {return validateOnCheckout;}public void setValidateOnCheckout(boolean validateOnCheckout) {this.validateOnCheckout = validateOnCheckout;}public int getIdleConnectionValidationSeconds() {return idleConnectionValidationSeconds;}public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;}public DruidDataSource getDatasource() {return datasource;}public void setDatasource(DruidDataSource datasource) {this.datasource = datasource;}public String getDiscardIdleConnectionsSeconds() {return discardIdleConnectionsSeconds;}public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;}}

注意:上边的这个数据源的内容是和quartz.properties中的数据源的配置互相配合的

Step6. 自定义一个jobfactory ,其目的是在具体的作业中 需要Spring 注入一些Service,所以要自定义一个jobfactory, 让其在具体job 类实例化时 使用Spring 的API 来进行依赖注入

@Component
public class JobFactory extends AdaptableJobFactory {/***    让不受spring管理的类具有spring自动注入*/@Autowiredprivate AutowireCapableBeanFactory capableBeanFactory;@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {// 调用父类的方法Object jobInstance = super.createJobInstance(bundle);// 进行注入capableBeanFactory.autowireBean(jobInstance);return jobInstance;}
}

Step7.创建调度器

package com.example.demo.common.config;import java.io.IOException;
import java.util.Properties;import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;import com.example.demo.common.factory.JobFactory;@Configuration
@EnableScheduling
public class SchedulerConfig {@AutowiredJobFactory jobFactory;@Bean(name="SchedulerFactory")public SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();//用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 //factory.setOverwriteExistingJobs(true);//QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动 //factory.setStartupDelay(10);factory.setQuartzProperties(quartzProperties());  //作业及数据源配置信息// 自定义Job Factory,用于Spring注入  service,bin等factory.setJobFactory(jobFactory);return factory;}@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//在quartz.properties中的属性被读取并注入后再初始化对象propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}/** quartz初始化监听器*/
/*    @Beanpublic QuartzInitializerListener executorListener() {return new QuartzInitializerListener();}*//** 通过SchedulerFactoryBean获取Scheduler的实例*/@Bean(name="Scheduler")public Scheduler scheduler() throws IOException {return schedulerFactoryBean().getScheduler();}}

Step8. 创建一个job_info_config.properties的配置文件,写关于作业的信息

#作业信息配置
demoquartz1_limit=2
demoquartz1_id=1000001
demoquartz1_name="样例作业1";

Step9. 新建一个类读取上面的配置文件中的信息

public class JobInfoConfig {/*** 样例作业1的队列查询上限* @return*/public static Integer getDemoQuartz1Limit() {return ConvertUtil.toInteger(ConfigUtil.getSettings("demoquartz1_limit"));}/*** 样例作业1的id* @return*/public static String getDemoQuartz1Id() {return ConvertUtil.toString(ConfigUtil.getSettings("demoquartz1_id"));}/*** 样例作业1的名称* @return*/public static String getDemoQuartz1Name() {return ConvertUtil.toString(ConfigUtil.getSettings("demoquartz1_name"));}}

Step10, 新建一个job类

package com.example.demo.job;import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;import com.example.demo.service.DemoService;@DisallowConcurrentExecution
public class DemoJob implements IBaseJob {private final Logger logger = LoggerFactory.getLogger(DemoJob.class);@Autowiredprivate DemoService demoService;@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {// TODO Auto-generated method stubtry{demoService.run();}catch(Exception e){logger.error(e.toString());}}}
package com.example.demo.job;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public interface IBaseJob extends Job {public void execute(JobExecutionContext context) throws JobExecutionException;}

Step11.在service 中写要执行的作业


/*** 读取作业信息* @author Administrator**/
public class BaseService {/*** 作业编号*/protected String jobId;/*** 作业名称*/protected String jobName;/*** 执行成功数量*/protected int success=0;/*** 执行失败数量*/protected int fail = 0;/*** 待处理记录总数*/protected int total = 0;/*** 设置job编号* @param jid*/protected void setJobId(String jId) {jobId = jId;}/*** 设置job名称* @param jName*/protected void setJobName(String jName) {jobName = jName;}/*** 开始作业方法*/protected void startJob() {success = 0;fail=0;total = 0;LogWriter.writeJobStartLog(jobId, jobName);}/*** 设置作业待处理总数* @param tl*/protected void setJobTotal(int tl) {total = tl;LogWriter.writeJobStartLog(jobId, jobName,total);}/*** 当前项处理状态* @param currentKey*/protected void setSuccessJob(String currentKey) {success++;String msg = String.format("jobsrun - jobid:%s jobname:%s key:%s status:success", jobId,jobName,currentKey);LogWriter.writeWorkLog(msg);}/*** 失败处理* @param currentKey*/protected void setFailJob(String currentKey) {fail++;String msg = String.format("jobsrun - jobid:%s jobname:%s key:%s status:fail", jobId,jobName,currentKey);LogWriter.writeWorkLog(msg);}/*** 结束作业*/protected void endJob() {LogWriter.writeJobEndLog(jobId, jobName, total, success, fail);}
}
package com.example.demo.service;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.example.demo.common.config.JobInfoConfig;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import com.example.demo.entity.UserExample;@Service
public class DemoService extends BaseService{private final Logger logger = LoggerFactory.getLogger(DemoService.class);@Autowiredprivate UserDao userDao;public void run(){//设置作业编号setJobId(JobInfoConfig.getDemoQuartz1Id());System.out.println("作业编号:"+JobInfoConfig.getDemoQuartz1Name());//设置作业名称setJobName(JobInfoConfig.getDemoQuartz1Name());System.out.println("作业名称为:"+JobInfoConfig.getDemoQuartz1Name());//开始作业startJob();//获取待处理队列List<User> list = getQueueList();if(list.size() == 0){endJob();return;}//设置待处理的总数setJobTotal(list.size());//循环处理内容for (User user : list) {queueToDeal(user);}//结束作业endJob();}public List<User> getQueueList(){List<User> list = new ArrayList<User>();try{UserExample userExample = new UserExample();userExample.createCriteria().andBalanceEqualTo(new BigDecimal(0));userExample.setStart(0);userExample.setLength(Integer.valueOf(JobInfoConfig.getDemoQuartz1Limit()));System.out.println("作业上限:"+JobInfoConfig.getDemoQuartz1Limit());list = userDao.selectByExample(userExample);}catch(Exception e){logger.error(jobName,e);}return list;}public void queueToDeal(User user){User users = userDao.selectByPrimaryKey(user.getUserId());try{if(users != null){users.setBalance(new BigDecimal(1));UserExample userExample = new UserExample();userExample.createCriteria().andUserIdEqualTo(user.getUserId());if(userDao.updateByExample(users, userExample)<=0){setFailJob(users.getUserId().toString());return;}setSuccessJob(users.getUserId().toString());}else{setFailJob(user.getUserId().toString());return;}}catch(Exception e){setFailJob(users.getUserId().toString());logger.error(e.toString());}}}
package com.example.demo.service;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.example.demo.dao.JobAndTriggerDao;
import com.example.demo.entity.JobAndTrigger;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;/*** 作业配置服务* 查询所有的作业*/
@Service
public class JobAndTriggerSevice {@Autowiredprivate JobAndTriggerDao jobAndTriggerDao;/*** 分页查询* @param pageNum* @param pageSize* @return*/public PageInfo<JobAndTrigger> getJobAndTriggerDetails(int pageNum, int pageSize) {PageHelper.startPage(pageNum, pageSize);List<JobAndTrigger> list = jobAndTriggerDao.getJobAndTriggerDetails();PageInfo<JobAndTrigger> page = new PageInfo<JobAndTrigger>(list);return page;}}

Step12. controller 中的代码如下:

package com.example.demo.controller;import java.util.HashMap;
import java.util.Map;import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import com.ds.tech.utility.log4j2.LogWriter;
import com.example.demo.entity.JobAndTrigger;
import com.example.demo.job.IBaseJob;
import com.example.demo.service.JobAndTriggerSevice;
import com.github.pagehelper.PageInfo;@RestController
@RequestMapping(value="/job")
public class JobController {@Autowiredprivate JobAndTriggerSevice jobAndTriggerSevice;//加入Qulifier注解,通过名称注入bean@Autowired @Qualifier("Scheduler")Scheduler scheduler;public static IBaseJob getClass(String classname) throws Exception {Class<?> class1 = Class.forName(classname);return (IBaseJob)class1.newInstance();}@PostMapping(value="/addjob")public void addJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName, @RequestParam(value="cronExpression")String cronExpression,@RequestParam(value="description")String description) throws Exception {// 启动调度器  scheduler.start();//构建job信息JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName, jobGroupName).withDescription(description).build();//表达式调度构建器(即任务执行的时间)CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);//按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName).withSchedule(scheduleBuilder).build();try {scheduler.scheduleJob(jobDetail, trigger);} catch (SchedulerException e) {System.out.println("创建定时任务失败"+e);//throw new Exception("创建定时任务失败");}}@PostMapping(value="/pausejob")public void pauseJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {try {scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));} catch (SchedulerException e) {System.out.println("停止定时任务失败"+e);//throw new Exception("创建定时任务失败");}}@PostMapping(value="/resumejob")public void resumeJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {try {scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));} catch (SchedulerException e) {System.out.println("继续定时任务失败"+e);//throw new Exception("创建定时任务失败");}}@PostMapping(value="/deletejob")public void deleteJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {try {scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));} catch (SchedulerException e) {System.out.println("删除定时任务失败"+e);//throw new Exception("创建定时任务失败");}}/*** 查询任务列表* @param pageNum* @param pageSize* @return*/@GetMapping(value="/queryjob")public Map<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize) {   Map<String, Object> map = new HashMap<String, Object>();try {PageInfo<JobAndTrigger> jobAndTrigger = jobAndTriggerSevice.getJobAndTriggerDetails(pageNum, pageSize);map.put("JobAndTrigger", jobAndTrigger);map.put("number", jobAndTrigger.getTotal());} catch (Exception e) {LogWriter.writeErrorLog("查询定时任务列表失败", e);}return map;}/*** 修改定时任务* @param jobClassName* @param jobGroupName* @param cronExpression* @param description* @throws Exception*/@PostMapping(value="/reschedulejob")public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName,@RequestParam(value="cronExpression")String cronExpression,@RequestParam(value="description")String description) throws Exception{            try {TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);// 表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);// 按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).withDescription(description).build();// 按新的trigger重新设置job执行scheduler.rescheduleJob(triggerKey, trigger);} catch (SchedulerException e) {LogWriter.writeErrorLog("更新定时任务失败", e);}}}

Step13. 放开页面所在的静态资源,让他可以访问到

package com.example.demo.common.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** Spring mvc 配置**/
@Configuration
@EnableWebMvc
@ComponentScan
public class WebMvcConfig implements WebMvcConfigurer {/*** 设置资源*/@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//全部静态资源,如果指定全部资源则全局异常捕获无法传递到自定义的error.html页面//registry.addResourceHandler("/webapp/**").addResourceLocations("classpath:/webapp/"); registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");WebMvcConfigurer.super.addResourceHandlers(registry);}}

Step14. 在static 文件夹下建立一个html,代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"><title>Quartz任务管理</title><link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css"><script src="https://unpkg.com/vue/dist/vue.js"></script><script src="https://cdn.bootcss.com/vue-validator/2.1.5/vue-validator.js"></script><script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script><script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script><style>      #top {background:#20A0FF;padding:5px;overflow:hidden}</style></head>
<body><div id="test">                <div id="top">         <el-button type="text" @click="search" style="color:white">查询</el-button> <el-button type="text" @click="handleadd" style="color:white">添加</el-button>  </span>                       </div>    <br/><div style="margin-top:15px">   <el-tableref="testTable"        :data="tableData"style="width:100%"border><el-table-columnprop="job_NAME"label="任务名称"sortableshow-overflow-tooltip></el-table-column><el-table-columnprop="job_GROUP"label="任务所在组"width="120"sortable></el-table-column><el-table-columnprop="job_CLASS_NAME"label="任务类名"sortable></el-table-column><el-table-columnprop="trigger_NAME"label="触发器名称"sortable></el-table-column><el-table-columnprop="trigger_GROUP"label="触发器所在组"width="120"sortable></el-table-column><el-table-columnprop="cron_EXPRESSION"label="表达式"sortable></el-table-column><el-table-columnprop="time_ZONE_ID"label="时区"sortable></el-table-column><el-table-column label="操作" width="300"><template scope="scope"><el-buttonsize="small"type="warning"@click="handlePause(scope.$index, scope.row)">暂停</el-button><el-buttonsize="small"type="info"@click="handleResume(scope.$index, scope.row)">恢复</el-button><el-buttonsize="small"type="danger"@click="handleDelete(scope.$index, scope.row)">删除</el-button><el-buttonsize="small"type="success"@click="handleUpdate(scope.$index, scope.row)">修改</el-button></template></el-table-column></el-table><div align="center"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[10, 20, 30, 40]":page-size="pagesize"layout="total, sizes, prev, pager, next, jumper":total="totalCount"></el-pagination></div></div> <el-dialog title="添加任务" :visible.sync="dialogFormVisible" width="500px"><el-form :model="form"><el-form-item label="任务名称" label-width="120px" style="width:400px"><el-input v-model="form.jobName" auto-complete="off"></el-input></el-form-item><el-form-item label="任务分组" label-width="120px" style="width:400px"><el-input v-model="form.jobGroup" auto-complete="off"></el-input></el-form-item><el-form-item label="表达式" label-width="120px" style="width:400px"><el-input v-model="form.cronExpression" auto-complete="off"></el-input></el-form-item><el-form-item label="备注信息" label-width="120px" style="width:400px"><el-input type="textarea" v-model="form.description" auto-complete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取 消</el-button><el-button type="primary" @click="add">确 定</el-button></div></el-dialog><el-dialog title="修改任务" :visible.sync="updateFormVisible" width="500px"><el-form :model="updateform"><el-form-item label="表达式" label-width="120px" style="width:400px"><el-input v-model="updateform.cronExpression" auto-complete="off"></el-input></el-form-item><el-form-item label="备注信息" label-width="120px" style="width:400px"><el-input type="textarea" v-model="form.description" auto-complete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="updateFormVisible = false">取 消</el-button><el-button type="primary" @click="update">确 定</el-button></div></el-dialog></div><footer align="center"><p>&copy; Quartz 任务管理</p></footer><script>var vue = new Vue({           el:"#test",data: {          //表格当前页数据tableData: [],//请求的URLurl:'job/queryjob',//默认每页数据量pagesize: 10,                //当前页码currentPage: 1,//查询的页码start: 1,//默认数据总数totalCount: 1000,//添加对话框默认可见性dialogFormVisible: false,//修改对话框默认可见性updateFormVisible: false,//提交的表单form: {jobName: '',jobGroup: '',cronExpression: '',description:'',},updateform: {jobName: '',jobGroup: '',cronExpression: '',description:'',},},methods: {//从服务器读取数据loadData: function(pageNum, pageSize){                   this.$http.get('job/queryjob?' + 'pageNum=' +  pageNum + '&pageSize=' + pageSize).then(function(res){console.log(res)this.tableData = res.body.JobAndTrigger.list;this.totalCount = res.body.number;},function(){console.log('failed');});                  },                              //单行删除handleDelete: function(index, row) {this.$http.post('job/deletejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//暂停任务handlePause: function(index, row){this.$http.post('job/pausejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//恢复任务handleResume: function(index, row){this.$http.post('job/resumejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//搜索search: function(){this.loadData(this.currentPage, this.pagesize);},//弹出对话框handleadd: function(){                        this.dialogFormVisible = true;               },//添加add: function(){this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression,"description":this.form.description},{emulateJSON: true}).then(function(res){this.loadData(this.currentPage, this.pagesize);this.dialogFormVisible = false;},function(){console.log('failed');});this.form.jobName="";},//更新handleUpdate: function(index, row){console.log(row)this.updateFormVisible = true;this.updateform.jobName = row.job_CLASS_NAME;this.updateform.jobGroup = row.job_GROUP;},//更新任务update: function(){this.$http.post('job/reschedulejob',{"jobClassName":this.updateform.jobName,"jobGroupName":this.updateform.jobGroup,"cronExpression":this.updateform.cronExpression,"description":this.form.description},{emulateJSON: true}).then(function(res){this.loadData(this.currentPage, this.pagesize);this.updateFormVisible = false;},function(){console.log('failed');});},//每页显示数据量变更handleSizeChange: function(val) {this.pagesize = val;this.loadData(this.currentPage, this.pagesize);},//页码变更handleCurrentChange: function(val) {this.currentPage = val;this.loadData(this.currentPage, this.pagesize);},         },        });//载入数据vue.loadData(vue.currentPage, vue.pagesize);</script>  </body>
</html>

这样所有的东西都完成啦,运行一下就好啦,运行效果如下:

SpringBoot 整合QUARTZ 并嵌入可视化界面相关推荐

  1. Spring Boot定时任务-SpringBoot整合Quartz

    如何通过SpringBoot整合Quartz框架,我们首先去创建一个项目,接下来我们需要在pom文件里添加坐标,我们在使用SpringBoot整合Quartz的时候,需要添加哪些坐标呢,我们来看一下, ...

  2. SpringBoot整合Quartz==适用于单任务多任务

    上一篇文章简单的介绍了一下Quartz的控制台运行,有兴趣的可以看看https://blog.csdn.net/yali_aini/article/details/85273209 这里我会介绍一下 ...

  3. SpringBoot整合Quartz之动态控制任务(暂停,启动,修改执行时间)

    SpringBoot整合 Quartz 篇请移步 https://blog.csdn.net/yali_aini/article/details/85287074 此篇文章讲述如何动态控制 Quart ...

  4. Springboot整合Quartz任务框架

      分享一篇关于Springboot整合Quartz任务框架在实际开发中的使用,基于SpringBoot2.0+Mybatis+Oracle开发 1. 导入jar包 <!-- 定时任务 --&g ...

  5. springboot整合quartz,实现数据库方式执行定时任务

    springboot整合quartz,实现数据库方式执行定时任务.把定时任务信息存进数据库,项目启动后自动执行定时任务. 1.引入依赖包: <dependency><groupId& ...

  6. SpringBoot整合Quartz执行持久化定时任务

    使用: 个人博客项目,定时刷新帖子排名用到了定时任务,在这里记录下来SpringBoot整合Quartz的基本使用,记录的同时也希望能帮助到大家. 1.什么是Quartz 官方介绍: Quartz是一 ...

  7. Java-Quartz实现定时任务(SpringBoot整合quartz)

    Quartz简介 Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中.它提供了巨大的灵活性而不牺牲简单性.你能够用它来为执行一个作业而创建简单 ...

  8. springboot整合mybaits-plusmybaits实现用户登陆界面(适合入门)+唯美界面

    springboot整合mybaits-plus/mybaits实现用户登陆界面(一步步解析)+唯美界面 文章目录 springboot整合mybaits-plus/mybaits实现用户登陆界面(一 ...

  9. Springboot整合Quartz集群部署以及配置Druid数据源

    参考链接: https://blog.csdn.net/wangmx1993328/article/details/105441308 https://blog.csdn.net/qq_3966905 ...

  10. SSM整合Quart和Springboot整合Quartz组件

    1]使用我们的这个ssm去整合我们的这个定时任务 1.Quartz的依赖 <dependency> <groupId>org.quartz-scheduler</grou ...

最新文章

  1. 登录状态保持Session/Cookie
  2. 【错误记录】Tinker 热修复示例运行报错 ( Execution failed for task ‘:app:tinkerProcessD‘ . tinkerId is not set!!! )
  3. java包和访问权限_Java包和访问权限—1
  4. keepalived vrrp协议
  5. android 只能输入汉字,EditText限制输入的几种方式及只显示中文汉字的做法
  6. svn版利用什么技术实现_金葱粉涂料印花利用了什么技术?
  7. mysql 多个库一起导出_MYSQL 导出多个库
  8. 汇编语言常见错误(转载)
  9. Seata多微服务互相调用_全局分布式事物使用案例_Account-Module 账户微服务说明---微服务升级_SpringCloud Alibaba工作笔记0064
  10. 基于Matlab----RSSI指纹定位技术性能仿真
  11. 统计匹配检索规则的物品数量
  12. bspline怎么使用 python_零基础5个月快速学会Python的秘诀
  13. VS中如何导入wav并且播放音乐
  14. 【数据库】E-R图相关知识、绘制方法及工具推荐
  15. POJO类中属性必须使用包装数据类型
  16. SGX Architectural Encalve(AE)及SGX密钥
  17. 打开虚拟机电脑就重启和虚拟机不兼容hyper - v无法开启的解决方案。
  18. Java第二课. 数据类型与运算符
  19. 如何利用Swagger生成统一格式的Responses
  20. 网站收录链接分析之网站排名查询

热门文章

  1. android网易云桌面歌词,网易云怎么设置桌面歌词?
  2. 模块式工业机器人柔性自动化生产线实训系统(五站)
  3. led阵列设计matlab代码,阵列信号处理MATLAB程序
  4. 李雅普诺夫稳定性、李雅普诺夫第一法、李雅普诺夫第二法及李雅普诺夫函数
  5. Android WallpaperManager 壁纸分析
  6. 营业执照在线生成_营业执照用旧的?办新的?办电子的?丨现在选哪个都很“好办”...
  7. 广数980tc3从u盘复制到系统_广数980tc3数控车床
  8. 纸质办公电子化——iWebOffice中间件
  9. 思源黑体下载 - Google 联合 Adobe 发布免费开源优雅的设计字体 (简繁中文/日韩文)
  10. 2020美赛F奖论文(一):摘要、绪论和模型准备