這篇文章給大家介紹springboot quartz以持久化的方式實現(xiàn)定時任務(wù),詳情如下所示:
篇幅較長,耐心的人總能得到最後的答案小生第一次用quartz做定時任務(wù),不足之處多多諒解。
首先
在springboot專案裡做定時任務(wù)是比較簡單的,最簡單的實作方式是使用**@Scheduled註解,然後在application啟動類別上使用@EnableScheduling**開啟定時任務(wù)。
範(fàn)例
@SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } // cron為每秒執(zhí)行一次 @Scheduled(cron = "* * * * * ?") public void print(){ System.out.println("執(zhí)行定時任務(wù)"); } }
結(jié)果執(zhí)行定時任務(wù)執(zhí)行定時任務(wù)
#執(zhí)行定時任務(wù)
執(zhí)行定時任務(wù)
執(zhí)行定時任務(wù)
執(zhí)行定時任務(wù)
執(zhí)行定時任務(wù)
執(zhí)行定時任務(wù)
簡單的定時任務(wù)就可以用這種方式來做,cron表達(dá)式的結(jié)果為任務(wù)執(zhí)行的間隔時間。 然而
在實際開發(fā)中,我們的任務(wù)可能有很多,需要手動操作單一/全部的任務(wù),例如新增、開啟、停止、繼續(xù)等等操作。那麼伴隨著(千牛B類。。。)的BGM有請quartz登場。
quartz整合 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- ##調(diào)度器Scheduler啟動觸發(fā)器去執(zhí)行任務(wù)觸發(fā)器Trigger
任務(wù)job
具體要執(zhí)行的任務(wù)內(nèi)容使用
使用quartz是需要設(shè)定檔的,quartz.properties在quartz的jar套件的org.quartz套件下可以找到預(yù)設(shè)的設(shè)定檔quartz.properties# Default Properties file for use by StdSchedulerFactory # to create a Quartz Scheduler Instance, if a different # properties file is not explicitly specified. # # 名字 org.quartz.scheduler.instanceName: DefaultQuartzScheduler org.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false org.quartz.scheduler.wrapJobExecutionInUserTransaction: false # 實例化ThreadPool時,使用的線程類為SimpleThreadPool org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool # 線程總個數(shù) org.quartz.threadPool.threadCount: 10 # 線程的優(yōu)先級 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true org.quartz.jobStore.misfireThreshold: 60000 # 持久化方式,默認(rèn)持久化在內(nèi)存中,后面我們使用db的方式 org.quartz.jobStore.class: org.quartz.simpl.RAMJobStorequartz任務(wù)持久化到db則需要一些官方定義的資料庫表,表的sql文件可以在quartz的jar包裡找到坐標(biāo)org.quartz.impl.jdbcjobstore,可以看到裡面有很多sql文件,有各種數(shù)據(jù)庫的,咱們用MySQL的,咱們不需要手動執(zhí)行sql語句,後面咱們在啟動專案的時候會自動初始化。 建立我們自己的properties檔案
# 實例化ThreadPool時,使用的線程類為SimpleThreadPool org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool # threadCount和threadPriority將以setter的形式注入ThreadPool實例 # 并發(fā)個數(shù) org.quartz.threadPool.threadCount=10 # 優(yōu)先級 org.quartz.threadPool.threadPriority=5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true org.quartz.jobStore.misfireThreshold=5000 #持久化使用的類 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX #數(shù)據(jù)庫中表的前綴 org.quartz.jobStore.tablePrefix=QRTZ_ #數(shù)據(jù)源命名 org.quartz.jobStore.dataSource=qzDS #qzDS 數(shù)據(jù)源,我們使用hikaricp,默認(rèn)的是c3p0 org.quartz.dataSource.qzDS.provider=hikaricp org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.qzDS.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 org.quartz.dataSource.qzDS.user=root org.quartz.dataSource.qzDS.password=123456 org.quartz.dataSource.qzDS.maxConnections=10現(xiàn)在我們沒有使用預(yù)設(shè)的連線池,那麼就探索一下,上原始碼!在這個套件下:org.quartz.utils,有一個PoolingConnectionProvider,顧名思義,連接池提供者部分原始碼
public interface PoolingConnectionProvider extends ConnectionProvider { /** The pooling provider. */ String POOLING_PROVIDER = "provider"; /** The c3p0 pooling provider. */ String POOLING_PROVIDER_C3P0 = "c3p0"; /** The Hikari pooling provider. */ String POOLING_PROVIDER_HIKARICP = "hikaricp"; }然後HikariCpPoolingConnectionProvider這個類別實作了PoolingConnectionProvider,自行查看。我們可以在org.quartz.impl下的StdSchedulerFactory中搜尋c3p0找到
if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) { cpClass = "org.quartz.utils.HikariCpPoolingConnectionProvider"; } else { cpClass = "org.quartz.utils.C3p0PoolingConnectionProvider"; }剩下的自己多看看吧,起始源碼研究起來沒有想像中那麼難那麼乏味(我也不喜歡看源碼),但是這個原始碼看起來確實小有成就感。 回到正題頻道,設(shè)定application.yml
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver password: 123456 url: jdbc:mysql://localhost:3306/quartz?characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2B8 username: root quartz: jdbc: initialize-schema: always job-store-type: jdbcinitialize-schema: always每次啟動項目,總是初始化資料庫表自動建立表格的關(guān)鍵地方,流程是先刪除資料庫表,再創(chuàng)建,如果表不存在,則拋異常,但是不會影響後面的生成表,下次再啟動專案的時候,由於表已經(jīng)存在了,所以不會再拋異常了job-store-type: jdbc就是任務(wù)持久化類型,我們用jdbc我們可能要在job裡注入spring對象,不做配置,是無法注入的。
/** * @author: taoym * @date: 2020/6/4 11:32 * @desc: 一定要自定義JobFactory重寫SpringBeanJobFactory的createJobInstance方法,否則在job中是獲取不到spring容器中的bean的 */ @Component public class JobFactory extends SpringBeanJobFactory { @Autowired private AutowireCapableBeanFactory beanFactory; /** * 這里覆蓋了super的createJobInstance方法,對其創(chuàng)建出來的類再進(jìn)行autowire */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object jobInstance = super.createJobInstance(bundle); beanFactory.autowireBean(jobInstance); return jobInstance; } }建立quartz的設(shè)定檔
@Configuration public class QuartzConfig { @Autowired private JobFactory jobFactory; /** * 讀取quartz.properties 文件 * 將值初始化 * * @return */ @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(jobFactory); schedulerFactoryBean.setQuartzProperties(quartzProperties()); return schedulerFactoryBean; } /** * 初始化監(jiān)聽器 * * @return */ @Bean public QuartzInitializerListener executorListener() { return new QuartzInitializerListener(); } @Bean(name = "scheduler") public Scheduler scheduler() throws IOException { return schedulerFactoryBean().getScheduler(); } }建立觸發(fā)器元件
public class TriggerComponent { /** * @author: taoym * @date: 2020/6/1 10:35 * @desc: 構(gòu)建cron觸發(fā)器 */ public static Trigger cronTrigger(String cron) { CronTrigger cronTrigger = TriggerBuilder.newTrigger() .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing()) .build(); return cronTrigger; } public static Trigger cronTrigger(String cron, JobDataMap jobDataMap) { CronTrigger cronTrigger = TriggerBuilder.newTrigger() .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing()) .usingJobData(jobDataMap) .build(); return cronTrigger; } }觸發(fā)器就用這個元件來取得就行了。 建立任務(wù)
@DisallowConcurrentExecution public class TestJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { } }jobExecutionContext這裡面可以取得任務(wù)群組、任務(wù)名稱、觸發(fā)器群組、觸發(fā)器名稱、jobdetail等資訊。那個註解是為了讓同一個實例(jobdetail)只能單執(zhí)行緒執(zhí)行??梢赃@麼理解,job為接口,jobdetail為實現(xiàn)類,a是其中一個實現(xiàn)類,a需要花費(fèi)100s執(zhí)行一定的操作,而你給的定時器是沒50s就執(zhí)行一次操作,a在執(zhí)行到一半的時候又需要開啟一個線程來執(zhí)行。使用了DisallowConcurrentExecution就相當(dāng)於a沒有把操作執(zhí)行完的時候,a不允許開啟執(zhí)行緒再執(zhí)行目前操作。不知道我的描述是否易懂! 按需建立自己的任務(wù)表,我是用定時任務(wù)做爬蟲的(小爬蟲)
CREATE TABLE `quartz_job` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `job_name` varchar(50) DEFAULT '' COMMENT '任務(wù)名', `job_group` varchar(50) DEFAULT '' COMMENT '任務(wù)組名稱', `job_desc` varchar(255) DEFAULT '' COMMENT 'job描述', `cron` varchar(50) DEFAULT '' COMMENT 'cron表達(dá)式', `status` tinyint(1) DEFAULT '0' COMMENT '狀態(tài)', `url` varchar(255) DEFAULT '' COMMENT '請求地址', `param` varchar(255) DEFAULT '' COMMENT '參數(shù)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;我們添加任務(wù)的時候不和quartz打交道,把任務(wù)放到資料庫即可。別慌,後面有用到他的地方。這個表格需要有增刪改查操作,我們會在系統(tǒng)中查詢?nèi)蝿?wù)清單選擇單一或所有任務(wù)開始執(zhí)行執(zhí)行任務(wù)
@Resource private QuartzJobMapper quartzJobMapper; @Autowired private Scheduler scheduler; @Override public String start(Integer id) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put(k,v); QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(id); JobKey jobKey = JobKey.jobKey(quartzJob.getJobName(), quartzJob.getJobGroup()); jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).storeDurably().build(); Trigger trigger = TriggerComponent.cronTrigger(quartzJob.getCron(), jobDataMap); try { scheduler.scheduleJob(jobDetail, trigger); quartzJobMapper.updateStatus(true, id); return "開始任務(wù)執(zhí)行成功"; } catch (SchedulerException se) { log.info("開始任務(wù)的時候發(fā)生了錯誤"); } return "開始任務(wù)的時候發(fā)生了錯誤,請檢查日志"; }最後我又按照此教學(xué)上的內(nèi)容貼上了一遍程式碼,可以正常運(yùn)作。
推薦教學(xué):《PHP》
以上是springboot+quartz 以持久化的方式實現(xiàn)定時任務(wù)的詳細(xì)內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Jasypt介紹Jasypt是一個java庫,它允許開發(fā)員以最少的努力為他/她的專案添加基本的加密功能,並且不需要對加密工作原理有深入的了解用於單向和雙向加密的高安全性、基於標(biāo)準(zhǔn)的加密技術(shù)。加密密碼,文本,數(shù)字,二進(jìn)位檔案...適合整合到基於Spring的應(yīng)用程式中,開放API,用於任何JCE提供者...添加如下依賴:com.github.ulisesbocchiojasypt-spring-boot-starter2. 1.1Jasypt好處保護(hù)我們的系統(tǒng)安全,即使程式碼洩露,也可以保證資料來源的

一、Redis實現(xiàn)分散式鎖原理為什麼需要分散式鎖在聊分散式鎖之前,有必要先解釋一下,為什麼需要分散式鎖。與分散式鎖相對就的是單機(jī)鎖,我們在寫多執(zhí)行緒程式時,避免同時操作一個共享變數(shù)產(chǎn)生資料問題,通常會使用一把鎖來互斥以保證共享變數(shù)的正確性,其使用範(fàn)圍是在同一個進(jìn)程中。如果換做是多個進(jìn)程,需要同時操作一個共享資源,如何互斥?現(xiàn)在的業(yè)務(wù)應(yīng)用通常是微服務(wù)架構(gòu),這也意味著一個應(yīng)用會部署多個進(jìn)程,多個進(jìn)程如果需要修改MySQL中的同一行記錄,為了避免操作亂序?qū)е麦v數(shù)據(jù),此時就需要引入分佈式鎖了。想要實現(xiàn)分

1.自訂RedisTemplate1.1、RedisAPI預(yù)設(shè)序列化機(jī)制基於API的Redis快取實作是使用RedisTemplate範(fàn)本進(jìn)行資料快取操作的,這裡開啟RedisTemplate類,查看該類別的源碼資訊publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations,BeanClassLoaderAware{//聲明了value的各種序列化方式,初始值為空@NullableprivateRedisSe

springboot讀取文件,打成jar包後訪問不到最新開發(fā)出現(xiàn)一種情況,springboot打成jar包後讀取不到文件,原因是打包之後,文件的虛擬路徑是無效的,只能通過流去讀取。文件在resources下publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

使用場景1、下單成功,30分鐘未支付。支付超時,自動取消訂單2、訂單簽收,簽收後7天未進(jìn)行評估。訂單超時未評價,系統(tǒng)預(yù)設(shè)好評3、下單成功,商家5分鐘未接單,訂單取消4、配送超時,推播簡訊提醒…對於延時比較長的場景、即時性不高的場景,我們可以採用任務(wù)調(diào)度的方式定時輪詢處理。如:xxl-job今天我們採

在Springboot+Mybatis-plus不使用SQL語句進(jìn)行多表添加操作我所遇到的問題準(zhǔn)備工作在測試環(huán)境下模擬思維分解一下:創(chuàng)建出一個帶有參數(shù)的BrandDTO對像模擬對後臺傳遞參數(shù)我所遇到的問題我們都知道,在我們使用Mybatis-plus中進(jìn)行多表操作是極其困難的,如果你不使用Mybatis-plus-join這一類的工具,你只能去配置對應(yīng)的Mapper.xml文件,配置又臭又長的ResultMap,然後再寫對應(yīng)的sql語句,這種方法雖然看上去很麻煩,但具有很高的靈活性,可以讓我們

SpringBoot和SpringMVC都是Java開發(fā)中常用的框架,但它們之間有一些明顯的差異。本文將探究這兩個框架的特點(diǎn)和用途,並對它們的差異進(jìn)行比較。首先,我們來了解一下SpringBoot。 SpringBoot是由Pivotal團(tuán)隊開發(fā)的,它旨在簡化基於Spring框架的應(yīng)用程式的建立和部署。它提供了一種快速、輕量級的方式來建立獨(dú)立的、可執(zhí)行

一、@Import引入普通類別@Import引入普通的類別可以幫助我們把普通的類別定義為Bean。 @Import可以加入在@SpringBootApplication(啟動類別)、@Configuration(配置類別)、@Component(組件類別)對應(yīng)的類別上。注意:@RestController、@Service、@Repository都屬於@Component@SpringBootApplication@Import(ImportBean.class)//透過@Import註解把ImportBean
