目录
1.异步任务
无返回值异步任务
效果
有返回值异步任务
效果
2.定时任务
效果
3.邮件任务
获取授权码
加入依赖
配置文件
纯文本邮件
效果
复杂邮件
效果
1.异步任务
@Async注释:
SpringBoot中的异步调用,可以写在类上或方法上,异步调用只是发送调用的指定,调用者无需等待被调用的方法完全执行完毕,而是继续执行下面流程。
@EnableAsync:
SpringBoot启动类开启异步支持。
无返回值异步任务
为了释放主线程
实现类
/**
* 无返回值异步任务
*/
@Service
//异步任务注释
@Async
public class TimeService {
public void sendSMS() {
System.out.println("发短信启动-----------------");
long l = System.currentTimeMillis();
//模拟发动短信过程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long l1 = System.currentTimeMillis();
System.out.println("短信业务耗时" + (l1 - l));
}
}
控制层
/**
* 无返回值异步任务
* 为了释放主线程
*
* @return
*/
@Autowired
TimeService timeService;
@RequestMapping("/text1")
public String SendSMS() {
System.out.println("主程序启动-----------------");
long timeMillis = System.currentTimeMillis();
//异步任务
timeService.sendSMS();
long timeMillis1 = System.currentTimeMillis();
System.out.println("主线程耗时" + (timeMillis1 - timeMillis));
return "主流程已完成,已释放";
}
效果
使用前:
主线程需要等待子线程完成后才结束。
使用后:
主线程已经进行完毕,子线程还在继续。
有返回值异步任务
AsyncResult类:封装异步任务上的异步操作的结果
Future类:表示一个可能还没有完成的异步任务的结果
Future的主要方法:
get()方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
get(long timeout,TimeUnit unit)做多等待timeout的时间就会返回结果
cancel(boolean mayInterruptIfRunning)方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false.
isDone()方法判断当前方法是否完成
isCancel()方法判断当前方法是否取消
实现类
此时@Async注解写在方法上,表示4个线程同时进行,主线程需要四个线程返回值的和
/**
* 有返回值异步任务
*/
@Service
public class MyAsyncService {
//异步任务注释
@Async
//Future<Integer>只能在这里和@Async配合使用
public Future<Integer> processA() {
System.out.println("开始分析并统计业务A数据...");
Long startTime = System.currentTimeMillis();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int count = 123;
Long endTime = System.currentTimeMillis();
System.out.println("业务A数据统计耗时:" + (endTime - startTime));
//封装在AsyncResult结果集中
return new AsyncResult<Integer>(count);
}
@Async
//Future<Integer>只能在这里和@Async配合使用
public Future<Integer> processB() {
System.out.println("开始分析并统计业务B数据...");
Long startTime = System.currentTimeMillis();
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int count = 456;
Long endTime = System.currentTimeMillis();
System.out.println("业务B数据统计耗时:" + (endTime - startTime));
//封装在AsyncResult结果集中
return new AsyncResult<Integer>(count);
}
@Async
//Future<Integer>只能在这里和@Async配合使用
public Future<Integer> processC() {
System.out.println("开始分析并统计业务C数据...");
Long startTime = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int count = 789;
Long endTime = System.currentTimeMillis();
System.out.println("业务C数据统计耗时:" + (endTime - startTime));
//封装在AsyncResult结果集中
return new AsyncResult<Integer>(count);
}
@Async
//Future<Integer>只能在这里和@Async配合使用
public Future<Integer> processD() {
System.out.println("开始分析并统计业务D数据...");
Long startTime = System.currentTimeMillis();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int count = 963;
Long endTime = System.currentTimeMillis();
System.out.println("业务D数据统计耗时:" + (endTime - startTime));
//封装在AsyncResult结果集中
return new AsyncResult<Integer>(count);
}
}
控制层
/**
* 有返回值异步任务
* 是为了释放完成任务的子线程
* @return
*/
@Autowired
MyAsyncService myAsyncService;
@RequestMapping("/text2")
public String statistics() throws ExecutionException, InterruptedException {
Long startTime = System.currentTimeMillis();
Future<Integer> futureA = myAsyncService.processA();
Future<Integer> futureB = myAsyncService.processB();
Future<Integer> futureC = myAsyncService.processC();
Future<Integer> futureD = myAsyncService.processD();
int total = futureA.get() + futureB.get()+ futureC.get()+ futureD.get();
System.out.println("异步任务数据统计汇总结果:" + total);
Long endTime = System.currentTimeMillis();
System.out.println("主流程耗时:" + (endTime - startTime));
return "子线程已完成,已释放主流程已完成";
}
效果
此时,线程完成自己的任务后即可释放。
2.定时任务
@EnableScheduling:
启用了Spring的任务调度功能,在配置类上使用,开启计划任务的支持
@Scheduled:
写在发放上,来声明这是一个任务,包括 cron,fixDelay,fixRate 等类型。无需调用,系统识别到注解会自动调用。
fixRate --按时间频率执行,控制方法执行的间隔时间,是以上一次方法执行完开始算起,如上一次方法执行阻塞住了,那么直到上一次执行完,并间隔给定的时间后,执行下一次。
fixDelay--延迟时间,按照一定的速率执行,是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行
cron--固定时间
域 | 是否必填 | 值以及范围 | 通配符 |
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | , - * / |
时 | 是 | 0-23 | , - * / |
日 | 是 | 1-31 | , - * ? / L W |
月 | 是 | 1-12 或 JAN-DEC | , - * / |
周 | 是 | 1-7 或 SUN-SAT | , - * ? / L # |
年 | 否 | 1970-2099 | , - * / |
* :通配,语义相当于每… 比如第五个位置的 *就表示每月都会执行(相当于1-12)
? :忽略,语义相当于不管… 比如第六个位置的?就表示不管当前是周几就会执行。至于为什么会有这种用法,我觉得应该是因为它和其他的字符可能会冲突。如果用*的话表示周一到周日都会执行,此时其他语义就不明确了,所以如果用不上星期的话一般给它用一个?表示 not care。
/ :间隔,语义相当于每隔… 比如例2中的第三个位置的2/5就表示从2点开始每隔五小时
- :区间,语义相当于第…到…的每… 比如例2中的第二个位置的15-20就表示第15分钟到20分钟之间的每分钟
, :枚举,语义相当于第…和… 比如例2中的第一个位置的15,20,40就表示第15秒、20秒和40秒
L :最后(last),语义相当于最后一个 比如例2中的第四个位置的L就表示最后一天
W :工作日,字面意思,就是工作日 比如例3中的第四个位置的15W表示15号附近最近的工作日,如果15号刚好是工作日那就15号触发,如果15号是周六那就14号触发,如果15号是周日那就16号触发。前面不带数字就是所有匹配的工作日。
'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资) 。
# :周定位,语义相当于每月的第几个周几 比如例4中的第六个位置的2#3就表示第三个周一。
周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同。
效果
@Scheduled(fixedRate = 1000)
public void ScheduledText(){
System.out.println("快背单词!");
}
每一秒打印一次
@Scheduled(fixedDelay = 1000)
public void ScheduledTest2(){
System.out.println("快去吃饭!------------------------");
}
延迟1秒打印一次
@Scheduled(cron = "0/10 08 19 * * ? ")
public void ScheduledTest3(){
System.out.println("要下课了!");
}
从19时8分0秒起,每10秒打印一次。
3.邮件任务
获取授权码
进入QQ邮箱,点击账号与安全
点击安全设置,先和手机进行绑定,如果绑定跳过这一步,直接跳到图一。
发送短信后点击我已发送。
用微信扫描二维码,发送短信后点击我已发送。
即可获得授权码
加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置文件
#发件人邮箱服务器相关配置
spring.mail.host=smtp.qq.com
spring.mail.port=587
#配置个人QQ账户和密码(密码是加密后的授权码)
spring.mail.username=3451866342@qq.com
spring.mail.password=oxfdtthfgoyrdbie
spring.mail.default-encoding=UTF-8
纯文本邮件
通过SimpleMailMessage类定制了邮件信息的发件人地址(From)、收件人地址(To)、邮件标题(ubject)和邮件内容(Text),最后使用JavaMailSenderImpl的send()方法实现纯文本邮件发送。
实现类
@Autowired
JavaMailSenderImpl javaMailSender;
@Value("${spring.mail.username}")//获得配置文件中的邮箱地址
private String from;
/**
* 纯文本邮件任务
*/
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);//发送邮箱
message.setSubject(subject);//邮件主题
message.setText(text);//邮件内容
javaMailSender.send(message);//发送邮件
System.out.println("纯文本邮件发送成功");
}
控制层
/**
* 邮件任务(纯文本邮件)
* @return
*/
@Autowired
SendEmailService sendEmailService;
@RequestMapping("/text3")
public String sendEmail(){
sendEmailService.sendSimpleEmail("3451866342@qq.com", "测试邮件", "这就是个邮件");
return "已发送完成";
}
效果
复杂邮件
编写一个发送带附件和图片邮件的业务方法sendComplexEmail() ,该方法需要接收的参数除了基本的发送信息外,还包括静态资源唯一标识、静态资源路径和附件路径。
实现类
/**
* 复杂邮件任务
*/
public void MailEmail(String to,String subject,String text,String filePath,String rscId,String rscId2,String rscPath,String rscPath2) {
// 创建一个 JavaMail MimeMessage 对象
MimeMessage message = javaMailSender.createMimeMessage();
try {
// 创建 MimeMessageHelper 对象,参数为 MimeMessage 对象和开启复杂邮件编码
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);//发送邮箱
helper.setTo(to);//接收邮箱
helper.setSubject(subject);//邮件主题
helper.setText(text, true);//邮件内容
// 创建一个 FileSystemResource 对象,表示给定的资源文件
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);//添加附件,将rscId和res附件联系一起
FileSystemResource res2 = new FileSystemResource(new File(rscPath2));
helper.addInline(rscId2, res2);//添加附件,将rscId和res附件联系一起
FileSystemResource file = new FileSystemResource(new File(filePath));
//截取文件名
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
javaMailSender.send(message);//发送邮件
System.out.println("复杂邮件发送成功");
} catch (MessagingException e) {
System.out.println("复杂邮件发送失败 " + e.getMessage());
e.printStackTrace();
}
}
控制层
/**
* 邮件任务(复杂邮件)
* @return
*/
@RequestMapping("/text4")
public String sendEmail2(){
String to = "3451866342@qq.com";
String subject = "冯怡然-22610302150754";//标题
StringBuilder builder = new StringBuilder();//页面
builder.append("<html><head></head>");
builder.append("<body><h1>今日农历二月十七日</h1>");
builder.append("<body><h1>甲辰龙年-丁卯月-己丑日</h1>");
String rscId = "img001";//图片id
builder.append("<img src='cid:img001'/>");
String rscPath = "C:\\Users\\Lenovo\\Desktop\\1.jpg";//图片路径
String rscId2 = "img002";
builder.append("<img src='cid:img002'/>");
String rscPath2 = "C:\\Users\\Lenovo\\Desktop\\2.jpg";
builder.append("<h3 color='red'>相信科学</h3>");
builder.append("</body>");
builder.append("</html>");
String filePath = "E:\\Typora\\导出文件\\SpringBoot3的RabbitMQ消息服务.pdf";//文件路径
sendEmailService.MailEmail(to, subject, builder.toString(), filePath,rscId, rscId2,rscPath, rscPath2);
return "已发送完成";
}
效果