springboot异步任务、邮件发送 及 @EnableAsync@Async使用总结

本文介绍了如何在SpringBoot中实现异步任务处理,通过@EnableAsync和@Async注解开启和标注异步方法,避免长时间阻塞主线程。同时,详细讲解了邮件发送的配置和测试,包括简单邮件和复杂邮件(含HTML内容及附件)的发送。通过添加spring-boot-starter-mail依赖,并在application.properties中配置邮件服务器信息,可以实现邮件的发送功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、异步任务

多线程实现异步任务。

前台一个请求发送到后端,如果是异步任务的话,且耗时比较长(例如发送邮件),前端页面不可能一直在那等着,等邮件发送出去,后端才返回结果。

解决这种一部问题,springboot已经做好了,只需要添加两个注解:@EnableAsync和Async即可。

在启动入口类中,添加@EnableAsync注解,开启异步注解功能
 

@EnableAsync // 开启异步注解功能
@SpringBootApplication // 启动入口类,spring-boot 核心注解 用于开启spring自动配置
@MapperScan("com.lxc.sprint_boot_01.mapper")
public class SprintBoot01Application {
    public static void main(String[] args) {
          SpringApplication.run(SprintBoot01Application.class, args);
    }
}

 在执行异步的方法上添加 @Async 注解,告知spring这是一个异步方法:

@Service
public class UserService {

    @Resource
    private UserMapper userMapper;
    @Async //告诉spring,这是一个异步的方法
    public void getTest(){
        try {
            Thread.sleep(3000); // 程序走到这会等待3秒,才能继续向下执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("数据处理完毕!");
    }
}

测试

​@RestController
public class UserController {

    @Resource
    private UserService userService;
    @GetMapping(value = "/test")
    public String userTest(Model model) {
        userService.getTest();
        return "OK";
    }
}

前台请求 /test 接口,瞬间返回结果,而调用的异步方法,3秒之后,输出了结果

补充(2022-02-22):
@EnableAsync 和 @Async 使用总结

我们在使用多线程的时候,往往需要创建Thread类,或者实现Runnable接口,如果要使用到线程池,我们还需要来创建Executors!在使用spring中,已经给我们做了很好的支持。
只要标注@EnableAsync就可以开启多线程。
使用@Async就可以定义一个线程任务。
通过spring给我们提供的ThreadPoolTaskExecutor就可以创建线程池。关于线程池 ThreadPoolTaskExecutor 参考如下文章:

java 线程池概念、优缺点、应用场景_记录点滴-优快云博客_线程池的优缺点

默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的TaskExecutor bean,要么搜索名为“taskExecutor”的Executor bean。如果两者都无法解析,则将使用SimpleAsyncTaskExecutor来处理异步方法调用。

@Configuration
@EnableAsync // 开启多线程
public class ThreadPoolConfig
{
    // 核心线程池大小
    private int corePoolSize = 50;
    // 最大可创建的线程数
    private int maxPoolSize = 200;
    // 队列最大长度
    private int queueCapacity = 1000;
    // 线程池维护线程所允许的空闲时间
    private int keepAliveSeconds = 300;

    @Bean(name = "myThreadPoolTaskExecutor") // 设置bean 名称
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

@Configuration用于定义配置类,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

如何使用?
通常会在service中使用@Async,因为业务逻辑会写在service中,这里为测试方便写在controller中:

@Slf4j
@RestController
@RequestMapping(value = "/test")
public class Ttest {
    @Autowired
    ISysDeptService iSysDeptServicel;
    
    @Async("threadPoolTaskExecutor") // 获取bean
    @GetMapping(value = "get")
    public String get(String params) {
        System.out.println("1:"+Thread.currentThread().getName());
        return "请求成功";
    }
}

@Async注解来声明一个或多个异步任务,可以加在方法或者类上,加在类上表示这整个类都是使用这个自定义线程池进行操作。

@Async注解失效的情况
一、异步方法使用static修饰
二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
三、异步方法不能与异步方法在同一个类中
四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
六、在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
七、调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用!!!!!!!
八、使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的

二、邮件发送

maven安装依赖。

<!--发送邮件依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

application.properties 配置发邮件信息

# 用户名
spring.mail.username=1063668@qq.com
# 授权码(如何获取授权码?)
spring.mail.password=jmeuyytzzrrby
# 发送邮件的服务器,下边使用qq(smtp.sina.com、smtp.163.com)
spring.mail.host=smtp.qq.com
# 开启加密授权验证(只有qq有这个,网易163没有)
spring.mail.properties.mail.smtp.ssl.enable=true

获取授权码:

邮箱 -> 设置 ->  账户 -> 点击开启pop3smtp服务 -> 会生成一个授权码

测试

简单邮件发送

@SpringBootTest
class SprintBoot01ApplicationTests {
    @Resource
    private JavaMailSenderImpl mailSender;
    @Test
    void contextLoads() {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("来自遥远星球的来信"); // 标题
        message.setText("你好啊,这是来自2021年7月4日,上午我在学习java邮件发送。"); // 内容
        message.setTo("1063618@qq.com"); // 发送给谁
        message.setFrom("1063618@qq.com");// 谁发送的
        mailSender.send(message);
    }
}

测试

复杂邮件发送

可以写html,发送附件(图片、文本、视频)。

@Test
void test() throws MessagingException {
    // 一个复杂的邮件
    MimeMessage mimeMessage = mailSender.createMimeMessage();

    // 组装 参数二:是否支持多文本上传
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

    // 标题
    helper.setSubject("测试");

    // 正文 参数二:开启对html的支持
    helper.setText("<div style='color: red;'>测试文字</div>", true);

    // 添加附件 new File("一个资源的绝对路径")
    helper.addAttachment("1.png", new File("C:\\Users\\l'x'c\\Desktop\\1.png"));
    helper.addAttachment("1.png", new File("C:\\Users\\l'x'c\\Desktop\\1.png"));

    helper.setFrom("1063614453@qq.com");
    helper.setTo("1063614453@qq.com");

    mailSender.send(mimeMessage);
}

### Spring Boot使用 `@Async` 实现异步方法的最佳实践 #### 1. 开启异步功能 为了使 `@Async` 注解生效,需要在应用程序的主类或者配置类上添加 `@EnableAsync` 注解。这一步是必要的,因为只有启用该注解后,Spring 才会识别并管理带有 `@Async` 的方法。 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } } ``` 此部分的操作已在多个引用中提及[^2][^4]。 --- #### 2. 定义异步方法 任何希望被异步执行的方法都可以加上 `@Async` 注解。需要注意的是,默认情况下,这些方法会被放入由 Spring 创建的线程池中运行。 ```java import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncTaskService { @Async public void executeAsyncTask() { System.out.println("任务正在异步执行..."); } @Async public Future<String> executeAsyncWithResult() throws InterruptedException { Thread.sleep(2000); // 模拟耗时操作 return new AsyncResult<>("异步返回结果"); } } ``` 上述代码展示了两种常见的场景:一种是没有返回值的任务;另一种是有返回值的任务,并且可以通过 `Future` 对象获取其结果[^3]。 --- #### 3. 自定义线程池 虽然 Spring 默认提供了一个简单的线程池用于异步任务调度,但在实际生产环境中更推荐自定义线程池以满足特定需求。可以借助 `ThreadPoolTaskExecutor` 来完成这一目标: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class ThreadPoolConfig { @Bean(name = "taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); // 核心线程数 executor.setMaxPoolSize(10); // 最大线程数 executor.setQueueCapacity(25); // 队列容量 executor.initialize(); // 初始化线程池 return executor; } } ``` 通过这种方式,我们可以精确控制线程的数量及其行为模式,从而优化系统的性能表现[^1]。 --- #### 4. 超时处理机制 当某些异步任务可能长时间未响应时,我们需要为其设定合理的超时策略以防阻塞资源。下面是一个基于 `CompletableFuture` 的例子展示如何实现这一点: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; public String callWithTimeout(int timeoutInSeconds) throws ExecutionException, TimeoutException, InterruptedException { CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(timeoutInSeconds * 1000L); // 模拟延迟 } catch (InterruptedException e) { throw new RuntimeException(e); } return "成功"; }, taskExecutor()); return future.get(timeoutInSeconds / 2, TimeUnit.SECONDS); // 设置一半时间为超时时限 } ``` 这里利用了 Java 提供的高级 API —— `CompletableFuture` 和它的 `get(long timeout, TimeUnit unit)` 方法来简化超时逻辑的设计。 --- #### 总结 以上就是关于如何在 Spring Boot 应用程序中有效运用 `@Async` 进行异步编程的一些最佳实践建议。合理调整线程池参数以及引入适当的错误恢复手段都是构建健壮服务不可或缺的部分。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值