SpringBoot 集成Quartz发布、修改、暂停、删除定时任务

前言

>>>SpringBoot自带schedule

SpringBoot本身支持表达式等多种定时任务,使用起来也很方便,但是如果使用复杂的任务操作时,SpringBoot自带的稍显不足,使用SpringBoot自带的定时任务, 只需要在程序启动的时候加上@EnableScheduling

@Scheduled(cron="0/20 * * * * ?")
public void task(){
      System.out.println("task - 20秒执行一次");
}

使用十分简单,本文不过多陈述

>>>为什么使用Quartz

多任务情况下,quartz更容易管理,可以实现动态配置 ,可随时删除和修改定时任务,方便使用

1、SpringBoot集成Quartz
项目目录:
1.png

由于一些quartz集成需要导入quartz自带的一些mysql库,使用起来稍显负复杂,本文采用自己创建任务库来管理简单的定时任务
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">
    <parent>
        <artifactId>learn</artifactId>
        <groupId>com.lss</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <quertz.version>2.2.1</quertz.version>
    </properties>
    <artifactId>quartz</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>${quertz.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8
    username: root
    password: 12345678
    driverClassName: com.mysql.jdbc.Driver
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    database-platform: org.hibernate.dialect.MySQL5Dialect
  quartz:
    #相关属性配置
    properties:
      org:
        quartz:
          #          dataSource:
          #            default:
          #              driver: com.mysql.jdbc.Driver
          #              URL: jdbc:mysql://localhost:3306/jobconfig?useUnicode=true&characterEncoding=utf8
          #              user: root
          #              password: 12345678
          #              maxConnections: 5
          scheduler:
            instanceName: DefaultQuartzScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: qrtz_
            isClustered: false
            clusterCheckinInterval: 10000
            useProperties: true
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true

    #数据库方式
    job-store-type: JDBC
    #初始化表结构
    jdbc:
      initialize-schema: NEVER
2、项目配置
2.1、新建ScheduleQuartzJob类,实现quartz的Job接口
package com.lss.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

@Slf4j
public class ScheduleQuartzJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        String group = context.getJobDetail().getJobDataMap().get("group").toString();
        String name = context.getJobDetail().getJobDataMap().get("name").toString();
        log.info("执行了task...group:{}, name:{}", group, name);
        // 可在此执行定时任务的具体业务
        // ...
    }
}
2.2、新建ScheduleJobPo类,对应数据库
package com.lss.entity.po;
import lombok.Data;
import javax.persistence.*;

@Data
@Table(name = "tbl_schedule_job")
@Entity
public class ScheduleJobPo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
   
    // 任务group名称
    @Column(name = "group_name")
    private String groupName;

    // 任务job名称
    @Column(name = "job_name")
    private String jobName;

    // cron表达式
    private String cron;

    // 0 - 代表正在执行  1 - 已删除  2 - 暂停
    @Column(name = "status")
    private Integer status;

    @Column(name = "create_time")
    private Long createTime;

    @Column(name = "modified_time")
    private Long modifiedTime;
}
2.3、新建ScheduleJobDaoRepository类,调用数据库
package com.lss.dao;
import com.lss.entity.po.ScheduleJobPo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface ScheduleJobDaoRepository extends JpaRepository<ScheduleJobPo, Integer>, JpaSpecificationExecutor<ScheduleJobPo> {
    public ScheduleJobPo findByIdAndStatus(Integer id, Integer status);

    public List<ScheduleJobPo> findAllByStatus(Integer status);

    public List<ScheduleJobPo> findByGroupNameAndJobNameAndStatus(String groupName, String jobName, Integer status);

    public List<ScheduleJobPo> findAllByStatusInOrderByCreateTimeDesc(List<Integer> statusList);
}
2.4、新建ScheduleJobService类,业务实现层
package com.lss.service;

import com.lss.dao.ScheduleJobDaoRepository;
import com.lss.entity.model.ScheduleJobModel;
import com.lss.entity.po.ScheduleJobPo;
import com.lss.job.ScheduleQuartzJob;
import com.lss.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class ScheduleJobService {

    // 获取工厂类
    private StdSchedulerFactory sf = new StdSchedulerFactory();

    @Autowired
    private ScheduleJobDaoRepository scheduleJobDaoRepository;

    // 项目重启后,初始化原本已经运行的定时任务
    @PostConstruct
    public void init(){
        List<ScheduleJobPo> poList = scheduleJobDaoRepository.findAllByStatus(0);
        poList.forEach(po -> {
            startScheduleByInit(po);
        });
    }

    /**
     * 初始化时开启定时任务
     */
    private void startScheduleByInit(ScheduleJobPo po){
        try {
            Scheduler scheduler = sf.getScheduler();
            startJob(scheduler, po.getGroupName(), po.getJobName(), po.getCron());
            scheduler.start();
        }catch (Exception e){
            log.error("exception:{}", e);
        }
    }

    /**
     * 开启定时任务
     * @param model
     */
    public void startSchedule(ScheduleJobModel model) {
        if (StringUtils.isEmpty(model.getGroupName()) || StringUtils.isEmpty(model.getJobName()) || StringUtils.isEmpty(model.getCron())){
            throw new RuntimeException("参数不能为空");
        }
        List<ScheduleJobPo> poList = scheduleJobDaoRepository.findByGroupNameAndJobNameAndStatus(model.getGroupName(), model.getJobName(), 0);
        if (!ObjectUtils.isEmpty(poList)){
            throw new RuntimeException("group和job名称已存在");
        }
        try {
            Scheduler scheduler = sf.getScheduler();
            startJob(scheduler, model.getGroupName(), model.getJobName(), model.getCron());
            scheduler.start();
            ScheduleJobPo scheduleJobPo = new ScheduleJobPo();
            scheduleJobPo.setGroupName(model.getGroupName());
            scheduleJobPo.setJobName(model.getJobName());
            scheduleJobPo.setCron(model.getCron());
            scheduleJobPo.setStatus(0);
            scheduleJobPo.setCreateTime(DateUtil.getCurrentTimeStamp());
            scheduleJobPo.setModifiedTime(DateUtil.getCurrentTimeStamp());
            scheduleJobDaoRepository.save(scheduleJobPo);
        }catch (Exception e){
            log.error("exception:{}", e);
        }

    }

    /**
     * 更新定时任务
     * @param model
     */
    public void scheduleUpdateCorn(ScheduleJobModel model) {
        if (ObjectUtils.isEmpty(model.getId()) || ObjectUtils.isEmpty(model.getCron())){
            throw new RuntimeException("定时任务不存在");
        }
        try {
            ScheduleJobPo po = scheduleJobDaoRepository.findByIdAndStatus(model.getId(), 0);
            // 获取调度对象
            Scheduler scheduler = sf.getScheduler();
            // 获取触发器
            TriggerKey triggerKey = new TriggerKey(po.getJobName(), po.getGroupName());
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            String oldTime = cronTrigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(model.getCron())) {
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(model.getCron());
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(po.getJobName(), po.getGroupName())
                        .withSchedule(cronScheduleBuilder).build();
                // 更新定时任务
                scheduler.rescheduleJob(triggerKey, trigger);
                po.setCron(model.getCron());
                // 更新数据库
                scheduleJobDaoRepository.save(po);
            }
        }catch (Exception e){
            log.info("exception:{}", e);
        }

    }
    /**
     * 任务 - 暂停
     */
    public void schedulePause(ScheduleJobModel model) {
        if (ObjectUtils.isEmpty(model.getId())){
            throw new RuntimeException("定时任务不存在");
        }
        ScheduleJobPo po = scheduleJobDaoRepository.findByIdAndStatus(model.getId(), 0);
        if (ObjectUtils.isEmpty(po)){
            throw new RuntimeException("定时任务不存在");
        }
        try {
            Scheduler scheduler = sf.getScheduler();
            JobKey jobKey = new JobKey(po.getJobName(), po.getGroupName());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.pauseJob(jobKey);
            po.setStatus(2);
            scheduleJobDaoRepository.save(po);
        }catch (Exception e){
            log.error("exception:{}", e);
        }
    }
    /**
     * 任务 - 恢复
     */

    public void scheduleResume(ScheduleJobModel model) {
        if (ObjectUtils.isEmpty(model.getId())){
            throw new RuntimeException("定时任务不存在");
        }
        ScheduleJobPo po = scheduleJobDaoRepository.findByIdAndStatus(model.getId(), 2);
        if (ObjectUtils.isEmpty(po)){
            throw new RuntimeException("定时任务不存在");
        }
        try {
            Scheduler scheduler = sf.getScheduler();
            JobKey jobKey = new JobKey(po.getJobName(), po.getGroupName());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.resumeJob(jobKey);
            po.setStatus(0);
            scheduleJobDaoRepository.save(po);
        }catch (Exception e){
            log.error("exception:{}", e);
        }
    }
    /**
     * 任务 - 删除一个定时任务
     */
    public void scheduleDelete(ScheduleJobModel model) {
        if (ObjectUtils.isEmpty(model.getId())){
            throw new RuntimeException("定时任务不存在");
        }
        ScheduleJobPo po = scheduleJobDaoRepository.findByIdAndStatus(model.getId(), 0);
        if (ObjectUtils.isEmpty(po)){
            throw new RuntimeException("定时任务不存在");
        }
        try {
            Scheduler scheduler = sf.getScheduler();
            JobKey jobKey = new JobKey(po.getJobName(), po.getGroupName());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.deleteJob(jobKey);
            po.setStatus(1);
            scheduleJobDaoRepository.save(po);
        }catch (Exception e){
            log.error("exception:{}", e);
        }
    }

    /**
     * 删除所有定时任务
     */
    public void scheduleDeleteAll() {
        try {
            Scheduler scheduler = sf.getScheduler();
            // 获取有所的组
            List<String> jobGroupNameList = scheduler.getJobGroupNames();
            for (String jobGroupName : jobGroupNameList) {
                GroupMatcher<JobKey> jobKeyGroupMatcher = GroupMatcher.jobGroupEquals(jobGroupName);
                Set<JobKey> jobKeySet = scheduler.getJobKeys(jobKeyGroupMatcher);
                for (JobKey jobKey : jobKeySet) {
                    String jobName = jobKey.getName();
                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    if (jobDetail == null)
                        return;
                    scheduler.deleteJob(jobKey);
                    // 更新数据库
                    List<ScheduleJobPo> poList = scheduleJobDaoRepository.findByGroupNameAndJobNameAndStatus(jobGroupName, jobName, 0);
                    poList.forEach(po -> {
                        po.setStatus(1);
                        scheduleJobDaoRepository.save(po);
                    });
                    log.info("group:{}, job:{}", jobGroupName, jobName);
                }
            }
        }catch (Exception e){
            log.error("exception:{}", e);
        }
    }
    // 开启任务
    private void startJob(Scheduler scheduler, String group, String name, String cron) throws SchedulerException {
        // 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
        // 在map中可传入自定义参数,在job中使用
        JobDataMap map = new JobDataMap();
        map.put("group", group);
        map.put("name", name);
        // JobDetail 是具体Job实例
        JobDetail jobDetail = JobBuilder.newJob(ScheduleQuartzJob.class).withIdentity(name, group)
                .usingJobData(map)
                .build();
        // 基于表达式构建触发器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        // CronTrigger表达式触发器 继承于Trigger
        // TriggerBuilder 用于构建触发器实例
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}
2.5、新建ScheduleJobModel类,参数调用
package com.lss.entity.model;

import lombok.Data;

@Data
public class ScheduleJobModel {

    private Integer id;

    private String groupName;

    private String jobName;

    private String cron;
}
2.6、新建TestController类,暴露restful接口,实现接口调用
package com.lss.controller;

import com.lss.entity.model.ScheduleJobModel;
import com.lss.service.ScheduleJobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/api")
public class TestController {

    @Autowired
    private ScheduleJobService scheduleJobService;

    /**
     * 开启
     * @param model
     * @return
     */
    @PostMapping("start")
    public String startSchedule(@RequestBody ScheduleJobModel model){
        scheduleJobService.startSchedule(model);
        return "ok";
    }

    /**
     * 更新
     * @param model
     * @return
     */
    @PostMapping("update")
    public String scheduleUpdateCorn(@RequestBody ScheduleJobModel model){
        scheduleJobService.scheduleUpdateCorn(model);
        return "ok";
    }

    /**
     * 暂停
     * @param model
     * @return
     */
    @PostMapping("/pause")
    public String schedulePause(@RequestBody ScheduleJobModel model){
        scheduleJobService.schedulePause(model);
        return "ok";
    }

    /**
     * 恢复
     * @param model
     * @return
     */
    @PostMapping("/resume")
    public String scheduleResume(@RequestBody ScheduleJobModel model){
        scheduleJobService.scheduleResume(model);
        return "ok";
    }

    /**
     * 删除一个定时任务
     * @param model
     * @return
     */
    @PostMapping("/delete")
    public String scheduleDelete(@RequestBody ScheduleJobModel model){
        scheduleJobService.scheduleDelete(model);
        return "ok";
    }

    /**
     * 删除所有定时任务
     * @param model
     * @return
     */
    @PostMapping("deleteAll")
    public String scheduleDeleteAll(@RequestBody ScheduleJobModel model){
        scheduleJobService.scheduleDeleteAll();
        return "ok";
    }
}
2.7、新建DateUtil类,获取当前日期
package com.lss.util;

public class DateUtil {
    /**
     * 得到当前时间戳
     * @return
     */
    public static Long getCurrentTimeStamp() {
        long timeMillis = System.currentTimeMillis();
        return timeMillis;
    }
}
以上是该项目的所有配置
3、启动项目
3.1、调用 /api/start 接口

curl -H "Content-Type:application/json" -X POST --data '{"groupName":"group","jobName":"job","cron":"0/5 * * * * ?"}' http://localhost:8080/api/start
看控制台日志,发现每隔5秒调用一次定时服务

2.png

3.2、调用 /api/update 接口

curl -H "Content-Type:application/json" -X POST --data '{"id":1, "cron":"0/8 * * * * ?"}' http://localhost:8080/api/update
看控制台日志,发现每隔8秒调用一次定时服务

3.png

这个接口实现是先调用的数据库,然后去quartz中更新了该定时任务,也可直接传入groupName和jobName去quartz中查询出来对应的定时任务,更新后再去更新数据库

后一种方法更好,实现也很容易实现,学习的朋友们可参照接口 /api/deleteAll 中的一些业务操作实现该业务,本文不在过多陈述
3.3、调用 /api/pause 接口

curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/api/pause
看控制台日志,发现定时任务已停止

3.4、调用 /api/resume 接口

curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/api/resume
看控制台日志,发现定时任务已恢复

3.5、调用 /api/delete 接口

curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/api/delete
发现定时任务已删除

3.6、调用 /api/deleteAll 接口

curl -H "Content-Type:application/json" -X POST --data '{}' http://localhost:8080/api/deleteAll
此时删除所有的定时任务

附录一些cron表达式,方便记忆
cron表达式
字段 允许值 允许的特殊字符
0-59 , - * /
0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L C #
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
(可选)留空 1970-2099 , - * /
每个符号的意义

* 表示所有值;
? 表示未说明的值,即不关心它为何值;
- 表示一个指定的范围;
, 表示附加一个可能值
/ 符号前表示开始时间,符号后表示每次递增的值;
L("last") ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。
W("weekday") 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个 月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第 16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在 day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。
# 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天。

示例
表达式 含义
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
30 * * * * ? 每半分钟触发任务
30 10 * * * ? 每小时的10分30秒触发任务
30 10 1 * * ? 每天1点10分30秒触发任务
30 10 1 20 * ? 每月20号1点10分30秒触发任务
30 10 1 20 10 ? * 每年10月20号1点10分30秒触发任务
30 10 1 20 10 ? 2011 2011年10月20号1点10分30秒触发任务
30 10 1 ? 10 * 2011 2011年10月每天1点10分30秒触发任务
30 10 1 ? 10 SUN 2011 2011年10月每周日1点10分30秒触发任务
15,30,45 * * * * ? 每15秒,30秒,45秒时触发任务
15-45 * * * * ? 15到45秒内,每秒都触发任务
15/5 * * * * ? 每分钟的每15秒开始触发,每隔5秒触发一次
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
0 0/3 * * * ? 每小时的第0分0秒开始,每三分钟触发一次
0 15 10 ? * MON-FRI 星期一到星期五的10点15分0秒触发任务
0 15 10 L * ? 每个月最后一天的10点15分0秒触发任务
0 15 10 LW * ? 每个月最后一个工作日的10点15分0秒触发任务
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发任务
0 15 10 ? * 5#3 每个月第三周的星期四的10点15分0秒触发任务
以上就是本文的所有内容,如有不对,欢迎指正,欢迎讨论
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • 本文是根据 Quartz定时器官方文档翻译的,只翻译了第1到第10课,如有翻译不精确的地方,请读者指正,互相学习,...
    ChinaXieShuai阅读 8,146评论 1 19
  • 官方文档 1. 使用quartz 在开始使用scheduler前,首先使用SchedulerFactory创建一个...
    jellyb阅读 12,151评论 0 1
  • 一点知识 在JAVA开发领域,目前可以通过以下几种方式进行定时任务: Timer:jdk中自带的一个定时调度类,可...
    雅倩兰爸爸阅读 3,749评论 1 28
  • 前言 项目新需求需要定时处理数据,但是原来没有这个模块,奉命开发,之前接触过Quartz。但是有些忘记了,所以过...
    浅笑丨无痕阅读 1,397评论 0 4
  • Quartz是什么 Quartz是一个开源的作业调度包,能够运行在几乎任何java项目中,小到单机应用,大到电商系...
    零度沸腾_yjz阅读 2,868评论 0 9