Quartz定时器和发送Email
本章主要介绍在Spring Boot中使用XML配置和Java注解两种方式定义和使用Quartz定时器,以及如何在Spring Boot中通过JavaMailSender接口给用户发送广告邮件等内容。
1.使用Quartz定时器
1.1 Quartz概述
Quartz是一个完全由Java编写的开源任务调度的框架,通过触发器设置作业定时运行规则,控制作业的运行时间。Quartz定时器作用很多,比如定时发送信息、定时生成报表等。
Quartz框架主要核心组件包括调度器、触发器、作业,调度器作为作业的总指挥,触发器作为作业的操作者,作业为应用的功能模块,其关系如图所示。
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执行等。
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 * * ? | 每天下午的 2点到2点59分(整点开始,每隔5分触发)每天下午的 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使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
1.2 引入依赖
在Spring Boot中集成Quartz,首先需要在pom.xml文件中引入所需的依赖,具体代码如下:
<!-- quartz定时器-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
1.3 定时器配置文件
创建定时器的方法有两种:①使用XML配置文件的方式;②使用注解的方式。注解的方式不需要任何配置文件且简单高效,这两种方式都会讲到。我们先来讲第一种方式,也就是配置文件的方式。首先,我们需要在/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/sch/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。并配置了它们之间的关系。
1.4 创建定时器类
定时器的依赖及配置文件开发完成之后,在/src/main/java/com.example.demo.quartz目录下新建定时器类TestTask.java。具体代码如下:
public class TestTask{
//日志对象
private static final Logger logger = LogManager.getLogger(TestTest.class);
public void run(){
logger.info("定时器运行了!!!");
}
}
如果使用第二种创建定时器的方法,相对就简单了,只需要创建一个定时器类,加上相关的注解就搞定了。比如,我们可以在/src/main/java/com.example.demo.quartz目录下创建SendMailQuartz定时器类,具体代码如下:
@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配置文件可以被SpringBoot扫描初始化。
- @EnableScheduling:通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。
- @Scheduled:注解为定时任务,cron表达式里写执行的时机。
1.5 Spring Boot扫描配置文件
已经开发好spring-mvc.xml配置文件,但是想让Spring Boot扫描到该配置文件,还需要在入口类MySpringBootApplication中添加@ImportResource注解,具体代码如下:
@SpringBootApplication
@ServletComponentScan
@ImportResource(locations={"classpath:spring-mvc.xml"})
public class MySpringBootApplication{
//忽略代码
}
- @ImportResource:导入资源配置文件,让Spring Boot可以读取到,类似于XML配置文件中的标签。
1.6 测试
代码开发完成之后,重新启动项目,在Intellij IDEA控制台中可以看到如图所示的信息,证明在Spring Boot中整合Quartz定时器成功。
2.Spring Boot发送Email
2.1 Email介绍
邮件服务在互联网早期就已经出现,如今已成为人们互联网生活中必不可少的一项服务。邮件发送与接收的过程如下:
- 发件人使用SMTP协议传输邮件到邮件服务器A。
- 邮件服务器A根据邮件中指定的接收者,投送邮件至相应的邮件服务器B。
- 收件人使用POP3协议从邮件服务器B接收邮件。
SMTP(Simple Mail Transfer Protocol)是电子邮件(Email)传输的互联网标准,定义在RFC5321,默认使用端口25。
POP3(Post Office Protocol-Version3)主要用于支持使用客户端远程管理在服务器上的电子邮件。定义在RFC1939,为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模块中已为此提供了自动化配置。
2.2 引入依赖
<!--mail start-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.3 添加Email配置
在pom文件引入Mail所需的依赖之后,需要在application.properties文件中添加如下的配置信息:
### Mail邮件配置
### 邮箱主机
spring.mail.host=smtp.163.com
### 用户名
spring.mail.username=huangwenyi10@163.com
### 设置的授权码
spring.mail.password=自己邮箱密码
### 默认编码
spring.mail.default-encoding=UTE-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring mail.properties.mail.smtp.starttls.required=true
2.4 在定时器中发送邮件
在Spring Boot中添加完依赖和配置之后,在项目的/src/main/java/com.example.demo.mail目录下新建邮件服务接口类SendJunkMailService,具体代码如下:
public interface SendJunkMailService{
boolean sendJunkMail(List<AyUser> ayUser);
}
然后,继续在项目的目录/src/main/java/com.example.demo.mail.impl下新建接口类的实现类SendJunkMailServiceImpl.java,具体代码如下:
@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(mimiMessage);
//邮件发送方
message.setFrom(from);
//邮件主题
message.setSubject("地瓜今日特卖");
//用邮件接收方
message.setTo("al_test@163.com");
//邮件内容
message.setText(ayUser.getName() + "test message");
//发送邮件
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注入进来,即可使用。
上一节中已经开发了SendMailQuartz定时器类,现在重新修改该类,让定时器类能够每隔一段时间给数据库的用户发送广告邮件,SendMailQuartz类具体的修改如下
@Component
@Configurable
@EnableScheduling
public class SendMailQuartz {
//日志对象
private static final Logger logger = LogManager.getLogger(SendMailQuartz.class);
//每5秒执行一次
@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("定时器运行了!!!");
}
}
2.5 测试
代码全部开发完成之后,重新启动项目,发送邮件定时器类SendMailQuartz每隔5秒(真实项目会设置比较长,比如1天、2天等)会查询数据库表ay_test中的所有用户,并发送广告邮件给对应的用户。我们登录al_test@163.com邮箱,便可以查看到邮件信息。