背景
开发平台具备最简内核能力后,需要集成一些第三方功能组件进行能力扩展,例如邮件发送、任务调度等。
邮件通知功能在业务系统中扮演着至关重要的角色,其用途广泛,不仅提高了工作效率,还增强了通信的便捷性和及时性。以下是邮件通知功能的几种常见用途:
- 提醒和警报:
- 系统可以通过邮件提醒用户即将到期或变更的任务、会议、账单支付等。
- 向客户提供订单确认、发货通知、退货状态更新等信息。
- 提供客户反馈和投诉的自动回复,提升客户满意度。
- 在出现紧急情况或系统异常时,自动发送警报邮件给管理员或相关人员。
- 发送报表和报告:
- 定期发送业务报表、销售报告、财务报告等,帮助管理层和决策者及时了解业务状况。
- 向项目团队成员发送项目进度报告,确保信息同步和透明。
邮件通知是业务系统的通知机制的主要方式之一,相比短信通知,特点是无额外费用,通知内容更丰富,可使用html,可携带附件。
今天我们就来说下如何通过集成功能组件,来实现邮件发送能力。
功能组件
在Java中,用于邮件发送的功能组件主要可以分为两大类:JavaMail API(现在称为 Jakarta Mail)和第三方库。以下是一些常用的组件和库:
- JavaMail API / Jakarta Mail:
- 这是Sun Microsystems提供的官方API,用于发送邮件。它是一个功能丰富、灵活的API,支持SMTP、POP3和IMAP协议。
- 网址:Jakarta Mail
- Apache Commons Email:
- Apache Commons Email库简化了JavaMail的使用,提供了一些易于使用的高层API。
- 网址:Apache Commons Email
- Spring Framework:
- Spring框架提供了对JavaMail的支持,通过Spring的
MailSender
接口和相关类简化邮件发送。 - 网址:Spring Framework
- Spring框架提供了对JavaMail的支持,通过Spring的
- MailKit:
- MailKit是一个基于JavaMail API的邮件处理库,用于替代JavaMail API。
- 网址:MailKit
邮件发送是一个常见的典型通用需求,因此市面上也有众多的组件来支撑这块功能,上面列的仅是主流的几个。
平台基于SpringBoot脚手架,因此优先选择了spring-boot-starter-mail功能组件,其内部集成的实际是Jakarta Mail功能组件。
系统集成
创建模块
开发平台下单设一个模块,专门用于邮件发送功能的集成,命名为platform-boot-starter-mail。
模块内部结构如下:
添加依赖
在模块的pom文件中,增加组件依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
组件配置
发送邮件需要配置邮件服务器的信息,在项目的配置文件中,在spring的节点下,新增mail节点,以使用网易的126邮箱为例,配置邮件服务器的主机地址、账号和密码三项信息。
自定义配置
上面环节是配置邮件服务器的信息,实际上,收件人收取邮件时显示的发件人,可以一定程度上自行制定,例如网易邮箱可以为126邮箱绑定一个手机号,并可以开启类似别名功能,手机号+@126.com可以作为邮箱地址来发送或接收邮件。
平台自定义一个配置参数,来灵活设置发件人邮箱,如下:
package tech.abc.platform.mail.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 对象存储配置文件
*
* @author wqliu
* @date 2020-04-05 11:19
*/
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "platform-config.mail")
public class MailConfig {
/**
* 发送方地址
*/
private String senderAddress = "";
}
定义实体
将邮件的核心要素梳理出来,如发件人、收件人、抄送、密送、主题、内容、附件等,定义一个邮件类来对应,如下:
package tech.abc.platform.mail.entity;
import lombok.Data;
import java.util.List;
/**
* 邮件 实体对象
*
* @author wqliu
* @date 2023-05-25
*/
@Data
public class Mail {
/**
* 发件人
*/
private String from;
/**
* 收件人
*/
private String to;
/**
* 抄送人
*/
private String cc;
/**
* 密送人
*/
private String bcc;
/**
* 主题
*/
private String subject;
/**
* 内容
*/
private String text;
/**
* 附件路径
*/
private List<String> attachmentList;
}
定义服务接口
邮件发送的服务接口,平台规划和设计了两类,一类是发送简单的纯文本信息,另一类则是发送可以使用html和携带附件的富文本信息。
同时为了便于调用方使用,设置了重载方法,既可以构建一个邮件对象传入,也可以直接传入收件人、主题、内容三要素。
完整源码如下:
package tech.abc.platform.mail.service;
import tech.abc.platform.mail.entity.Mail;
/**
* 邮件服务
*
* @author wqliu
* @date 2023-05-25
*/
public interface MailService {
/**
* 发送纯文本邮件,传入参数
*
* @param to 收件人
* @param subject 主题
* @param content 内容
*/
void sendTextMail(String to, String subject, String content);
/**
* 发送纯文本邮件,传入对象,不可携带附件
*
* @param mail
*/
void sendTextMail(Mail mail);
/**
* 发送Html邮件,传入参数
*
* @param to 收件人
* @param subject 主题
* @param content 内容
*/
void sendHtmlMail(String to, String subject, String content);
/**
* 发送Html邮件,传入对象,可携带附件
*
* @param mail
*/
void sendHtmlMail(Mail mail);
}
服务实现
服务实现类比较简单,按照邮件功能组件的要求,组织数据调用即可,如下:
package tech.abc.platform.mail.service.impl;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import tech.abc.platform.common.exception.CustomException;
import tech.abc.platform.mail.config.MailConfig;
import tech.abc.platform.mail.entity.Mail;
import tech.abc.platform.mail.exception.MailExceptionEnum;
import tech.abc.platform.mail.service.MailService;
import javax.activation.FileDataSource;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.util.List;
/**
* 使用spring mail组件发送邮件的服务实现类
*
* @author wqliu
* @date 2023-05-25
*/
@Service
public class SpringMailServiceImpl implements MailService {
private static final String COMMA = ",";
@Autowired
private JavaMailSender mailSender;
@Autowired
private MailConfig mailConfig;
@Autowired
private MapperFacade mapperFacade;
@Override
public void sendTextMail(String to, String subject, String content) {
Mail mail = new Mail();
mail.setTo(to);
mail.setSubject(subject);
mail.setText(content);
sendTextMail(mail);
}
@Override
public void sendTextMail(Mail mail) {
try {
// 设置对象
SimpleMailMessage message = new SimpleMailMessage();
BeanUtils.copyProperties(mail, message);
// 发件人由后端指定
message.setFrom(mailConfig.getSenderAddress());
// 处理收件人
String[] to = getArray(mail.getTo());
if (to != null) {
message.setTo(to);
}
// 处理抄送人
String cc = mail.getCc();
if (cc != null) {
message.setCc(cc);
}
// 处理密送人
String bcc = mail.getBcc();
if (bcc != null) {
message.setBcc(bcc);
}
// 发送邮件
mailSender.send(message);
} catch (Exception e) {
throw new CustomException(MailExceptionEnum.SEND_ERROR, e.getMessage());
}
}
@Override
public void sendHtmlMail(String to, String subject, String content) {
Mail mail = new Mail();
mail.setTo(to);
mail.setSubject(subject);
mail.setText(content);
sendHtmlMail(mail);
}
@Override
public void sendHtmlMail(Mail mail) {
try {
// 设置对象
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
// 发件人由后端指定
messageHelper.setFrom(mailConfig.getSenderAddress());
// 处理收件人
String[] to = getArray(mail.getTo());
if (to != null) {
messageHelper.setTo(to);
}
// 处理抄送人
String cc = mail.getCc();
if (cc != null) {
messageHelper.setCc(cc);
}
// 处理密送人
String bcc = mail.getBcc();
if (bcc != null) {
messageHelper.setBcc(bcc);
}
messageHelper.setSubject(mail.getSubject());
messageHelper.setText(mail.getText(), true);
// 附件处理
List<String> attachmentList = mail.getAttachmentList();
if (CollectionUtils.isNotEmpty(attachmentList)) {
for (int i = 0; i < attachmentList.size(); i++) {
String filePath = attachmentList.get(i);
String filename = FilenameUtils.getName(filePath);
messageHelper.addAttachment(MimeUtility.encodeWord(filename), new FileDataSource(filePath));
}
}
// 发送邮件
mailSender.send(message);
} catch (Exception e) {
throw new CustomException(MailExceptionEnum.SEND_ERROR, e.getMessage());
}
}
/**
* 将字符串转换为数组
*
* @param address
* @return
*/
private String[] getArray(String address) {
if (StringUtils.isNotBlank(address)) {
if (StringUtils.contains(address, COMMA)) {
return address.split(COMMA);
} else {
return new String[]{address};
}
}
return null;
}
}
调用示例
下面是发送邮件服务的调用示例,对应的应用场景是当用户忘记密码时,输入注册时填写的邮箱地址,由系统发送一封重置密码的邮件,源码如下:
private void sendResetPasswordEmail(String email, String account) {
// 生成唯一性编码
String code = UUID.randomUUID().toString();
// 存入redis,24小时后失效
cacheUtil.set(code, account, 24, TimeUnit.HOURS);
// 生成内容
String systemUrl = systemConfig.getSystemUrl();
String resetUrl = systemUrl + "/#/selfResetPassword?code=" + code;
String content = MessageFormat.format("重设【遇见】应用密码,请点击链接或将其复制到浏览器地址栏:<a href=\"{0}\">{0}</a>", resetUrl);
// 使用线程异步发送邮件
Runnable runnable = new Runnable() {
@Override
public void run() {
mailService.sendHtmlMail(email, "重设【遇见】应用密码", content);
}
};
// noinspection AlibabaAvoidManuallyCreateThread
Thread thread = new Thread(runnable);
// 启动
thread.start();
}
此外,这里提一下,这个场景比较简单,直接使用字符串拼接的邮件正文内容。如对邮件正文有复杂的格式,并且需要灵活修改,可以使用平台业务支撑模块下的内容模板功能,使用富文本编辑器设置好格式、内容和变量,读取业务数据后,替换变量,再调用邮件发送功能。
开源平台资料
平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!