Quartz定时器和发送Email

Posted 阿_毅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Quartz定时器和发送Email相关的知识,希望对你有一定的参考价值。

开心一笑

【从年初的一无所有,到年终的身无分文,唉,我这不忘初心。。。】

新书购买

戳图购买 >>>

9.1 使用Quartz定时器

9.1.1 Quartz概述

Quartz是一个完全由Java编写的开源任务调度的框架,通过触发器设置作业定时运行规则,控制作业的运行时间。Quartz定时器作用很多,比如:定时发送信息、定时生成报表等等。
Quartz框架主要核心组件包括调度器、触发器、作业。调度器作为作业的总指挥,触发器作为作业的操作者,作业为应用的功能模块。其关系如图9-1所示:


图9-1 redis断点调试界面
Job是一个接口,该接口只有一个方法execute,被调度的作业(类)需实现该接口中execute()方法,JobExecutionContext类提供了调度上下文的各种信息。每次执行该Job均重新创建一个Job实例。Job的源码如下所示:

public interface Job 
    void execute(JobExecutionContext var1) throws JobExecutionException;

Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。JobDetail 用来保存我们作业的详细信息。一个JobDetail可以有多个Trigger,但是一个Trigger只能对应一个JobDetail
Trigger触发器描述触发Job执行规则。主要有SimpleTrigger和 CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等。Cron表达式定义如下:

CronTrigger配置格式:
格式: [秒] [分] [小时] [日] [月] [周] [年]
0 0 12 * * ?           每天12点触发 
0 15 10 ? * *          每天10点15分触发 
0 15 10 * * ?          每天10点15分触发  
0 15 10 * * ? *        每天10点15分触发  
0 15 10 * * ? 2005     2005年每天10点15分触发 
0 * 14 * * ?           每天下午的 2点到2点59分每分触发 
0 0/5 14 * * ?         每天下午的 2点到2点59分(整点开始,每隔5分触发)  
0 0/5 14,18 * * ?        每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ?            每天下午的 2点到2点05分每分触发 
0 10,44 14 ? 3 WED        3月分每周三下午的 2点10分和2点44分触发 
0 15 10 ? * MON-FRI       从周一到周五每天上午的10点15分触发 
0 15 10 15 * ?            每月15号上午10点15分触发 
0 15 10 L * ?             每月最后一天的10点15分触发 
0 15 10 ? * 6L            每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6L 2002-2005  从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3           每月的第三周的星期五开始触发 
0 0 12 1/5 * ?            每月的第一个中午开始每隔5天触发一次 
0 11 11 11 11 ?           每年的11月11号 11点11分触发(光棍节)

Scheduler负责管理Quartz的运行环境,Quartz它是基于多线程架构的,它启动的时候会初始化一套线程,这套线程会用来执行一些预置的作业。Trigger和JobDetail可以注册到Scheduler中。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

9.1.2 引入依赖

在Spring Boot中集成Quartz,首先需要在pom.xml文件中引入所需的依赖,具体代码如下:

<!-- quartz定时器 -->
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.2.3</version>
</dependency>

9.1.3 定时器配置文件

创建定时器的方法有两种,1)使用XML配置文件的方式。2)使用注解的方式。注解的方式不需要任何配置文件,简单高效,这两种方式都会讲到。我们先来讲第一种方式,也就是配置文件的方式。首先,我们需要在/src/main/resources目录下新建配置文件spring-mvc.xml,具体代码如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/task 
       http://www.springframework.org/schema/task/spring-task.xsd">

    <context:annotation-config/>
    <!-- 利用import引入定时器的文件 -->
    <import resource="spring-quartz.xml"/>

</beans>

< import>标签:利用import标签导入定时器的配置文件,该标签可以根据具体业务分离配置文件。然后,我们在/src/main/resources目录下新建spring-quartz.xml配置文件,具体代码如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
<!--  定义Job对象 -->
<bean id="taskJob" class="com.example.demo.quartz.TestTask"/>
    <!--  定义JobDetail对象 -->
    <bean id="jobDetail"
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--  目标对象taskJob -->
<property name="targetObject">
            <ref bean="taskJob"/>
        </property>
<!--  目标方法 -->
        <property name="targetMethod">
            <value>run</value>
        </property>
    </bean>

    <!--  调度触发器 -->
    <bean id="myTrigger"
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!--  指定使用jobDetail -->
<property name="jobDetail">
            <ref bean="jobDetail" />
        </property>
        <!--  定义触发规则,每10秒执行一次 -->
        <property name="cronExpression">
            <value>0/10 * * * * ?</value>
        </property>
    </bean>

    <!-- 调度工厂 -->
    <bean id="scheduler"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 注册触发器,可注册多个 -->
<property name="triggers">
            <list>
                <ref bean="myTrigger"/>
            </list>
        </property>
    </bean>
</beans>

在spring-quartz.xml配置文件中,我们分别定义了Job、JobDetail、Trigger、以及Scheduler。并配置了它们之间的关系。

9.1.4 创建定时器类

定时器的依赖以及配置文件开发完成之后,我们在/src/main/java/com.example.demo.quartz目录下新建定时器类TestTask.java。具体代码如下:

/**
 * 描述:定时器类
 * @author Ay
 * @date   2017/11/18
 */
public class TestTask 

    //日志对象
    private static final Logger logger = LogManager.getLogger(TestTask.class);

    public void run() 
        logger.info("定时器运行了!!!");
    

如果我们使用第二种创建定时器的方法,相对就简单了,只需要创建一个定时器类,加上相关的注解就搞定了。比如,我们可以在/src/main/java/com.example.demo.quartz目录下创建SendMailQuartz定时器类,具体代码如下:

/**
 * 描述:定时器类
 * @author Ay
 * @date   2017/11/18
 */
@Component
@Configurable
@EnableScheduling
public class SendMailQuartz 

    //日志对象
    private static final Logger logger = LogManager.getLogger(SendMailQuartz.class);

    //每5秒执行一次
    @Scheduled(cron = "*/5 * *  * * * ")
    public void reportCurrentByCron()
        logger.info("定时器运行了!!!");
    


@Configurable:加上此注解的类相当于XML配置文件,可以被Spring Boot扫描初始化。
@EnableScheduling:通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。
@Scheduled:注解为定时任务,cron表达式里写执行的时机。

9.1.5 Spring Boot扫描配置文件

9.1.3节中,我们已经开发好spring-mvc.xml配置文件,但是想让Spring Boot扫描到该配置文件,还需要在入口类MySpringBootApplication中添加@ImportResource注解,具体代码如下:

@SpringBootApplication
@ServletComponentScan
@ImportResource(locations="classpath:spring-mvc.xml")
public class MySpringBootApplication 
    //省略代码

@ImportResource:导入资源配置文件,让Spring Boot可以读取到,类似于XML配置文件中的标签。

9.1.6 测试

代码开发完成之后,我们重新启动项目,在Intellij IDEA控制台中可以看到如图9-2所示的信息,证明在Spring Boot中整合Quartz定时器成功。


图9-2 Quartz定时器打印信息

9.2 Spring Boot发送Email

9.2.1 Email介绍

邮件服务在互联网早期就已经出现,如今已成为人们互联网生活中必不可少的一项服务。邮件发送与接收的过程如下:
1)发件人使用SMTP协议传输邮件到邮件服务器A;
2)邮件服务器A根据邮件中指定的接收者,投送邮件至相应的邮件服务器B;
3)收件人使用POP3协议从邮件服务器B接收邮件。
SMTP(Simple Mail Transfer Protocol)是电子邮件(Email)传输的互联网标准,定义在RFC5321,默认使用端口25;
POP3(Post Office Protocol - Version 3)主要用于支持使用客户端远程管理在服务器上的电子邮件。定义在RFC 1939,为POP协议的第三版(最新版)。
这两个协议均属于TCP/IP协议族的应用层协议,运行在TCP层之上。
发送邮件的需求比较常见,如找回密码、事件通知、向用户发送广告邮件等。SUN公司提供给广大Java开发人员的一款邮件发送和接收的开源类库JavaMail,支持常用的邮件协议,如:SMTP、POP3、IMAP等。开发人员使用JavaMail编写邮件程序时,不再需要考虑底层的通讯细节如:Socket,而是关注逻辑层面。JavaMail可以发送各种复杂MIME格式的邮件内容,注意JavaMail仅支持JDK4及以上版本。虽然JavaMail是JDK的API但它并没有直接加入JDK中,所以我们需要另外添加依赖。
Spring提供了非常好用的JavaMailSender接口实现邮件发送。在Spring Boot的Starter模块中已为此提供了自动化配置。

9.2.2 引入依赖

在Spring Boot中集成Mail,首先需要在pom.xml文件中引入所需的依赖,具体代码如下:

<!-- mail start -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

9.2.3 添加Email配置

在pom文件引入Mail所需的依赖之后,我们需要在application.properties文件中添加如下的配置信息:

###mail邮件配置
###邮箱主机
spring.mail.host=smtp.163.com
###用户名
spring.mail.username=ay_test@163.com
###设置的授权码
spring.mail.password=ay12345
###默认编码
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

9.2.4 在定时器中发送邮件

在Spring Boot中添加完依赖和配置之后,我们在项目的目录/src/main/java/com.example.demo.mail下新建邮件服务接口类SendJunkMailService,具体代码如下:

/**
 * 描述:发送用户邮件服务
 * @author Ay
 * @date   2017/11/19
 */
public interface SendJunkMailService 

    boolean sendJunkMail(List<AyUser> ayUser);

然后我们继续在项目的目录/src/main/java/com.example.demo.mail.impl下新建接口类的实现类SendJunkMailServiceImpl.java,具体代码如下:

/**
 * 描述:发送用户邮件服务
 * @author Ay
 * @date   2017/11/19
 */
@Service
public class SendJunkMailServiceImpl implements SendJunkMailService 

    @Autowired
    JavaMailSender mailSender;
    @Resource
    private AyUserService ayUserService;
    @Value("$spring.mail.username")
    private String from;
          public static final Logger logger = 
LogManager.getLogger(SendJunkMailServiceImpl.class);

    @Override
    public boolean sendJunkMail(List<AyUser> ayUserList) 

        try
            if(ayUserList == null || ayUserList.size() <= 0 ) return Boolean.FALSE;
            for(AyUser ayUser: ayUserList)
                MimeMessage mimeMessage = this.mailSender.createMimeMessage();
                MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
                //邮件发送方
message.setFrom(from);
//邮件主题
                message.setSubject("地瓜今日特卖");
                //邮件接收方
                message.setTo("al_test@163.com");
                //邮件内容
                message.setText(ayUser.getName() +" ,你知道么?厦门地瓜今日特卖,一斤只要9元");
                //发送邮件
this.mailSender.send(mimeMessage);
            
        catch(Exception ex)
            logger.error("sendJunkMail error and ayUser=%s", ayUserList, ex);
            return Boolean.FALSE;
        
        return Boolean.TRUE;
    

@Value:可以将application.properties配置文件中的配置设置到属性中。如上面代码中,会将spring.mail.username的值huangwenyi10@163.com设置给from属性。
JavaMailSender:邮件发送接口。在Spring Boot的Starter模块中已为此提供了自动化配置。我们只需要通过注解@Autowired注入进来,即可使用。
在9.1.4节中,我们已经开发了SendMailQuartz定时器类,现在我们重新修改该类,让定时器类能够每隔一段时间,给数据库的用户发送广告邮件,SendMailQuartz类具体的修改如下:

/**
 * 描述:定时器类
 * @author Ay
 * @date   2017/11/18
 */
@Component
@Configurable
@EnableScheduling
public class SendMailQuartz 

    //日志对象
    private static final Logger logger = LogManager.getLogger(SendMailQuartz.class);

    @Resource
    private SendJunkMailService sendJunkMailService;
    @Resource
    private AyUserService ayUserService;

    //每5秒执行一次
    @Scheduled(cron = "*/5 * *  * * * ")
    public void reportCurrentByCron()
        List<AyUser> userList = ayUserService.findAll();
        if (userList == null || userList.size() <= 0) return;
        //发送邮件
        sendJunkMailService.sendJunkMail(userList);
        logger.info("定时器运行了!!!");
    


9.2.5 测试

代码全部开发完成之后,我们重新启动项目,发送邮件定时器类SendMailQuartz每隔5秒(真实项目会设置比较长,比如1天,2天等)会查询数据库表ay_test中的所有用户,并发送广告邮件给对应的用户。我们登陆al_test@163.com邮箱,便可以查看到如图9-1、9-2所示的信息。


图9-1 163邮箱界面 图9-2 163邮件内容


读书感悟

来自《梳毛、八卦及语言的进化》

  • 150人的邓巴数字是由人类的大脑决定的,维持 150人的社交 关系,既不会让我们感觉太费劲,又可以最大限度地发挥人多力量大的优势。
  • 我们天生喜爱社交,也爱聊八卦,而八卦又反过来推动社会的发展,这样建立 起来的社会是很有人情味的。
  • 邓巴数字可以帮助我们估计群体规模,判断何时应该做出调整。而聊八卦更是 我们日常生活的重要组成部分,它既能帮我们维持人际关系,也能创造价值,让 我们作为社会的一份子不觉得寂寞。

经典故事

一天,我发现,一只黑蜘蛛在后院的两檐之间结了一张很大的网。难道蜘蛛会飞?要不,从这个檐头到那个檐头,中间有一丈余宽,第一根线是怎么拉过去的?后来,我发现蜘蛛走了许多弯路。从一个檐头起,打结,顺墙而下,一步一步向前爬,小心翼翼,翘起尾部,不让丝沾到地面的沙石或别的物体上,走过空地,再爬上对面的檐头,高度差不多了,再把丝收紧,以后也是如此。
【蜘蛛不会飞翔,但它能够把网凌结在半空中。它是勤奋、敏感、沉默而坚韧的昆虫,它的网制得精巧而规矩,八卦形地张开,仿佛得到神助。这样的成绩,使人不由想起那些沉默寡言的人和一些深藏不露的智者。于是,我记住了蜘蛛不会飞翔,但它照样把网结在空中。奇迹是执着者造成的。】


大神文章


其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎鼓励,点赞、顶、欢迎留下宝贵的意见、多谢支持!

以上是关于Quartz定时器和发送Email的主要内容,如果未能解决你的问题,请参考以下文章

利用quartz实现邮件的定时发送

quartz-job实现实时或定时发送短信任务

Quartz定时向阿里云MQ发送数据(TCP模式)

Spring + quartz实现定时发送邮件功能

Quartz框架简介

定时发送邮件(利用quart)