一、异步任务
多线程实现异步任务。
前台一个请求发送到后端,如果是异步任务的话,且耗时比较长(例如发送邮件),前端页面不可能一直在那等着,等邮件发送出去,后端才返回结果。
解决这种一部问题,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 线程池概念、优缺点、应用场景_记录点滴-CSDN博客_线程池的优缺点
默认情况下,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);
}