邮件群发工具实验报告
邮件群发工具实验报告
目录
一、项目概述
本项目是一个基于Java的邮件批量发送工具,通过整合Excel数据读取、模板渲染和邮件发送功能,实现了个性化邮件的批量推送。项目采用模块化设计,核心技术栈包括POI(Excel处理)、JavaMail(邮件发送)、FreeMarker(模板引擎),并通过Maven进行依赖管理,确保开发过程的高效与规范。
核心技术栈在项目中的落地应用
- POI(Apache POI):作为Excel数据解析的核心工具,我通过POI的
Workbook、Sheet、Row、Cell等核心类实现了对xlsx格式联系人表格的读取,兼容字符串、数字等不同单元格类型的解析逻辑,精准提取姓名、邮箱、公司等收件人信息,为批量发送提供数据基础。 - JavaMail:是邮件发送功能的核心依赖,我基于JavaMail的
Session、MimeMessage、Transport等类封装了邮件发送逻辑,配置SMTP服务器参数(如QQ邮箱的smtp.qq.com、465端口),结合身份认证实现邮件的精准投递,并通过多线程+重试机制提升发送稳定性。 - FreeMarker:用于实现邮件内容的个性化渲染,我将固定邮件模板(email.ftl)与Excel读取的联系人数据结合,通过模板变量(如${name}、${company})动态替换内容,无需硬编码即可生成不同收件人的个性化邮件,大幅提升模板复用性和内容灵活性。
- Maven:作为项目依赖管理工具,我通过pom.xml统一引入POI、JavaMail、FreeMarker等依赖包,自动管理各组件的版本和依赖关系,避免手动导入jar包的冲突问题,保障项目构建和运行的一致性,同时简化了项目的部署和协作流程。
二、项目架构
- 配置初始化:设置邮件服务器参数及发件人信息
- 数据导入:从Excel读取收件人列表
- 模板渲染:结合联系人信息生成个性化邮件内容
- 批量发送:多线程并发发送邮件,带重试机制
- 结果反馈:生成发送报告,统计成功/失败情况
三、项目展示
1.目录结构

2. 主程序(MailBulkSender)
- 串联各个模块,完成邮件群发的完整流程
package com;
// 省略import
public class MailBulkSender {
public static void main(String[] args) {
try {
// 1. 配置邮件参数
MailConfig mailConfig = new MailConfig();
mailConfig.setSenderEmail("3407645223@qq.com");
mailConfig.setSenderPassword("lblrwihhllzjdbed");
mailConfig.setSmtpHost("smtp.qq.com");
mailConfig.setSmtpPort(465);
mailConfig.setSslEnable(true);
// 2. 读取Excel联系人
ExcelReader excelReader = new ExcelReader();
List<Contact> contacts = excelReader.readContacts("D:\\homework\\Java/contacts.xlsx");
System.out.println("读取到联系人数量:" + contacts.size());
// 3. 初始化发送器并批量发送
MailSender mailSender = new MailSender(mailConfig);
String subjectTemplate = "【${company}】尊敬的${name},这是一封个性化测试邮件";
List<String> attachments = null;
List<SendLog> logs = mailSender.batchSend(contacts, "email.ftl", subjectTemplate, attachments);
// 4. 生成发送报告
mailSender.generateReport(logs, "D:\\homework\\Java/send-report.txt");
} catch (Exception e) {
e.printStackTrace();
System.err.println("群发失败:" + e.getMessage());
}
}
}
3. 核心业务模块关键逻辑
(1)邮件发送核心(MailSender - 批量发送 + 重试)
package com.bulk.sender;
// 省略import及基础属性定义
public class MailSender {
// 核心批量发送逻辑(保留核心流程)
public List<SendLog> batchSend(List<Contact> contacts, String templateName,
String subjectTemplate, List<String> attachmentPaths) {
List<SendLog> logs = new CopyOnWriteArrayList<>();
ExecutorService executor = Executors.newFixedThreadPool(10); // 线程池控制并发
for (Contact contact : contacts) {
SendLog log = new SendLog();
log.setContact(contact);
log.setSendTime(new Date());
logs.add(log);
executor.submit(() -> {
int retryCount = 0;
boolean sendSuccess = false;
// 失败重试核心逻辑
while (retryCount < 3 && !sendSuccess) {
try {
// 模板变量渲染
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("name", contact.getName());
dataModel.put("company", contact.getCompany());
String subject = renderSimpleTemplate(subjectTemplate, dataModel);
String content = new TemplateRenderer().render(templateName, dataModel);
// 发送单封邮件
sendSingleMail(contact.getEmail(), subject, content, attachmentPaths);
sendSuccess = true;
} catch (Exception e) {
retryCount++;
// 重试间隔(指数退避)
try { Thread.sleep(1000 * retryCount); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
}
}
// 更新发送日志
log.setSuccess(sendSuccess);
log.setRetryCount(retryCount);
});
}
executor.shutdown();
return logs;
}
// 发送报告生成核心
public void generateReport(List<SendLog> logs, String reportPath) {
long successCount = logs.stream().filter(SendLog::isSuccess).count();
long failCount = logs.size() - successCount;
// 控制台报告+文件写入核心
logger.info("===== 邮件发送报告 =====");
logger.info("总发送数:{} | 成功数:{} | 失败数:{}", logs.size(), successCount, failCount);
// 省略文件写入细节(保留核心统计逻辑)
}
// 省略其他辅助方法
}
(2)Excel 读取核心(ExcelReader - 数据解析)
package com.bulk.reader;
// 省略import
public class ExcelReader {
// 核心Excel解析逻辑
public List<Contact> readContacts(String excelPath) throws Exception {
List<Contact> contacts = new ArrayList<>();
Workbook workbook = WorkbookFactory.create(new FileInputStream(new File(excelPath)));
Sheet sheet = workbook.getSheetAt(0);
// 跳过表头读取数据
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
Contact contact = new Contact();
contact.setName(getCellValue(row.getCell(0))); // 姓名
contact.setEmail(getCellValue(row.getCell(1))); // 邮箱
contact.setCompany(getCellValue(row.getCell(2))); // 公司
if (contact.getEmail() != null && !contact.getEmail().isEmpty()) {
contacts.add(contact);
}
}
workbook.close();
return contacts;
}
// 单元格值统一处理核心
private String getCellValue(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING: return cell.getStringCellValue().trim();
case NUMERIC: return String.valueOf(cell.getNumericCellValue()).trim();
default: return "";
}
}
}
(3)模板渲染核心(TemplateRenderer)
package com.bulk.renderer;
// 省略import
public class TemplateRenderer {
// 核心模板渲染逻辑
public String render(String templateName, Map<String, Object> dataModel) throws Exception {
Configuration freemarkerConfig = new Configuration(Configuration.VERSION_2_3_32);
freemarkerConfig.setClassForTemplateLoading(this.getClass(), "/templates");
Template template = freemarkerConfig.getTemplate(templateName);
StringWriter writer = new StringWriter();
template.process(dataModel, writer); // 核心模板渲染
return writer.toString();
}
}
4.实体类(简化展示)
// Contact实体
package com.bulk.entity;
public class Contact {
private String name; // 姓名
private String email; // 邮箱
private String company; // 公司
// 省略Getter/Setter
}
// SendLog实体
package com.bulk.entity;
public class SendLog {
private Contact contact; // 收件人
private boolean success; // 是否发送成功
private int retryCount; // 重试次数
// 省略其他字段及Getter/Setter
}
四、运行效果
1.实际接收邮件

2.单次邮件发送报告

3.发送日志

五、学习心得
做这个邮件群发项目让我们特别有感触,让邮件成功发送出去总会遇到各种问题,在编程过程中,加强了排查问题的能力,还有利用AI引入FreeMarker模板引擎,不用硬编码写邮件内容,改改变量就能给不同人发个性化邮件,省了超多事。
六、总结
本项目实现了邮件群发的基本功能,后续考虑添加以下功能并作为课设:
- 图形化界面,简化操作流程
- 邮件接收功能
- 邮件内容在线编辑功能
- 其他(暂时没想到)
浙公网安备 33010602011771号