利用Javax.mail 包
JavaMail简述
JavaMail是Java编程语言中用于发送、接收和处理电子邮件的标准API。它提供了一组类和接口,可以轻松地与邮件服务器进行通信,并执行各种与电子邮件相关的操作,如发送邮件、接收邮件、搜索邮件、删除邮件、处理附件等。
JavaMail API中提供了以下几个核心接口:
-
Session(会话):表示JavaMail API与邮件服务器之间的连接会话。通过Session对象,可以设置诸如服务器组织名,协议、身份验证等属性。
getInstance(Properties props, Authenticator authenticator): 创建一个新的邮件会话实例。使用给定的属性(props)和认证器(authenticator)配置邮件会话。getTransport(String protocol): 获取用于发送邮件的Transport实例。参数protocol指定邮件传输协议,如"smtp"或"imap"。getStore(String protocol): 获取用于接收邮件的Store实例。参数protocol指定邮件存储协议,如"pop3"或"imap"。getProperty(String name): 获取指定名称的会话属性值。setDebug(boolean debug): 设置是否启用调试模式。如果启用调试模式,会输出详细的日志信息,方便调试邮件相关操作。getTransportProtocol(): 获取当前会话的默认邮件传输协议。getStoreProtocol(): 获取当前会话的默认邮件存储协议。getProperties(): 获取当前会话的属性集合。getSession.(getDefaultInstance(Properties props, Authenticator authenticator): 获取默认的邮件会话实例。使用给定的属性(props)和认证器(authenticator)配置邮件会话。
-
Store(存储):表示一个邮件存储对象,用于连接到邮件服务器的特定存储,并打开一个或多个邮件文件夹,如收件箱、已发送等。
connect(String host, String username, String password): 连接到指定的邮件服务器。需要提供邮件服务器的主机名(host)、用户名(username)和密码(password)。close(): 关闭与邮件服务器的连接。isConnected(): 检查当前是否已连接到邮件服务器。getFolder(String name): 获取指定名称的邮件文件夹。参数name为文件夹的名称,如"Inbox"、"Sent"等。
入参值 解释 INBOX 收件箱 SENT 已发送文件夹 DRAFTS 草稿 TRASH 垃圾箱 SPAM 垃圾邮件文件夹 getDefaultFolder(): 获取默认邮件文件夹,通常是用户的根文件夹。getPersonalNamespaces(): 获取个人邮件命名空间。返回一个Folder数组,包含当前用户的个人邮件命名空间的文件夹。getSharedNamespaces(): 获取共享邮件命名空间。返回一个Folder数组,包含当前用户共享的邮件命名空间的文件夹。create(Folder folder): 在邮件服务器上创建一个新的邮件文件夹。deleteFolder(Folder folder): 删除邮件服务器上的邮件文件夹。renameFolder(Folder folder, String newName): 重命名邮件服务器上的邮件文件夹。hasCapability(String capability): 检查邮件服务器是否支持指定的功能。getURLName(): 获取与当前存储相关联的URLName对象。
-
Folder(文件夹):表示邮件存储中的一个文件夹,可用于访问和操作其中的邮件。常见的文件夹包括收件箱、已发送、草稿等。
open(int mode): 打开文件夹。使用指定的模式打开文件夹,可以是只读模式(Folder.READ_ONLY)或读写模式(Folder.READ_WRITE)。close(boolean expunge): 关闭文件夹。关闭文件夹并可选择是否执行彻底删除操作(expunge为true表示执行)。关闭后,文件夹将不再可用。getMessages(): 获取文件夹中的所有邮件。返回一个Message数组,包含文件夹中所有的邮件。getMessageCount(): 获取文件夹中的邮件总数。getUnreadMessageCount(): 获取文件夹中未读邮件的数量。getNewMessageCount(): 获取文件夹中新邮件的数量。delete(boolean expunge): 删除文件夹中的所有邮件。可选择是否执行彻底删除操作(expunge为true表示执行)。create(int type): 创建子文件夹。在当前文件夹下创建一个新的子文件夹,使用指定的类型(type)。list(String pattern): 列出满足指定模式匹配规则的子文件夹。返回一个Folder数组,包含满足指定模式匹配规则的子文件夹。search(SearchTerm term): 根据指定的搜索条件搜索文件夹中的邮件。返回一个满足搜索条件的Message数组。- SearchTerm 接口:该接口是一个抽象接口,用于定义搜索条件。
- FormTrem:按发件人地址搜索
- SubjectTerm:按主题搜索
- SentDataTerm:按发送日期搜索
- FlagTerm 类:该类用于按邮件标记进行搜索。
标记 解释 Flags.Flag.SEEN 已读标记 Flags.Flag.RECENT 最近收到的标记 Flags.Flag.DELETED 已删除标记 - Address 接口和相关类:表示电子邮件地址,它有两个具体的实现类
- InternetAddress: 用于表示Internet邮件地址
- NewsAddress:用于表示新闻组地址
- Message 类:该类代表一份邮件,可以用作指定模板查找需要的邮件
- SearchTerm 接口:该接口是一个抽象接口,用于定义搜索条件。
copyMessages(Message[] msgs, Folder folder): 将指定的邮件复制到目标文件夹中。moveMessages(Message[] msgs, Folder folder): 将指定的邮件移动到目标文件夹中。
-
Message(邮件):表示一封邮件消息,包含邮件的发件人、收件人、主题、正文、附件等消息。可以使用Message对象读取和设置邮件的各种属性。
getSubject(): 获取邮件主题。setSubject(String subject): 设置邮件主题。getFrom(): 获取发件人地址数组。setFrom(Address address): 设置发件人地址。getRecipients(Message.RecipientType type): 获取指定类型(如收件人、抄送人、密送人)的地址数组。setRecipients(Message.RecipientType type, Address[] addresses): 设置指定类型的地址。getSentDate(): 获取邮件发送日期。setSentDate(Date sentDate): 设置邮件发送日期。getContent(): 获取邮件内容。setText(String text): 设置纯文本类型的邮件内容。setHtml(String html): 设置HTML类型的邮件内容。attachFile(File file): 添加附件。saveChanges(): 保存对邮件的修改。isMimeType(String mimeType): 检查邮件是否具有指定的MIME类型。
入参值 解释 text/plain 是否是纯文本消息 text/html 是否是HTML消息 multipart/* 是否是复合类型数据(不关心具体的子类型) multipart/mixed 是否是包含附件的符合类型数据 multipart/alternative 是否是包含多个邮件正文的复合类型消息 application/octet-stream 是否是二进制附件 image/jpeg 是否是JPEG格式的图片类型 message/rfc822 是否是嵌套消息(如嵌套的邮件) application/pdf 是否是PDF文件 getFlags(): 获取邮件标志(Flag)。setFlag(Flag flag, boolean value): 设置邮件标志。reply(boolean replyToAll): 创建一个回复邮件。forward(): 创建一个转发邮件。
-
Address(地址):表示电子邮件地址,包括发件人地址、收件人地址等。可以使用Address对象构建邮件的收件人、抄送、密送列表。
getType(): 获取地址类型,返回一个字符串(如 "to"、"cc"、"bcc")。getPersonal(): 获取地址的个人名称(如果有)。getAddress(): 获取地址的电子邮件地址部分。writeTo(OutputStream out): 将地址写入输出流。writeTo(StringBuffer sb): 将地址写入StringBuffer。toUnicodeString(): 返回地址的Unicode表示形式。
-
Multipart(多部份):表示多部份消息,用于处理包含附件或嵌入内容的邮件。
getCount(): 获取多部份邮件中包含的邮件部分数量getBodyPart(int index): 获取指定索引处的邮件部分addBodyPart(BodyPart part): 向多部份邮件中添加邮件部分removeBodyPart(BodyPart part): 向多部份邮件中删除指定的邮件部分removeBodyPart(int index):从多部分邮件中移除指定索引处的邮件部分
-
BodyPart(邮件内容):表示邮件的一个部分,如正文、附件等。邮件内容可以是纯文本、HTML、图像、多媒体等形式。
getContentType():获取邮件部分的内容类型getContent:获取邮件部分的内容setContent(Object content, String contentType):设置邮件部分的内容和内容类型getDisposition: 获取邮件部分的处理方式(如附件的处理方式)
返回值 解释 attachment 表示是一个附件 inline 表示是内联附件,即以内联方式显示在正文中的附件 setDisposition(String disposition): 设置邮件部分的处理方式getFileName: 获取附件的文件名setFileName(String filename):设置附件的文件名isMimeType(String mimeType): 检查邮件是否符合指定的MIME类型addHeader(String name, String value):添加邮件头部的字段和值getHeader(String name): 获取指定名称的邮件头部字段的值setFlag(Flags.Flag flag, boolean value):设置邮件的标志isSet(Flags.Flag flag): 检查邮件是否已设置指定的标志
(8~12是其父类Part类提供的接口)
JavaMail API本身支持多种邮件协议,包括但不限于SMTP、POP3、IMAP等。(本文只实现POP3、IMAP双协议,后续再补全)。 8. Transport(运输):表示邮件传输对象,可以使用该接口发送邮件以及附件
IMAP(Internet Message Access Protocol)和POP3(Post Office Protocol version 3)协议差别
- 邮件存储方式:
- POP3协议将邮件从邮件服务器下载到本地设备,并将其从服务器上删除。它使用单向通信模式,即一旦邮件被下载,服务器上的副本就会被删除。
- IMAP 协议在邮件服务器上保留邮件的副本,并允许用户在不同设备上多次访问相同的邮件。它使用双向通信模式,支持对邮件进行管理、搜索、标记、文件夹同步等操作。
- 邮件访问:
- POP3 协议只
- 能从邮件服务器下载邮件,并在本地客户端上进行查看和管理。邮件只存在于本地设备上,无法在其他设备上访问之前下载的邮件。
- IMAP 协议允许用户在多个设备上访问邮件,并与服务器上的邮件进行同步。邮件保留在服务器上,可以在任何连接的设备上进行访问和管理。
- 邮件同步:
- 由于 POP3 协议的单向下载特性,如果在另一个设备上已经下载了邮件,那么在其他设备上将无法再次下载。
- IMAP 协议支持邮件同步,即使在多个设备上访问邮件,也可以保持彼此之间的同步状态。例如,删除或标记一封邮件后,所有连接的设备都会反映这些更改。
- 文件夹管理:
- POP3 协议通常只提供一个收件箱,并不支持创建和管理其他文件夹。
- IMAP 协议支持创建、重命名和删除文件夹,使得用户可以更好地组织和管理邮件。
- 邮件搜索和过滤:
- POP3 协议没有内置的邮件搜索和过滤功能,只能通过下载所有邮件并在本地进行搜索。
- IMAP 协议提供了强大的邮件搜索和过滤功能,可以根据条件(如发件人、主题、日期等)在服务器上直接搜索和筛选邮件。
- 下载附件区别
- POP3:邮件和附件会被完整下载到本地计算机,存储在客户端的邮件程序中,下载受本地计算机性能和存储设备速度限制。
- IMAP:邮件和附件会保留在邮件服务器上,存储在客户端的邮件程序中,下载受网速限制,如果附件较大或网络不稳定,下载速度可能会较慢。
- 总结:IMAP下载附件可能会稍慢于使用POP3,但IMAP提供了更好的远程管理和选择性下载附件的功能
总的来说,IMAP 协议相对于 POP3 协议提供了更灵活、更强大的邮件访问和管理功能。它适合那些需要随时在多个设备上访问和同步邮件的用户。而 POP3 协议则适合那些只需将邮件下载到单个设备上进行查看和管理的用户。选择使用哪种协议取决于你的具体需求和使用场景。
发送邮件代码(阿里云示范)
package com.ycansoft.input.common.utils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.security.Security;
import java.util.Date;
import java.util.Properties;
/**
* @Version: 1.0
* @Description: TODO
* @Date: 2023/6/30 11:11
**/
public class TestUtil {
public static void main(String[] args) {
try{
//设置SSL连接、邮件环境
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties props = System.getProperties();
//协议
//props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.smtp.host", "smtp.mxhichina.com");//smtp服务器地址
//props.setProperty("mail.smtp.port", "25");//非加密端口
// 使用ssl加密方式,进行如下配置:
props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.socketFactory.port", "465");
props.setProperty("mail.smtp.auth", "true");//表示SMTP发送邮件,需要进行身份验证
props.setProperty("mail.smtp.from", "发件人地址");//mailfrom 参数
props.setProperty("mail.user","发件人地址");//发件人的账号
props.setProperty("mail.password","发件人密码");// 发件人的账号的密码,如果开启三方客户端安全密码请使用新生产的密码
//建立邮件会话
Session session = Session.getDefaultInstance(props, new Authenticator() {
//身份认证
protected PasswordAuthentication getPasswordAuthentication() {
//发件人的账号、密码
String userName = props.getProperty("mail.user");
String password = props.getProperty("mail.password");
return new PasswordAuthentication(userName, password);
}
});
//建立邮件对象
MimeMessage message = new MimeMessage(session);
//设置邮件的发件人
InternetAddress from = new InternetAddress("发件人地址","发件人名称"); //from 参数,可实现代发,注意:代发容易被收信方拒信或进入垃圾箱。
message.setFrom(from);
//设置邮件的收件人
String[] to = {"收件人地址1","收件人地址2","收件人地址3"};//收件人列表
InternetAddress[] sendTo = new InternetAddress[to.length];
for (int i=0;i<to.length;i++){
//System.out.println("发送到:" + to[i]);
sendTo[i] = new InternetAddress(to[i]);
}
//传入收件人
message.setRecipients(Message.RecipientType.TO,sendTo);
//设置邮件的主题
message.setSubject("邮件主题");
//设置邮件的文本
String content="邮件内容";
message.setContent(content,"text/html;charset=UTF-8");
//设置时间
message.setSentDate(new Date());
message.saveChanges();
//发送邮件
Transport.send(message);
System.out.println("发送成功!");
}catch(Exception e){
System.out.println("异常:"+e.toString());
}
}
}
接收邮件代码(QQ邮箱示范)
package com.ycansoft.input.controller.attachment;
import com.ycansoft.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.FlagTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import javax.mail.search.SubjectTerm;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.Properties;
/**
* @Version: 1.0
* @Description: 邮箱采集工具类
* @Date: 2023/6/26 16:55
**/
public class mailUtil {
/**
* 接收邮件,获取信息
* @param username 邮箱账号
* @param password 安全凭证(密码/安全凭证)
* @param protocol 协议编号
* @throws Exception
*/
public void resceive(String username,String password,String protocol) throws Exception{
// 1.获取session
Session session = connectEmail(protocol);
// 2.获取Store
Store store = session.getStore();
store.connect(username,password);
// 3.获取Fodler
Folder defaultFolder = store.getDefaultFolder();
if (Objects.isNull(defaultFolder)){
System.out.println("服务器不可用,");
return;
}
Folder folder = defaultFolder.getFolder("INBOX");
// 获取邮箱的权限,目前改权限为只读
folder.open(Folder.READ_ONLY);
System.out.println("邮件夹的URL名称:"+folder.getURLName().toString());
System.out.println("邮件总数:"+folder.getMessageCount());
System.out.println("未读邮件数:"+folder.getUnreadMessageCount());
System.out.println("新邮件数:"+folder.getNewMessageCount());
// 4. 搜索并处理邮件
searchMail(folder);
// 5.关流 (如果没有打开folder,folder不必关流)
folder.close();
store.close();
}
/**
* 获取POP3收信配置
*/
private Properties getPOP3(){
Properties properties = new Properties();
properties.setProperty("mail.store.protocol","pop3");
properties.setProperty("mail.pop3.host","pop.qq.com");
properties.setProperty("mail.pop3.socketFactory.port","995");
// SSL安全连接参数
properties.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("mail.pop3.socketFactory.fallback", "false");
return properties;
}
/**
* 获取 IMAP收信配置
* @return
*/
private Properties getIMAP(){
Properties properties = new Properties();
properties.setProperty("mail.store.protocol", "imap");
properties.setProperty("mail.imap.host", "imap.qq.com");
properties.setProperty("mail.imap.port", "993");
// SSL安全连接参数
properties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("mail.imap.socketFactory.fallback", "false");
return properties;
}
/**
* 根据连接协议生成 Session
* @param protocol
* @return
*/
private Session connectEmail(String protocol){
Session session = null;
// 如果是pop3协议
if (Objects.equals("pop3",protocol)){
// 获取连接
session = Session.getDefaultInstance(getPOP3());
} else {
session = Session.getDefaultInstance(getIMAP());
}
session.setDebug(true);
return session;
}
/**
* 将邮件主题转换为中文
* @param mailSubject 邮件主题
* @return
*/
private String transferChinese(String mailSubject){
try {
if (StringUtils.isBlank(mailSubject)){
// 如果邮件为空
return null;
}
mailSubject = MimeUtility.encodeText(new String(mailSubject.getBytes(StandardCharsets.UTF_8),
StandardCharsets.UTF_8), String.valueOf(StandardCharsets.UTF_8),"B");
mailSubject = MimeUtility.decodeText(mailSubject);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return mailSubject;
}
/**
* 文件拷贝,在用户进行附件下载时,可以把附件的InputStream传给用户进行下载
* @param is 输入流
* @param os 输出流
*/
private void copy(InputStream is, OutputStream os) throws IOException {
byte[] bytes = new byte[1024];
int len = 0;
while ( (len = is.read(bytes)) != -1){
os.write(bytes,0,len);
}
if (os != null){
os.close();
}
if (is != null){
is.close();
}
}
/**
* 保存文件
* @param part 邮箱部分
* @param fileName 文件名
* @throws MessagingException
* @throws IOException
*/
private void saveFile(Part part,String fileName) throws MessagingException, IOException {
// 获取附件地址
String path = "xxx/xxx";
// 打开附件的输入流
InputStream is = part.getInputStream();
Path directory = Paths.get(path);
// 如果目录不存在则直接新建目录
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
// 拼接文件路劲,生成绝对路径
Path filePath = directory.resolve(fileName);
// 复制文件
copy(is, Files.newOutputStream(filePath));
}
/**
* 处理 Part对象
* @param part
*/
private void receiverPart(Part part) {
String tempFilePath = null;
try {
// 判断有无附件
String disposition = part.getDisposition();
if (StringUtils.isNotNull(disposition)){
// 如果有附件
// 解决附近名乱码问题并获得附件名
String fileName = MimeUtility.decodeText(part.getFileName());
System.out.println("发现附件:"+fileName);
System.out.println("附件类型:"+MimeUtility.decodeText(part.getContentType()));
// 判断邮件 是否为 想要的附件类型进行下载操作(目前无判断直接保存附件)
saveFile(part,fileName);
} else {
// 如果没有附件
if (part.getContentType().startsWith("text/plain") || part.getContentType().startsWith("Text/plain")){
//是纯文本内容
System.out.println("邮件是纯文本,内容是:"+ part.getContent());
} else if (part.getContentType().startsWith("text/html")){
System.out.println("邮件内容是html格式");
} else {
System.out.println("邮件类型:"+ part.getContentType());
System.out.println("邮件内容是:"+ part.getContent());
}
}
} catch (MessagingException | IOException e) {
throw new RuntimeException(e);
}
}
/**
* 解析 multipart对象
* @param multipart
*/
private void receiverMultipart(Multipart multipart) throws MessagingException, IOException {
// 获取邮件多部份数量
int partCount = multipart.getCount();
System.out.println("Multipart邮件共有"+ partCount + "部分组成");
// 依次处理多个部分
for (int i = 0; i < partCount; i++) {
Part part = multipart.getBodyPart(i);
// 解包,取出 Multipart的各个部分。每个部分可能是邮件,也有可能又是一个复杂邮件
if (part.getContent() instanceof Multipart){
System.out.println("部分" + i + "的ContentType:"+ part.getContentType()+"执行receiverMultipart方法");
// 转成小包裹,再次递归调用
Multipart p = (Multipart) part.getContent();
receiverMultipart(p);
} else {
System.out.println("部分" + i + "的ContentType:" + part.getContentType() + "执行receiverPart方法");
receiverPart(part);
}
}
}
/**
* 解析邮件对象
* @param message 邮件对象
*/
private void receiverMail(Message message) throws MessagingException, IOException {
// 邮件主题
String mailSubjects = transferChinese(message.getSubject());
System.out.println("邮件主题:"+ mailSubjects);
// 发件人信息
Address[] froms = message.getFrom();
if (ArrayUtils.isNotEmpty(froms)){
InternetAddress address = (InternetAddress) froms[0];
System.out.println("发件人地址:"+ address.getAddress());
System.out.println("发件人名称:"+ transferChinese(address.getPersonal()));
} else {
System.out.println("邮件发送人地址为空");
}
// 邮件发送日期(收件日期)
Date sentDate = message.getSentDate();
if (Objects.isNull(sentDate)){
System.out.println("邮件发送日期为空");
} else {
System.out.println("邮件发送日期:"+ sentDate);
}
// 获取邮件内容 (Part相当于外包装)
Object content = message.getContent();
if (content instanceof Multipart){
Multipart multipart = (Multipart) content;
receiverMultipart(multipart);
} else if (content instanceof Part){
Part part = (Part) content;
receiverPart(part);
} else {
String contentType = message.getContentType();
if (contentType != null && contentType.startsWith("text/html")) {
System.out.println("html类型:" + contentType);
} else {
System.out.println("---类型:" + contentType);
System.out.println("---内容:" + message.getContent());
}
}
}
/**
* 创建搜索条件 (如昨天收到的,主题中有"新闻",并且已读的邮件)
* @param folder
*/
private void searchMail(Folder folder) throws MessagingException, IOException {
// 获取当前时间
Calendar calendar = Calendar.getInstance();
// 获取昨天日期
calendar.add(Calendar.DAY_OF_MONTH,-1);
Date date = calendar.getTime();
// 构建搜索条件
SearchTerm searchTerm = new AndTerm(new SearchTerm[]{
new ReceivedDateTerm(ComparisonTerm.EQ,date),
new SubjectTerm("新闻"),
new FlagTerm(new Flags(Flags.Flag.SEEN),true)
});
Message[] messages = folder.search(searchTerm);
for (int i = 0; i < messages.length; i++) {
receiverMail(messages[i]);
}
}
}
关于IMAP/POP3协议地址(持续更新)
| 邮箱 | 端口 | 协议 | 地址 |
|---|---|---|---|
| 163.com/126.com/yeah.net(网易三大邮箱) | pop3 | 995(启用SSl协议)110 | pop.163.com/pop.126.com/pop.yeah.net |
| IMAP | 993(启用SSL协议)143 | imap.163.com/imap.126.com/imap.yeah.net | |
| 139.com/10086.cn(中国移动) | pop3 | 995(启用SSL协议) 143 | |
| IMAP | 993(启用SSL协议) 110 | ||
| qq.com | pop3 | 995(启用SSL协议) 143 | |
| IMAP | 993(启用SSL协议) 110 |
MailSender包(后续更新)
MailSender 包是一个抽象层级或模块,用于封装与电子邮件发送相关的功能。它提供了一组类或接口,用于简化和统一发送邮件的操作。