之前两篇Blog都是SpringBoot使用层面上的实践,拦截器和异常处理。今天这篇Blog也一样,从使用实践层面上看看SpringBoot给我们封装了什么,能让我们便利的进行异步任务、定时任务、邮件任务的处理。在没有SpringBoot之前,这些功能可能的实现可能需要写很多代码才能实现,同时这篇Blog是SpringBoot实践层面的最后一篇,接下来我们进入集成层面的学习,学习SpringBoot集成Swagger、Dubbo以及SpringSecurity,做个小预告,集成内容学习完就开始SpringCloud旅程。
SpringBoot异步任务异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成pending,直到邮件发送完毕,响应才会成功,这样用户的体验非常差。所以我们一般会采用多线程的方式去后台处理这些任务,不让用户等待太多时间。
同步的情况下首先我们编写一个10秒左右的任务,编写方法,假装正在处理数据,使用线程设置一些延时,模拟同步等待的情况:
AsyncService
package com.example.springboot.service;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
public void asyncTask(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("业务进行中....");
}
}
然后我们编写一个用户请求的方法。
AsyncController
package com.example.springboot.controller;
import com.example.springboot.service.AsyncService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class AsyncController {
@Resource
AsyncService asyncService;
@GetMapping("/asyncTask")
public String asyncTask(){
asyncService.asyncTask();
return "success";
}
}
访问http://localhost:8080/asyncTask进行测试,10秒后出现success,这是同步等待的情况.
10秒后业务执行完了才返回响应
我们如果想让用户直接得到响应,就在后台使用多线程的方式异步调用AsyncService.asyncTask方法即可,但是每次都需要自己手动去编写多线程的实现的话,太麻烦了。
1 编写AsyncService异步任务类SpringBoot都帮我们做了,我们只需要在我们的方法上加一个简单的注解即可,给asyncTask方法添加@Async注解
AsyncService
package com.example.springboot.service;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
public void asyncTask(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("业务进行中....");
}
}
2 SpringbootApplication 开启异步注解功能
SpringBoot就会自己开一个线程池进行任务异步调用,但是要让这个注解生效,我们还需要在主程序上添加一个注解@EnableAsync ,开启异步注解功能
SpringbootApplication
package com.example.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync //开启异步注解功能
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
再次访问http://localhost:8080/asyncTask进行测试,76ms秒后出现success
后台的任务还是10秒执行完。
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式。
1 编写ScheduledService定时任务类ScheduledService
package com.example.springboot.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class ScheduledService {
//秒 分 时 日 月 周几
//0 * * * * MON-FRI
//注意cron表达式的用法;
@Scheduled(cron = "0/5 * * * * ?")
public void scheduled(){
System.out.println("定时任务执行了,现在是:"+ LocalDateTime.now());
}
}
这里用了cron表达式进行定时任务定制,关于cron表达式,在这篇Blog里我有详细介绍【解决方案 二十】作业调度系统cron表达式详解。
2 SpringbootApplication 开启定时任务功能设置好ScheduledService 后我们需要在主程序上增加@EnableScheduling 开启定时任务功能
SpringbootApplication
package com.example.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync //开启异步注解功能
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
启动SpringBoot后我们可以看到异步定时任务每隔5秒执行一次
邮件发送,在我们的日常开发中,也非常的多,Springboot也帮我们做了支持
1 引入pom依赖邮件发送需要引入spring-boot-start-mail
org.springframework.boot spring-boot-starter-mail
看它引入的依赖,可以看到 jakarta.mail
org.springframework.boot spring-boot-starter 2.5.4 compile org.springframework spring-context-support 5.3.9 compile com.sun.mail jakarta.mail 1.6.7 compile
照例我们从启动器下通过发现机制查看自动配置类MailSenderAutoConfiguration:
MailSenderAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({MimeMessage.class, MimeType.class, MailSender.class})
@ConditionalOnMissingBean({MailSender.class})
@Conditional({MailSenderAutoConfiguration.MailSenderCondition.class})
@EnableConfigurationProperties({MailProperties.class})
@import({MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class})
public class MailSenderAutoConfiguration {
public MailSenderAutoConfiguration() {
}
static class MailSenderCondition extends AnyNestedCondition {
MailSenderCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(
prefix = "spring.mail",
name = {"jndi-name"}
)
static class JndiNameProperty {
JndiNameProperty() {
}
}
@ConditionalOnProperty(
prefix = "spring.mail",
name = {"host"}
)
static class HostProperty {
HostProperty() {
}
}
}
}
从导入的MailSenderJndiConfiguration.class向下看:
**MailSenderJndiConfiguration **
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Session.class})
@ConditionalOnProperty(
prefix = "spring.mail",
name = {"jndi-name"}
)
@ConditionalOnJndi
class MailSenderJndiConfiguration {
private final MailProperties properties;
MailSenderJndiConfiguration(MailProperties properties) {
this.properties = properties;
}
@Bean
JavaMailSenderImpl mailSender(Session session) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setDefaultEncoding(this.properties.getDefaultEncoding().name());
sender.setSession(session);
return sender;
}
@Bean
@ConditionalOnMissingBean
Session session() {
String jndiName = this.properties.getJndiName();
try {
return (Session)JndiLocatorDelegate.createDefaultResourceRefLocator().lookup(jndiName, Session.class);
} catch (NamingException var3) {
throw new IllegalStateException(String.format("Unable to find Session in JNDI location %s", jndiName), var3);
}
}
}
发现使用了JavaMailSenderImpl进行邮件发送,同时读取的配置内容来源是:MailProperties
2 定义MailProperties内容配置我们看下MailProperties 中的配置内容:
所以我们在application.yml中添加如下配置即可:
spring:
#邮件配置
mail:
#邮箱地址
username: xxxxxtml@qq.com
#授权码
password: ofcezjolcpx
#邮箱服务器
host: smtp.qq.com
# qq需要配置ssl
properties:
mail:
smtp:
ssl:
enabl: true
3 设置邮箱服务
在QQ邮箱中的设置->账户->开启pop3和smtp服务来获取授权码
在Spring单元测试中填写如下地址
@Resource
JavaMailSenderImpl mailSender;
@Test
public void sendMailTestEasy() {
//邮件设置1:一个简单的邮件
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("通知-这是来自SpringBoot单元测试的来信,tml请接收");
message.setText("你有一封SpringBoot单元测试的来信请接收");
message.setTo("xxxxxtml@qq.com"); //已做打码处理
message.setFrom("xxxxxtml@qq.com"); //已做打码处理
//如果需要发送附件
mailSender.send(message);
}
然后我们就收到了这封邮件:
今天这篇Blog学习了SpringBoot的几个应用场景,异步任务,定时任务以及邮件任务,其实都不复杂,都是使用层面上的小实践,但是也可以看的出SpringBoot的功能之强大,扩展之方便,其实实际使用时我们不一定使用SpringBoot自带的,可能会集成一些三方的系统,但是在了解过程中又过了一遍SpringBoot自动配置原理,了解之后看到相关代码能立刻看懂也还是大有好处。



