纯手写SpringBoot教案系列-SpringBoot进阶课程-任务

五.任务

1. 异步任务(多线程)

@EnableAsync

@Async

2. 定时任务

2.1 普通定时任务

@EnableScheduling

@Scheduled(cron = “”)

Cron表达式参数分别表示:

  • 秒(0~59) 例如0/5表示每5秒
  • 分(0~59)
  • 时(0~23)
  • 日(0~31)的某天,需计算
  • 月(0~11)
  • 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)

@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。

实战

首先在SpringBootApplication上开启任务功能

@EnableScheduling

然后在需要执行的方法上添加@Scheduled(cron = “”)注解

以下是源码,每隔5秒运行一次

@Service
public class MyTask {
    @Scheduled(cron = "0/5 * * * * ?")
    public void doTask(){
        Logger logger = LoggerFactory.getLogger(MyTask.class);
        logger.info("我运行了");
    }
}

2.2 动态添加任务

详细参照

https://www.cnblogs.com/mmzs/p/10161936.html

https://blog.csdn.net/qq_34125349/article/details/77430956

2.2.1 在执行过程中修改周期

@RestController
public class TaskCtrl implements SchedulingConfigurer {
    //时间:秒,分钟,小时,日期,月份,星期,年
    private String expression = "0/1 * * * * *";

    //编写更改调度时间的方法
    @RequestMapping("change")
    public String changeExpression() {
        expression = "0/2 * * * * *";
        return "changeExpression";
    }


    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        //定时任务要执行的方法
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("changeTask" + new Date());
            }
        };
        /**
         * 调度实现的时间控制
         */
        Trigger trigger = new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                CronTrigger cronTrigger = new CronTrigger(expression);
                return cronTrigger.nextExecutionTime(triggerContext);
            }
        };
        scheduledTaskRegistrar.addTriggerTask(task, trigger);
    }

}

2.2.2 手动启动/终止定时任务

可能会报错,但是不影响运行

@RestController
public class DynamicTaskCtrl {

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private ScheduledFuture<?> future;

    @Bean
    public ThreadPoolTaskScheduler trPoolTaskScheduler(){
        return new ThreadPoolTaskScheduler();
    }
    /**
     * 1,定义一个方法实现定时任务的启动
     * 2,定义一个方法实现用于终止定时任务
     * 3,修改定时任务时间:changeCron
     */
    /**
     * 启动定时器
     * @return
     */
    @RequestMapping("startTest")
    public String StartTest(){
        /**
         * task:定时任务要执行的方法
         * trigger:定时任务执行的时间
         */
        future=threadPoolTaskScheduler.schedule(new myRunable(),new CronTrigger("0/5 * * * * *") );

        return "startTest";
    }

    /**
     * 停止定时任务
     * @return
     */
    @RequestMapping("endTask")
    public String endTask(){
        if(future!=null){
            future.cancel(true);
        }
        System.out.println("endTask");
        return "endTask";
    }

    /**
     * 改变调度的时间
     * 步骤:
     * 1,先停止定时器
     * 2,在启动定时器
     */
    @RequestMapping("changeTask")
    public String changeTask(){
        //停止定时器
        endTask();
        //定义新的执行时间
        future=threadPoolTaskScheduler.schedule(new myRunable(),new CronTrigger("0/10 * * * * *") );
        //启动定时器
        StartTest();
        System.out.println("changeTask");
        return "changeTask";
    }

    /**
     * 定义定时任务执行的方法
     * @author Admin
     *
     */
    public class myRunable implements Runnable{
        @Override
        public void run() {
            System.out.println("定时任务要执行的方法"+new Date());
        }

    }
}

如果要消除报错,我们可以指定注入哪一个具体的Bean

@Qualifier("trPoolTaskScheduler")

2.3 多线程异步任务(略,和第一点的结合注解)

2.4 整合Quartz

以上我们使用的都是SpringBoot自带的Schedule框架,采用的是SimpleScheduleConfiguration

接下来我们来讲解怎么整合著名的Quartz

2.4.1 导入quartz依赖


<dependency>
    <groupId>org.quartz-schedulergroupId>
    <artifactId>quartzartifactId>
    <version>2.3.0version>
dependency>

2.4.2 构建动态添加工具类

public class QuartzManager {

    private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
    private static String JOB_GROUP_NAME = "MY_JOBGROUP_NAME";
    private static String TRIGGER_GROUP_NAME = "MY_TRIGGERGROUP_NAME";

    /**
     * @param jobName 任务名
     * @param cls     任务
     * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void addJob(String jobName, Class cls, String cron) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // 按新的cronExpression表达式构建一个新的trigger
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
                    .withSchedule(scheduleBuilder).build();

            // 交给scheduler去调度
            sched.scheduleJob(job, trigger);

            // 启动
            if (!sched.isShutdown()) {
                sched.start();
                System.err.println("添加任务:" + jobName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*带参数传入的任务创建*/
    public static void addJob(String jobName, Class cls, String cron, Map<String,Object> params) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
            /*写入参数方便调度*/
            if(params!=null&&params.size()!=0){
                Iterator<String> iterator = params.keySet().iterator();
                while(iterator.hasNext()){
                    String key = iterator.next();
                    Object value = params.get(key);
                    job.getJobDataMap().put(key,value);
                }
            }
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // 按新的cronExpression表达式构建一个新的trigger
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
                    .withSchedule(scheduleBuilder).build();

            // 交给scheduler去调度
            sched.scheduleJob(job, trigger);

            // 启动
            if (!sched.isShutdown()) {
                sched.start();
                System.err.println("添加任务:" + jobName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName          任务名
     * @param jobGroupName     任务组名
     * @param triggerName      触发器名
     * @param triggerGroupName 触发器组名
     * @param jobClass         任务
     * @Description: 添加一个定时任务
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
                              Class jobClass, String cron) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            JobDetail job = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // 按新的cronExpression表达式构建一个新的trigger
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName, triggerGroupName)
                    .withSchedule(scheduleBuilder).build();
            sched.scheduleJob(job, trigger);
            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @Description: 修改一个任务的触发时间(使用默认的任务组名 , 触发器名 , 触发器组名)
     */
    public static void modifyJobTime(String jobName, String cron) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);

        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                // 按新的trigger重新设置job执行
                sched.rescheduleJob(triggerKey, trigger);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * @param oldJobName :原任务名
     * @param jobName
     * @param jobclass
     * @param cron
     * @Description:修改任务,(可以修改任务名,任务类,触发时间) 原理:移除原来的任务,添加新的任务
     * @date 2018年5月23日 上午9:13:10
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void modifyJob(String oldJobName, String jobName, Class jobclass, String cron) {
        /*
         * removeJob(oldJobName);
         * addJob(jobName, jobclass, cron);
         * System.err.println("修改任务"+oldJobName);
         */
        TriggerKey triggerKey = TriggerKey.triggerKey(oldJobName, TRIGGER_GROUP_NAME);
        JobKey jobKey = JobKey.jobKey(oldJobName, JOB_GROUP_NAME);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            Trigger trigger = (Trigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(jobKey);// 删除任务
            System.err.println("移除任务:" + oldJobName);

            JobDetail job = JobBuilder.newJob(jobclass).withIdentity(jobName, JOB_GROUP_NAME).build();
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // 按新的cronExpression表达式构建一个新的trigger
            Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
                    .withSchedule(scheduleBuilder).build();

            // 交给scheduler去调度
            sched.scheduleJob(job, newTrigger);

            // 启动
            if (!sched.isShutdown()) {
                sched.start();
                System.err.println("添加新任务:" + jobName);
            }
            System.err.println("修改任务【" + oldJobName + "】为:" + jobName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    /**
     * @param triggerName
     * @param triggerGroupName
     * @Description: 修改一个任务的触发时间
     */
    public static void modifyJobTime(String triggerName, String triggerGroupName, String cron) {
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                // trigger已存在,则更新相应的定时设置
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                // 按新的trigger重新设置job执行
                sched.resumeTrigger(triggerKey);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @Description 移除一个任务(使用默认的任务组名 , 触发器名 , 触发器组名)
     */
    public static void removeJob(String jobName) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            Trigger trigger = (Trigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(jobKey);// 删除任务
            System.err.println("移除任务:" + jobName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @Description: 移除一个任务
     */
    public static void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, triggerGroupName);
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(jobKey);// 删除任务
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @Description:暂停一个任务(使用默认组名)
     */
    public static void pauseJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @Description:暂停一个任务
     */
    public static void pauseJob(String jobName, String jobGroupName) {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jobName
     * @Description:恢复一个任务(使用默认组名)
     */
    public static void resumeJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @Description:恢复一个任务
     * @date 2018年5月17日 上午9:56:09
     */
    public static void resumeJob(String jobName, String jobGroupName) {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description:启动所有定时任务
     */
    public static void startJobs() {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description 关闭所有定时任务
     */
    public static void shutdownJobs() {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            if (!sched.isShutdown()) {
                sched.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @Description: 立即运行任务,这里的立即运行,只会运行一次,方便测试时用。
     * @date 2018年5月17日 上午10:03:26
     */
    public static void triggerJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @Description: 立即运行任务,这里的立即运行,只会运行一次,方便测试时用。
     * @date 2018年5月17日 上午10:03:26
     */
    public static void triggerJob(String jobName, String jobGroupName) {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jobName 触发器名
     * @Description: 获取任务状态
     * NONE: 不存在
     * NORMAL: 正常
     * PAUSED: 暂停
     * COMPLETE:完成
     * ERROR : 错误
     * BLOCKED : 阻塞
     * @date 2018年5月21日 下午2:13:45
     */
    public static String getTriggerState(String jobName) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);
        String name = null;
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            TriggerState triggerState = sched.getTriggerState(triggerKey);
            name = triggerState.name();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return name;
    }

    /**
     * @param cron
     * @Description:获取最近8次执行时间
     * @date 2018年5月24日 下午5:13:03
     */
    public static List<String> getRecentTriggerTime(String cron) {
        List<String> list = new ArrayList<String>();
        try {
            CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
            cronTriggerImpl.setCronExpression(cron);
            // 这个是重点,一行代码搞定
            List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 8);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (Date date : dates) {
                list.add(dateFormat.format(date));
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return list;
    }

}

2.4.3 创建Job文件

@Service
public class SignJob implements Job {
    @Autowired
    private MyMapper myMapper;

    @Transactional
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 使得job对象可以通过注解实现依赖注入
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        /*将所有用户签到重置*/
        myMapper.updateSign();
        System.out.println("重置签到");
    }
}

2.4.4 Service中调用Job

 @PostConstruct
    @Transactional
    public void init() throws JobExecutionException {
        /*装载版本信息*/
        System.out.println("装载版本号...");
        String version = getConfigByName("version").getConfigValue();
        servletContext.setAttribute("version",version);
        /*创建最基本的定时任务*/
        System.out.println("创建定时任务...");
        QuartzManager.addJob("smsJob", SmsJob.class,"0 0 0 * * ?");
        String smsJob = QuartzManager.getTriggerState("smsJob");
        System.out.println("创建短信任务完成,状态为:"+smsJob);
        QuartzManager.addJob("signJob", SignJob.class,"0 0 6 * * ?");
        String signJob = QuartzManager.getTriggerState("signJob");
        System.out.println("创建签到任务完成,状态为:"+signJob);
        /*QuartzManager.addJob("weatherJob", WeatherJob.class,"0 0 0/1 * * ?");
        String weatherJob = QuartzManager.getTriggerState("weatherJob");
        System.out.println("创建天气任务完成,状态为:"+weatherJob);*/
    }

2.5 注意事项

如果在传入cron时格式出现错误,则定时任务会停止,即使重新修改正确仍不能恢复,此时只能重新启动项目才能恢复。

3. 邮件任务

  1. 引入mail的starter(在I/O中)

     <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-starter-mailartifactId>
        dependency>
    
  2. 配置邮箱的地址,用户名密码(在QQ邮箱账户中开启pop3支持,并获取备用密码,不是qq邮箱本身密码)

    spring:
      #邮箱相关
      mail:
        username: 自己的邮箱地址
        password: 保密嘿嘿
        host: smtp.qq.com
    
  3. 在单元测试中测试发送简单的邮件

    @Autowired
        JavaMailSenderImpl mailSender;
    
        @Test
        public void contextLoads() throws SQLException {
            SimpleMailMessage message = new SimpleMailMessage();
            //设置邮件标题
            message.setSubject("hello beijing");
            //设置邮件内容
            message.setText("你好北京");
            //设置收件人
            message.setTo("[email protected]");
            //设置发件人
            message.setFrom("[email protected]");
            mailSender.send(message);
    
           /* System.out.println(dataSource.getClass());
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            connection.close();*/
        }
    
  4. 当然,在有的时候会发送不成功,提示我们不是安全连接(ssl),这时候需要设置邮件为安全连接

    spring:
     #邮箱相关
      mail:
        username: [email protected]
        password: vshucimzjnxybgia
        host: smtp.qq.com
        properties:
          mail.smtp.ssl.enable: true
    
  5. 测试成功!

你可能感兴趣的:(纯手写SpringBoot教案,SpringBoot)