核心在于模板渲染的可追踪、可扩展、可复用:通过callable多线程并行处理每封邮件,反射动态加载实现mailtemplate接口的渲染器,按lang和业务类型匹配模板id,返回renderresult聚合结果并捕获异常。

核心不在“一键”,而在于让模板渲染过程可追踪、可扩展、可复用。有返回值的多线程负责并行处理每封邮件的数据+模板组合,反射机制则用来动态加载模板类、调用渲染方法、注入变量,避免硬编码模板路径或字段名。
模板渲染需解耦数据与结构
静态写死模板(如直接拼接"亲爱的" + name + ",您的订单已发货")无法应对不同业务线、不同语言、不同客户等级的差异化内容。真正有效的批量渲染,必须把模板逻辑抽离成独立单元——比如 FreeMarker 的 .ftl 文件、或 Java 中的 TemplateRenderer 接口实现类。每个模板对应一个唯一标识(如 order_shipped_zh),运行时通过反射按需加载,而非编译期绑定。
用带返回值的线程池驱动单封邮件渲染
不要用 Runnable,改用 Callable<renderresult></renderresult>。每个线程处理一条记录(如 Excel 中的一行),返回包含渲染后 HTML 内容、收件人邮箱、状态码、错误信息的 RenderResult 对象。示例关键逻辑:
- 从 Excel 读出一行:{name=张三, email=zhangsan@xx.com, order_id=ORD20260526001, lang=zh}
- 根据
lang和业务类型自动匹配模板 ID:email_order_shipped_${lang} - 用反射获取模板渲染器:
Class.forName("cn.example.render.ZhOrderShippedRenderer").getDeclaredConstructor().newInstance() - 反射调用其
render(Map data)方法,传入该行数据 - 线程返回
new RenderResult(email, htmlContent, true, null)
反射加载模板类的关键控制点
不是所有类都适合反射调用,需约定规范:
- 所有模板渲染器必须实现统一接口(如
MailTemplate),含getId()和render(Map<string object>)</string> - 类名需含语义前缀(如
ZhWelcomeRenderer、EnResetPwdRenderer),便于按规则扫描 - 构造器必须无参,或只接受配置对象(避免反射时传参失败)
- 在 Spring 环境中,可用
@Component("template.zh.welcome")注解 +ApplicationContext.getBean("template.zh.welcome")替代原始反射,更安全可控
结果聚合与异常穿透不能丢
多线程返回值必须被收集,否则就失去“可追踪”意义:
- 用
ExecutorService.invokeAll(tasks)获取全部Future<renderresult></renderresult> - 遍历每个
future.get(),捕获ExecutionException,提取原始异常(如模板语法错、空指针)并记录到明细日志 - 失败项不中断整体流程,但需标记为
RENDER_FAIL,后续发送阶段跳过或降级为默认模板 - 最终生成渲染统计报告:成功数 / 失败数 / 平均耗时 / 最慢模板










