【SpringBoot学习笔记 十二】SpringBoot异步任务、定时任务、邮件任务

简介: 【SpringBoot学习笔记 十二】SpringBoot异步任务、定时任务、邮件任务

之前两篇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自动配置原理,了解之后看到相关代码能立刻看懂也还是大有好处。

相关文章
|
9月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
861 2
|
10月前
|
SQL JSON Java
|
9月前
|
人工智能 安全 Java
Spring Boot 中使用 Function 和异步线程池处理列表拆分任务并汇总结果
在Java开发中,处理大规模数据时常常需要将列表拆分为多个子列表进行异步处理并汇总结果。本文介绍如何在Spring Boot中使用Function和异步线程池实现高效且可维护的代码,涵盖结果封装、线程池配置、列表拆分处理及结果汇总等关键步骤。
373 0
|
10月前
|
Java Spring
如何优雅的实现 SpringBoot 并行任务
我是小假 期待与你的下一次相遇 ~
204 1
|
分布式计算 大数据 Java
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
216 0
|
11月前
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
962 4
|
Java 调度 数据库
SpringBoot整合XXL-JOB【05】- 任务分片
在实际业务中,批量定时任务可能因上一批任务未完成而影响业务。为解决此问题,本文介绍如何使用Xxl-job对批量任务进行分片处理,通过分片广播形式调度集群机器并行执行任务,大幅提升执行效率。具体步骤包括环境准备、添加依赖和配置、声明实体类与查询类,以及改造业务逻辑实现分片查询。测试结果显示,分片处理将两千条数据的执行时间从30秒缩短至15秒,性能提升显著。
2045 13
SpringBoot整合XXL-JOB【05】-  任务分片
|
存储 安全 Java
SpringBoot异步任务获取HttpServletRequest
通过上述方法,我们可以在Spring Boot应用中的异步任务获取 `HttpServletRequest`,从而实现更为灵活和高效的异步处理逻辑。
904 64
|
前端开发 Java API
SpringBoot整合Flowable【07】- 驳回节点任务
本文通过绩效流程的业务场景,详细介绍了如何在Flowable工作流引擎中实现任务驳回功能。具体步骤包括:获取目标任务节点和当前任务节点信息,进行必要的判空和逻辑校验,调用API完成节点回退,并清理相关脏数据(如历史任务和变量)。最后通过测试验证了驳回功能的正确性,确保流程能够成功回退到指定节点并清除中间产生的冗余数据。此功能在实际业务中非常有用,能够满足上级驳回自评等需求。
2520 0
SpringBoot整合Flowable【07】- 驳回节点任务
|
消息中间件 缓存 监控
【Java笔记+踩坑】SpringBoot基础3——开发。热部署+配置高级+整合NoSQL/缓存/任务/邮件/监控
springboot的热部署、配置的宽松绑定和校验、任务、邮件、监控、springboot整合JdbcTemplate,h2等sql技术、整合redis,mongodb,es等nosql技术、整合redis,Memcached,jetcache,j2cache等缓存技术、整合ActiveMQ,RabbitMQ,RocketMQ,Kafka等消息的中间件的入门、整合缓存/任务/邮件/监控
【Java笔记+踩坑】SpringBoot基础3——开发。热部署+配置高级+整合NoSQL/缓存/任务/邮件/监控