SpringBoot+Quartz+javaEmail 定时发送邮件 及Job中无法自动装配Spring bean的解决方案

Springboot 用quartz定时发送邮件

一、目录结构

SpringBoot+Quartz+javaEmail 定时发送邮件 及Job中无法自动装配Spring bean的解决方案_第1张图片

二、开发思路

1. 先添加springboot的依赖

2. 添加数据库表来管理定时任务的Job,并且生成实体和Mapper

3.创建job任务,写好定时器的任务逻辑等待调用

4.创建service层管理啊job,例如job的开启关闭。并且在服务层中将JobDetail和Trigger交予scheduler安排触发

5.创建ApplicationInit实现CommandLineRunner接口,服务启动时开启job服务

6.处理Job中无法调用spring的bean导致的NullpointException(无法自动装配问题)

三、项目配置

3.1 pom.xml



<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.4.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.alipaygroupId>
    <artifactId>demoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>demoname>
    <description>Demo project for Spring Bootdescription>

    <properties>
        <java.version>1.8java.version>
    properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
            <plugin>
                <groupId>org.mybatis.generatorgroupId>
                <artifactId>mybatis-generator-maven-pluginartifactId>
                <version>1.3.2version>
                <configuration>
                    <configurationFile>src/main/resources/mybatis-generator-config.xmlconfigurationFile>
                    <verbose>trueverbose>
                    <overwrite>trueoverwrite>
                configuration>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifactsid>
                        <goals>
                            <goal>generategoal>
                        goals>
                    execution>
                executions>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generatorgroupId>
                        <artifactId>mybatis-generator-coreartifactId>
                        <version>1.3.2version>
                    dependency>
                dependencies>
            plugin>
        plugins>

    build>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.11version>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>1.3.2version>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
            <version>1.5.1.RELEASEversion>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.9version>
        dependency>
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
            <exclusions>
                <exclusion>
                    <groupId>redis.clientsgroupId>
                    <artifactId>jedisartifactId>
                exclusion>
                <exclusion>
                    <groupId>io.lettucegroupId>
                    <artifactId>lettuce-coreartifactId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>redis.clientsgroupId>
            <artifactId>jedisartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-quartzartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-context-supportartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-mailartifactId>
        dependency>

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

    dependencies>



project>

yml配置文件

#日志
logging:
  level:
    com:
      alipay:
        demo:
          mapper: debug
          quartz: debug
  file: quartz-service.log

#Mybatis
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapperLocations: /mapper/*.xml
  typeAliasesPackage: com.hiqiblog.entity
#端口
server:
  port: 8090
  tomcat:
    uri-encoding: utf-8

spring:
  #  定时发送邮件
  mail:
  #发送者qq邮箱的授权码  如有疑问请移步https://blog.csdn.net/qq_33407429/article/details/89889912
    password: ******
    default-encoding: UTF-8
    host: smtp.qq.com
    properties:
      mail:
        smtp:
          starttls:
            enable: true
            required: true
          auth: true
    #发送者qq邮箱
    username: *********@qq.com
    #durid框架
  datasource:
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    driver-class-name: com.mysql.cj.jdbc.Driver
    exceptionSorter: true
    filters: stat,wall,log4j
    initialSize: 5
    maxActive: 20
    maxPoolPreparedStatementPerConnectionSize: 20
    maxWait: 60000
    minEvictableIdleTimeMillis: 300000
    minIdle: 5
    password: 123456
    poolPreparedStatements: true
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: true
    timeBetweenEvictionRunsMillis: 60000
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    useGlobalDataSourceStat: true
    username: root
    validationQuery: SELECT 1 FROM DUAL
  mvc:

    static-path-pattern: /static/**
    view:
      prefix: /templates/
      suffix: .html
 #缓存
  redis:
    database: 3
    host: 127.0.0.1
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: -1
        min-idle: 0
      shutdown-timeout: 100
    password: ''
    port: 6379
    timeout: 3600
#Quartz
quartz:
  #相关属性配置
  properties:
    org:
      quartz:
        scheduler:
          instanceName: clusteredScheduler
          instanceId: AUTO
        jobStore:
          class: org.quartz.impl.jdbcjobstore.JobStoreTX
          driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
          tablePrefix: QRTZ_
          isClustered: true
          clusterCheckinInterval: 10000
          useProperties: false
        threadPool:
          class: org.quartz.simpl.SimpleThreadPool
          threadCount: 10
          threadPriority: 5
          threadsInheritContextClassLoaderOfInitializingThread: true
  #数据库方式
  job-store-type: jdbc



3.2 配置结束后,我们应该在数据库中建立一张简单的表去管理一下这些定时执行的job

-- ----------------------------
-- Table structure for `tbl_quartz_job`
-- ----------------------------
DROP TABLE IF EXISTS `tbl_quartz_job`;
CREATE TABLE `tbl_quartz_job` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `job_name` varchar(50) NOT NULL COMMENT '任务名称',
  `job_group` varchar(50) NOT NULL COMMENT '任务分组',
  `job_class_name` varchar(100) NOT NULL COMMENT '执行类',
  `cron_expression` varchar(100) NOT NULL COMMENT 'cron表达式',
  `trigger_state` varchar(15) NOT NULL COMMENT '任务状态',
  `old_job_name` varchar(50) NOT NULL DEFAULT '' COMMENT '修改之前的任务名称',
  `old_job_group` varchar(50) NOT NULL DEFAULT '' COMMENT '修改之前的任务分组',
  `description` varchar(100) NOT NULL COMMENT '描述',
  PRIMARY KEY (`id`),
  UNIQUE KEY `un_group_name` (`job_group`,`job_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='定时任务';

建完表后,用Mybatis的逆向生成工具生成实体和Mapper

QuartzJobsMapper.xml



<mapper namespace="com.hiqiblog.mapper.QuartzJobsMapper" >
  <resultMap id="BaseResultMap" type="com.hiqiblog.entity.QuartzJobs" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="job_name" property="jobName" jdbcType="VARCHAR" />
    <result column="job_group" property="jobGroup" jdbcType="VARCHAR" />
    <result column="job_class_name" property="jobClassName" jdbcType="VARCHAR" />
    <result column="cron_expression" property="cronExpression" jdbcType="VARCHAR" />
    <result column="trigger_state" property="triggerState" jdbcType="VARCHAR" />
    <result column="old_job_name" property="oldJobName" jdbcType="VARCHAR" />
    <result column="old_job_group" property="oldJobGroup" jdbcType="VARCHAR" />
    <result column="description" property="description" jdbcType="VARCHAR" />
  resultMap>
  <sql id="Base_Column_List" >
    id, job_name, job_group, job_class_name, cron_expression, trigger_state, old_job_name, 
    old_job_group, description
  sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from tbl_quartz_job
    where id = #{id,jdbcType=INTEGER}
  select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from tbl_quartz_job
    where id = #{id,jdbcType=INTEGER}
  delete>
  <insert id="insert" parameterType="com.hiqiblog.entity.QuartzJobs" >
    insert into tbl_quartz_job ( job_name, job_group,
      job_class_name, cron_expression, trigger_state, 
      old_job_name, old_job_group, description
      )
    values (#{jobName,jdbcType=VARCHAR}, #{jobGroup,jdbcType=VARCHAR},
      #{jobClassName,jdbcType=VARCHAR}, #{cronExpression,jdbcType=VARCHAR}, #{triggerState,jdbcType=VARCHAR}, 
      #{oldJobName,jdbcType=VARCHAR}, #{oldJobGroup,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}
      )
  insert>
  <insert id="insertSelective" parameterType="com.hiqiblog.entity.QuartzJobs" >
    insert into tbl_quartz_job
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      if>
      <if test="jobName != null" >
        job_name,
      if>
      <if test="jobGroup != null" >
        job_group,
      if>
      <if test="jobClassName != null" >
        job_class_name,
      if>
      <if test="cronExpression != null" >
        cron_expression,
      if>
      <if test="triggerState != null" >
        trigger_state,
      if>
      <if test="oldJobName != null" >
        old_job_name,
      if>
      <if test="oldJobGroup != null" >
        old_job_group,
      if>
      <if test="description != null" >
        description,
      if>
    trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      if>
      <if test="jobName != null" >
        #{jobName,jdbcType=VARCHAR},
      if>
      <if test="jobGroup != null" >
        #{jobGroup,jdbcType=VARCHAR},
      if>
      <if test="jobClassName != null" >
        #{jobClassName,jdbcType=VARCHAR},
      if>
      <if test="cronExpression != null" >
        #{cronExpression,jdbcType=VARCHAR},
      if>
      <if test="triggerState != null" >
        #{triggerState,jdbcType=VARCHAR},
      if>
      <if test="oldJobName != null" >
        #{oldJobName,jdbcType=VARCHAR},
      if>
      <if test="oldJobGroup != null" >
        #{oldJobGroup,jdbcType=VARCHAR},
      if>
      <if test="description != null" >
        #{description,jdbcType=VARCHAR},
      if>
    trim>
  insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.hiqiblog.entity.QuartzJobs" >
    update tbl_quartz_job
    <set >
      <if test="jobName != null" >
        job_name = #{jobName,jdbcType=VARCHAR},
      if>
      <if test="jobGroup != null" >
        job_group = #{jobGroup,jdbcType=VARCHAR},
      if>
      <if test="jobClassName != null" >
        job_class_name = #{jobClassName,jdbcType=VARCHAR},
      if>
      <if test="cronExpression != null" >
        cron_expression = #{cronExpression,jdbcType=VARCHAR},
      if>
      <if test="triggerState != null" >
        trigger_state = #{triggerState,jdbcType=VARCHAR},
      if>
      <if test="oldJobName != null" >
        old_job_name = #{oldJobName,jdbcType=VARCHAR},
      if>
      <if test="oldJobGroup != null" >
        old_job_group = #{oldJobGroup,jdbcType=VARCHAR},
      if>
      <if test="description != null" >
        description = #{description,jdbcType=VARCHAR},
      if>
    set>
    where id = #{id,jdbcType=INTEGER}
  update>
  <update id="updateByPrimaryKey" parameterType="com.hiqiblog.entity.QuartzJobs" >
    update tbl_quartz_job
    set job_name = #{jobName,jdbcType=VARCHAR},
      job_group = #{jobGroup,jdbcType=VARCHAR},
      job_class_name = #{jobClassName,jdbcType=VARCHAR},
      cron_expression = #{cronExpression,jdbcType=VARCHAR},
      trigger_state = #{triggerState,jdbcType=VARCHAR},
      old_job_name = #{oldJobName,jdbcType=VARCHAR},
      old_job_group = #{oldJobGroup,jdbcType=VARCHAR},
      description = #{description,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  update>
  <select id="listJob" resultMap="BaseResultMap"  >
    select
    <include refid="Base_Column_List" />
    from tbl_quartz_job where
      job_name LIKE #{jobName}
  select>

  <select id="getJob" resultMap="BaseResultMap">
        SELECT
       <include refid="Base_Column_List" />
        FROM
          tbl_quartz_job
        WHERE
          job_group = #{jobGroup} AND job_name = #{jobName}
    select>

mapper>

package com.hiqiblog.entity;

/**
 * @author ww
 */
public class QuartzJobs {
    private Integer id;

    private String jobName;

    private String jobGroup;

    private String jobClassName;

    private String cronExpression;

    private String triggerState;

    private String oldJobName;

    private String oldJobGroup;

    private String description;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName == null ? null : jobName.trim();
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup == null ? null : jobGroup.trim();
    }

    public String getJobClassName() {
        return jobClassName;
    }

    public void setJobClassName(String jobClassName) {
        this.jobClassName = jobClassName == null ? null : jobClassName.trim();
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression == null ? null : cronExpression.trim();
    }

    public String getTriggerState() {
        return triggerState;
    }

    public void setTriggerState(String triggerState) {
        this.triggerState = triggerState == null ? null : triggerState.trim();
    }

    public String getOldJobName() {
        return oldJobName;
    }

    public void setOldJobName(String oldJobName) {
        this.oldJobName = oldJobName == null ? null : oldJobName.trim();
    }

    public String getOldJobGroup() {
        return oldJobGroup;
    }

    public void setOldJobGroup(String oldJobGroup) {
        this.oldJobGroup = oldJobGroup == null ? null : oldJobGroup.trim();
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description == null ? null : description.trim();
    }
}

QuartzJobsMapper

package com.hiqiblog.mapper;


import com.hiqiblog.entity.QuartzJobs;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;


import java.util.List;

/**
 * @author ww
 */

public interface QuartzJobsMapper {
    /**
     * 根据id删除
     * @param id
     * @return
     */
    int deleteByPrimaryKey(Integer id);
    /**
     * 插入
     * @param record
     * @return
     */
    int insert(QuartzJobs record);
    /**
     * 插入
     * @param record
     * @return
     */
    int insertSelective(QuartzJobs record);
    /**
     * 根据id查找
     * @param id
     * @return
     */
    QuartzJobs selectByPrimaryKey(Integer id);
    /**
     * 根据实体更新
     * @param record
     * @return
     */
    int updateByPrimaryKeySelective(QuartzJobs record);
    /**
     * 根据实体中的主键更新
     * @param record
     * @return
     */
    int updateByPrimaryKey(QuartzJobs record);
    /**
     * 根据jobName获取所有job
     * @param jobName
     * @return
     */
    List<QuartzJobs> listJob(String jobName);

    /**
     * 根据jobName和jobGroup查询实体
     * @param jobName
     * @param jobGroup
     * @return
     */
    QuartzJobs getJob(String jobName,String jobGroup);


}

3.3 文件生成结束,然后要创建SendEmailJob.class

package com.hiqiblog.job;

import com.hiqiblog.service.ISendEmailService;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 串行执行(去掉是并行)
 */
@DisallowConcurrentExecution
/**
 * @author ${ww}
 * @date
 */
public class SendEmailJob implements  Job {
    /**
    * 发送邮件逻辑 请移步https://blog.csdn.net/qq_33407429/article/details/89889912
    * 如果没有3.6的解决 这里的ISendEmailService将无法自动装配,会导致空指针错误
    * sendEmailService=null
    */
    @Autowired
     ISendEmailService sendEmailService;
    /**
     *  重写excute方法 执行定时器的业务逻辑
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("现在的时间是:" + sf.format(date));
        //具体的业务逻辑
        System.out.println("开始任务");
        sendEmailService.sendEmail();
        System.out.println("结束任务");
    }
}

3.4 建立service层管理job

IJobService.class

package com.hiqiblog.service;

import com.hiqiblog.entity.QuartzJobs;

import java.util.List;

/**
 * @Author helloc
 * @Date 2019/5/7 19:16
 * @Version 1.0
 */
public interface IJobService {
    /**
     * 获取所有
     * @param jobName
     * @return
     */

    List<QuartzJobs> listQuartzJob(String jobName);

    /**
     * 新增job
     * @param quartz
     * @return
     */
    int saveJob(QuartzJobs quartz);

    /**
     * 触发job
     * @param jobName
     * @param jobGroup
     * @return
     */
    int triggerJob(String jobName, String jobGroup);

//    /**
//     * 暂停job
//     * @param jobName
//     * @param jobGroup
//     * @return
//     */
//    int pauseJob(String jobName, String jobGroup);
//
//    /**
//     * 恢复job
//     * @param jobName
//     * @param jobGroup
//     * @return
//     */
//    int resumeJob(String jobName, String jobGroup);
//
//    /**
//     * 移除job
//     * @param jobName
//     * @param jobGroup
//     * @return
//     */
//    int removeJob(String jobName, String jobGroup);
//
//    /**
//     * 根据jobname 和jobgroup 获得job
//     * @param jobName
//     * @param jobGroup
//     * @return
//     */
//    QuartzJob getJob(String jobName, String jobGroup);
//    /**
//     * 更新
//     * @param quartz
//     * @return
//     */
//    int updateJob(QuartzJob quartz);
//
    /**
     * 重点!!
     * 将任务和出发器放入计划容器中安排出发  
     * @param job
     * @throws Exception
     */
    void schedulerJob(QuartzJobs job) throws Exception;
}

JobServiceImpl.class

package com.hiqiblog.service.impl;

import com.hiqiblog.entity.QuartzJobs;
import com.hiqiblog.enums.JobStatus;
import com.hiqiblog.job.SendEmailJob;
import com.hiqiblog.mapper.QuartzJobsMapper;
import com.hiqiblog.service.IJobService;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author helloc
 * @Date 2019/5/7 19:21
 * @Version 1.0
 */
@Service
public class JobServiceImpl implements IJobService {
    @Autowired
    private QuartzJobsMapper quartzJobsMapper;
    @Autowired
    private Scheduler scheduler;

    private static final String TRIGGER_IDENTITY = "trigger";



    @Override
    public List<QuartzJobs> listQuartzJob(String jobName) {
        List<QuartzJobs> jobList = quartzJobsMapper.listJob(jobName);
        return jobList;
    }

    @Override
    public int saveJob(QuartzJobs quartz){
        try {
            schedulerJob(quartz);
            quartz.setTriggerState(JobStatus.RUNNING.getStatus());
            quartz.setOldJobGroup(quartz.getJobGroup());
            quartz.setOldJobName(quartz.getJobName());
            quartzJobsMapper.insert(quartz);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
        return 1;
    }

    @Override
    public int triggerJob(String jobName, String jobGroup) {
        JobKey key = new JobKey(jobName,jobGroup);
        try {
            scheduler.triggerJob(key);
        } catch (SchedulerException e) {
            e.printStackTrace();
            return 0;
        }
        return 1;
    }

//    @Override
//    public int pauseJob(String jobName, String jobGroup) {
//        JobKey key = new JobKey(jobName,jobGroup);
//        try {
//            scheduler.pauseJob(key);
//            jobMapper.updateJobStatus(jobName, jobGroup, JobStatus.PAUSED.getStatus());
//        } catch (SchedulerException e) {
//            e.printStackTrace();
//            return 1;
//        }
//        return 0;
//    }
//
//    @Override
//    public int resumeJob(String jobName, String jobGroup) {
//        JobKey key = new JobKey(jobName,jobGroup);
//        try {
//            scheduler.resumeJob(key);
//            jobMapper.updateJobStatus(jobName,jobGroup, JobStatus.RUNNING.getStatus());
//        } catch (SchedulerException e) {
//            e.printStackTrace();
//            return 0;
//        }
//        return 1;
//    }
//
//    @Override
//    public int removeJob(String jobName, String jobGroup) {
//        try {
//            TriggerKey triggerKey = TriggerKey.triggerKey(TRIGGER_IDENTITY + jobName, jobGroup);
//            // 停止触发器
//            scheduler.pauseTrigger(triggerKey);
//            // 移除触发器
//            scheduler.unscheduleJob(triggerKey);
//            // 删除任务
//            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
//            jobMapper.removeQuartzJob(jobName, jobGroup);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return 0;
//        }
//        return 1;
//    }
//
//    @Override
//    public QuartzJob getJob(String jobName, String jobGroup) {
//        QuartzJob job= jobMapper.getJob(jobName, jobGroup);
//        return job;
//    }
//
//    @Override
//    public int updateJob(QuartzJobs quartz) {
//        try {
//
//            scheduler.deleteJob(new JobKey(quartz.getOldJobName(),quartz.getOldJobGroup()));
//            schedulerJob(quartz);
//
//            quartz.setOldJobGroup(quartz.getJobGroup());
//            quartz.setOldJobName(quartz.getJobName());
//            jobMapper.updateJob(quartz);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return 0;
//        }
//        return 1;
//    }

    @Override
    public void schedulerJob(QuartzJobs job) throws Exception {
//        构建job信息
//        Class cls = Class.forName(job.getJobClassName()) ;
//         检验类是否存在
//        boolean isExist= (boolean)cls.newInstance();
        //用JobDetail来装饰Job
        JobDetail jobDetail = JobBuilder.newJob(SendEmailJob.class).withIdentity(job.getJobName(),job.getJobGroup())
                .withDescription(job.getDescription()).build();

        // 触发时间点(cron表达式 "*/5 * * * * ?"==五秒一次)
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression().trim());
        //建立触发器,将触发时间点配置到触发器中
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(TRIGGER_IDENTITY + job.getJobName(), job.getJobGroup())
                .startNow().withSchedule(cronScheduleBuilder).build();
        //交由Scheduler安排触发
        scheduler.scheduleJob(jobDetail, trigger);
    }
}

JobController.class

package com.hiqiblog.controller;

import com.hiqiblog.entity.QuartzJobs;
import com.hiqiblog.service.IJobService;
import com.hiqiblog.service.impl.JobServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * @Author helloc
 * @Date 2019/5/7 19:52
 * @Version 1.0
 */
@Controller
@RequestMapping("/job")
public class JobController {
    private final static Logger LOGGER = LoggerFactory.getLogger(JobController.class);

    @Autowired
    private IJobService iJobService ;

    @RequestMapping(value = "/addJob",method = RequestMethod.GET)
    @ResponseBody
    public String addJob(){
        //理论上从页面输入job的相关数据,这里直接写的虚假数据
        QuartzJobs job=new QuartzJobs();
        job.setJobGroup("ss");
        job.setJobName("ss");
        //Cron表达式  五秒一次
        job.setCronExpression("*/5 * * * * ?");
        job.setDescription("ss");
        job.setJobClassName("com.hiqiblog.job.SendEmailJob");
        int result= iJobService.saveJob(job);
        if(result != 1){
            return "失败";
        }
        else{
            return "成功";
        }
    }

    @RequestMapping(value = "/startJob",method = RequestMethod.GET)
    @ResponseBody
    public  String trigger() {
        LOGGER.info("触发任务");
        int result = iJobService.triggerJob("ss", "ss");
        if(result != 1){
            return "失败";
        }
        else{
            return "成功";
        }
    }
}

3.5 创建服务启动类

ApplicationInit

package com.hiqiblog.init;

import com.hiqiblog.entity.QuartzJobs;
import com.hiqiblog.enums.JobStatus;
import com.hiqiblog.mapper.QuartzJobsMapper;
import com.hiqiblog.service.IJobService;
import com.hiqiblog.service.impl.JobServiceImpl;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;


/**
 * 主要用于实现在应用初始化后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次。
 * 三种实现方式
 * 1.和@Component注解一起使用,和下文一样
 * 2.和@SpringBootApplication注解一起使用。启动类后面实现CommandLineRunner,在启动类里面重写run方法
 * 3.声明一个实现了CommandLineRunner接口的Bean   在启动类中调用
 */

/**
 * @Author helloc
 * @Date 2019/5/7 15:03
 * @Version 1.0
 */
@Component
public class ApplicationInit implements CommandLineRunner {

   private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationInit.class);

    @Autowired
    private QuartzJobsMapper quartzJobsMapper;
    @Autowired
    private IJobService jobService;
    @Autowired
    private Scheduler scheduler;

    @Override
    public void run(String... args) throws Exception {
        loadJobToQuartz();
    }

    private void loadJobToQuartz() throws Exception {
        LOGGER.info("quartz job load...");
        //从数据库中找到所有的job
        List<QuartzJobs> jobs = quartzJobsMapper.listJob("ss");
        //循环启动job
        for(QuartzJobs job : jobs) {
            jobService.schedulerJob(job);
            //如果数据库中状态为暂停 就暂停job
            if (JobStatus.PAUSED.equals(job.getTriggerState())) {
                scheduler.pauseJob(new JobKey(job.getJobName(), job.getJobGroup()));
            }
        }
    }
}


3.6 上述代码如果运行会报空指针错误,原因定时任务Job对象的实例化过程是在Quartz中进行的,无法感知到Spring生成的bean,所以Job中调用Spring的服务会显示空指针错误(NullPointException),那么我们将bean交予Spring管理,问题解决如下:

建立SpringJobFactory继承 AdaptableJobFactory

package com.hiqiblog.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
 * @Author helloc
 * @Date 2019/5/8 15:30
 * @Version 1.0
 */

    @Component
    public class SpringJobFactory  extends AdaptableJobFactory
    {
        /**
         * SchedulerFactoryBean 使用 AdaptableJobFactory 对Job对象进行实例
         * 将Qurartz创建好Job的bean放入这个BeanFactory进行管理,方便job任务中自动装配其他的Spring的bean.
         * 例如:SendEmailJob中的sendEmailService的autowired
         */
        @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception
        {
            // 首先,调用父类的方法创建好Quartz所需的Job实例
            Object jobInstance = super.createJobInstance(bundle);
            // 然后,使用BeanFactory为创建好的Job实例进行属性自动装配并将其纳入到Spring容器的管理之中
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }




QuartzConfig将scheduler的setJobFactory更改成Spring管理的JobFactory,这样就可以3.3中就可以正常调用其他服务了

package com.hiqiblog.config;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;

/**
 * @Author helloc
 * @Date 2019/5/8 15:36
 * @Version 1.0
 */
@Configuration
public class QuartzConfig {
    @Autowired
    private SpringJobFactory  springJobFactory;

  @Bean
  public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //自动启动
        schedulerFactoryBean.setAutoStartup(true);
        //延时5秒启动
        schedulerFactoryBean.setStartupDelay(5);
        schedulerFactoryBean.setJobFactory(springJobFactory);
        return schedulerFactoryBean;
    }

    /**
     * 被spring管理的bean
     * @return
     * @throws IOException
     */
    @Bean
    public Scheduler scheduler()  throws IOException{
        return schedulerFactoryBean().getScheduler();
    }

}

大功告成!!!!!!!

Enum中的字符串比较JobStatus.PAUSED.equals(JobStatus.valueOf(job.getTriggerState()))

你可能感兴趣的:(springboot项目,SpringBoot,Quartz,发送邮件)