之前两篇Blog都是SpringBoot使用层面上的实践,拦截器和异常处理。今天这篇Blog也一样,从使用实践层面上看看SpringBoot给我们封装了什么,能让我们便利的进行异步任务、定时任务、邮件任务的处理。在没有SpringBoot之前,这些功能可能的实现可能需要写很多代码才能实现,同时这篇Blog是SpringBoot实践层面的最后一篇,接下来我们进入集成层面的学习,学习SpringBoot集成Swagger、Dubbo做个小预告,集成内容学习完就开始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秒执行完。
SpringBoot定时任务
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,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邮件任务
邮件发送,在我们的日常开发中,也非常的多,Springboot也帮我们做了支持
1 引入pom依赖
邮件发送需要引入spring-boot-start-mail
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
看它引入的依赖,可以看到 jakarta.mail
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.5.4</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.3.9</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>jakarta.mail</artifactId> <version>1.6.7</version> <scope>compile</scope> </dependency> </dependencies>
照例我们从启动器下通过发现机制查看自动配置类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服务来获取授权码
4 SpringTest单元测试调用
在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自动配置原理,了解之后看到相关代码能立刻看懂也还是大有好处。