5.2任务
这里说的任务系统指的是定时任务。定时任务是企业级开发中必不可少的组成部分,如长周期业务数据的计算,如年度报表,如系统脏数据的处理,再比如系统性能监控报告,还有抢购类活动的商品上架,这些都离不开定时任务。
①Quartz
Quartz技术是一个比较成熟的定时任务框架,但是配置略微复杂,springboot对其进行整合后,简化了一系列的配置,将很多配置采用默认设置,这样开发阶段就简化了很多。
- 学习springboot整合Quartz前先了解几个Quartz的概念。
- 工作(Job):用于定义具体执行的工作
- 工作明细(JobDetail):用于描述定时工作相关的信息
- 触发器(Trigger):描述了工作明细与调度器的对应关系
- 调度器(Scheduler):用于描述触发工作的执行规则,通常使用cron表达式定义规则
-
导入springboot整合Quartz的starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
-
定义任务Bean,按照Quartz的开发规范制作,继承QuartzJobBean
public class MyQuartz extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("quartz task run..."); } }
-
创建Quartz配置类,定义工作明细(JobDetail)与触发器的(Trigger)bean
@Configuration public class QuartzConfig { @Bean public JobDetail printJobDetail(){ //绑定具体的工作 return JobBuilder.newJob(MyQuartz.class).storeDurably().build(); } @Bean public Trigger printJobTrigger(){ ScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?"); //绑定对应的工作明细 return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(schedBuilder).build(); } }
工作明细中要设置对应的具体工作,使用newJob()操作传入对应的工作任务类型即可。
触发器需要绑定任务,使用forJob()操作传入绑定的工作明细对象。此处可以为工作明细设置名称然后使用名称绑定,也可以直接调用对应方法绑定。触发器中最核心的规则是执行时间,此处使用调度器定义执行时间,执行时间描述方式使用的是cron表达式。
cron表达式的规则,略微复杂,而且格式不能乱设置,不是写个格式就能用的,写不好就会出现冲突问题。
②Task
spring根据定时任务的特征,将定时任务的开发简化到了极致。
-
开启定时任务功能,在引导类上开启定时任务功能的开关,使用注解@EnableScheduling
@SpringBootApplication //开启定时任务功能 @EnableScheduling public class Springboot22TaskApplication { public static void main(String[] args) { SpringApplication.run(Springboot22TaskApplication.class, args); } }
-
定义Bean,在对应要定时执行的操作上方,使用注解@Scheduled定义执行的时间,执行时间的描述方式还是cron表达式
@Component public class MyBean { @Scheduled(cron = "0/1 * * * * ?") public void print(){ System.out.println(Thread.currentThread().getName()+" :spring task run..."); } }
-
如何想对定时任务进行相关配置,可以通过配置文件进行
spring: task: scheduling: pool: size: 1 # 任务调度线程池大小 默认 1 thread-name-prefix: ssm_ # 调度线程名称前缀 默认 scheduling- shutdown: await-termination: false # 线程池关闭时等待所有任务完成 await-termination-period: 10s # 调度线程关闭前最大等待时间,确保最后一定关闭
-
5.3邮件
学习邮件发送之前先了解3个概念,这些概念规范了邮件操作过程中的标准。
- SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
- POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
- IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
简单说就是SMPT是发邮件的标准,POP3是收邮件的标准,IMAP是对POP3的升级。
我们制作程序中操作邮件,通常是发邮件,所以SMTP是使用的重点,收邮件大部分都是通过邮件客户端完成,所以开发收邮件的代码极少。
除非你要读取邮件内容,然后解析,做邮件功能的统一处理。例如HR的邮箱收到求职者的简历,可以读取后统一处理。
①发送简单邮件
-
导入springboot整合javamail的starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
配置邮箱的登录信息
#java程序仅用于发送邮件,邮件的功能还是邮件供应商提供的,所以这里是用别人的邮件服务,要配置对应信息。 spring: mail: #host配置的是提供邮件服务的主机协议,当前程序仅用于发送邮件,因此配置的是smtp的协议。 host: smtp.126.com username: [email protected] #password并不是邮箱账号的登录密码,是邮件供应商提供的一个加密后的密码 password: test
-
使用JavaMailSender接口发送邮件
@Service public class SendMailServiceImpl implements SendMailService { @Autowired private JavaMailSender javaMailSender; //将发送邮件的必要信息(发件人、收件人、标题、正文)封装到SimpleMailMessage对象中,可以根据规则设置发送人昵称等。 //发送人 private String from = "[email protected]"; //接收人 private String to = "[email protected]"; //标题 private String subject = "测试邮件"; //正文 private String context = "测试邮件正文内容"; @Override public void sendMail() { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(from+"(小甜甜)"); message.setTo(to); message.setSubject(subject); message.setText(context); javaMailSender.send(message); } }
②发送网页正文邮件
-
此处只修改上面的发送内容
@Service public class SendMailServiceImpl2 implements SendMailService { @Autowired private JavaMailSender javaMailSender; //发送人 private String from = "[email protected]"; //接收人 private String to = "[email protected]"; //标题 private String subject = "测试邮件"; //正文 private String context = "<img src='test.JPG'/><a href='https://www.baidu.com'>点开有惊喜</a>"; public void sendMail() { try { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message); helper.setFrom(to+"(小甜甜)"); helper.setTo(from); helper.setSubject(subject); helper.setText(context,true); //此处设置正文支持html解析 javaMailSender.send(message); } catch (Exception e) { e.printStackTrace(); } } }
③发送带有附件的邮件
-
同上
@Service public class SendMailServiceImpl2 implements SendMailService { @Autowired private JavaMailSender javaMailSender; //发送人 private String from = "[email protected]"; //接收人 private String to = "[email protected]"; //标题 private String subject = "测试邮件"; //正文 private String context = "测试邮件正文"; public void sendMail() { try { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message,true); //此处设置支持附件 helper.setFrom(to+"(小甜甜)"); helper.setTo(from); helper.setSubject(subject); helper.setText(context); //添加附件 File f1 = new File("mail.jar"); File f2 = new File("resources\\test.png"); helper.addAttachment(f1.getName(),f1); helper.addAttachment("测试.png",f2); javaMailSender.send(message); } catch (Exception e) { e.printStackTrace(); } } }
5.4消息
从广义角度来说,消息其实就是信息,但是和信息又有所不同。信息通常被定义为一组数据,而消息除了具有数据的特征之外,还有消息的来源与接收的概念。通常发送消息的一方称为消息的生产者,接收消息的一方称为消息的消费者。这样比较后,发现其实消息和信息差别还是很大的。
信息通常就是一组数据,但是消息由于有了生产者和消费者,就出现了消息中所包含的信息可以被二次解读,生产者发送消息,可以理解为生产者发送了一个信息,也可以理解为生产者发送了一个命令; 消费者接收消息,可以理解为消费者得到了一个信息,也可以理解为消费者得到了一个命令。对比一下我们会发现信息是一个基本数据,而命令则可以关联下一个行为动作,这样就可以理解为基于接收的消息相当于得到了一个行为动作,使用这些行为动作就可以组织成一个业务逻辑,进行进一步的操作。 总的来说,消息其实也是一组信息,只是为其赋予了全新的含义,因为有了消息的流动,并且是有方向性的流动,带来了基于流动的行为产生的全新解读。开发者就可以基于消息的这种特殊解,将其换成代码中的指令。
- 对于消息的生产者与消费者的工作模式,还可以将消息划分成两种模式,同步消费与异步消息。
- 同步消息就是生产者发送完消息,等待消费者处理,消费者处理完将结果告知生产者,然后生产者继续向下执行业务。
- 异步消息就是生产者发送完消息,无需等待消费者处理完毕,生产者继续向下执行其他动作。
①Java处理消息的标准规范
- 目前企业级开发中广泛使用的消息处理技术共三大类
- JMS
- AMQP
- MQTT
为什么是三大类,而不是三个技术呢?因为这些都是规范,就想JDBC技术,是个规范,开发针对规范开发,运行还要靠实现类,例如MySQL提供了JDBC的实现,最终运行靠的还是实现。并且这三类规范都是针对异步消息进行处理的,也符合消息的设计本质,处理异步的业务。
JMS
JMS(Java Message Service),这是一个规范,作用等同于JDBC规范,提供了与消息服务相关的API接口。
-
JMS消息模型
- 点对点模型:peer-2-peer,生产者会将消息发送到一个保存消息的容器中,通常使用队列模型,使用队列保存消息。一个队列的消息只能被一个消费者消费,或未被及时消费导致超时。这种模型下,生产者和消费者是一对一绑定的。
- 发布订阅模型:publish-subscribe,生产者将消息发送到一个保存消息的容器中,也是使用队列模型来保存。但是消息可以被多个消费者消费,生产者和消费者完全独立,相互不需要感知对方的存在。
- 以上这种分类是从消息的生产和消费过程来进行区分,针对消息所包含的信息不同,还可以进行不同类别的划分。
-
JMS消息种类
- TextMessage
- MapMessage
- BytesMessage
- StreamMessage
- ObjectMessage
- Message (只有消息头和属性
- JMS主张不同种类的消息,消费方式不同,可以根据使用需要选择不同种类的消息。
AMQP
JMS的问世为消息中间件提供了很强大的规范性支撑,但是使用的过程中就开始被人诟病,比如JMS设置的极其复杂的多种类消息处理机制。本来分门别类处理挺好的 为什么会被诟病呢?原因就在于JMS的设计是J2EE规范,站在Java开发的角度思考问题。但是现实往往是复杂度很高的。 比如我有一个.NET开发的系统A,有一个Java开发的系统B,现在要从A系统给B系统发业务消息,结果两边数据格式不统一,没法操作。 JMS不是可以统一数据格式吗?提供了6种数据种类,总有一款适合你啊。NO,一个都不能用。因为A系统的底层语言不是Java语言开发的,根本不支持那些对象。 这就意味着如果想使用现有的业务系统A继续开发已经不可能了,必须推翻重新做使用Java语言开发的A系统。
AMQP的出现解决的是消息传递时使用的消息种类的问题,化繁为简,但是其并没有完全推翻JMS的操作API,所以说AMQP仅仅是一种协议,规范了数据传输的格式而已。
AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS操作。
- 优点
- 具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现
- AMQP消息种类
- byte[]
- AMQP消息模型
- AMQP在JMS的消息模型基础上又进行了进一步的扩展,除了
点对点
和发布订阅
的模型,开发了几种全新的消息模型,适应各种各样的消息发送。- direct exchange
- fanout exchange
- topic exchange
- headers exchange
- system exchange
- 目前实现了AMQP协议的消息中间件技术也很多,而且都是较为流行的技术,例如:RabbitMQ、StormMQ、RocketMQ
- AMQP在JMS的消息模型基础上又进行了进一步的扩展,除了
MQTT
MQTT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一。由于与JavaEE企业级开发没有交集,不学。
KafKa
Kafka,一种高吞吐量的分布式发布订阅消息系统,提供实时消息功能。Kafka技术并不是作为消息中间件为主要功能的产品,但是其拥有发布订阅的工作模式,也可以充当消息中间件来使用,而且目前企业级开发中其身影也不少见。
②购物订单发送手机短信案例
为了便于下面演示各种各样的消息中间件技术,我们创建一个购物过程生成订单时为用户发送短信的案例环境,模拟使用消息中间件实现发送手机短信的过程。
手机验证码案例需求
执行下单业务时(模拟此过程),调用消息服务,将要发送短信的订单id传递给消息中间件
消息处理服务接收到要发送的订单id后输出订单id(模拟发短信)
由于不涉及数据读写,仅开发业务层与表现层,其中短信处理的业务代码独立开发
订单业务
-
业务层接口
//模拟传入订单id,执行下订单业务,参数为虚拟设定,实际应为订单对应的实体类 public interface OrderService { void order(String id); }
-
业务层实现
//业务层转调短信处理的服务MessageService @Service public class OrderServiceImpl implements OrderService { @Autowired private MessageService messageService; @Override public void order(String id) { //一系列操作,包含各种服务调用,处理各种业务 System.out.println("订单处理开始"); //短信消息处理 messageService.sendMessage(id); System.out.println("订单处理结束"); System.out.println(); } }
-
表现层服务
//表现层对外开发接口,传入订单id即可(模拟) @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderService orderService; @PostMapping("{id}") public void order(@PathVariable String id){ orderService.order(id); } }
短信处理业务
-
业务层接口
//短信处理业务层接口提供两个操作,发送要处理的订单id到消息中间件,另一个操作目前暂且设计成处理消息,实际消息的处理过程不应该是手动执行,应该是自动执行,到具体实现时再进行设计 public interface MessageService { void sendMessage(String id); String doMessage(); }
-
业务层实现
//短信处理业务层实现中使用集合先模拟消息队列,观察效果 @Service public class MessageServiceImpl implements MessageService { private ArrayList<String> msgList = new ArrayList<String>(); @Override public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列,id:"+id); msgList.add(id); } @Override public String doMessage() { String id = msgList.remove(0); System.out.println("已完成短信发送业务,id:"+id); return id; } }
-
表现层服务
// 短信处理表现层接口暂且开发出一个处理消息的入口,但是此业务是对应业务层中设计的模拟接口,实际业务不需要设计此接口。 @RestController @RequestMapping("/msgs") public class MessageController { @Autowired private MessageService messageService; @GetMapping public String doMessage(){ String id = messageService.doMessage(); return id; } }
下面开启springboot整合各种各样的消息中间件,从严格满足JMS规范的ActiveMQ开始
标签:String,private,public,消息,id,邮件,SpringBoot From: https://www.cnblogs.com/Myvlog/p/17259953.html