基于Freemarker模板技术的邮件发送模块设计
来源:互联网 发布:孤岛惊魂4优化太垃圾 编辑:程序博客网 时间:2024/05/22 00:51
2.需求分析
关键点有
2.1邮件内容的存放
a)直接把邮件内容写死在代码里,然后拼接成一个很长的字符串,缺点也很明显,要改邮件的内容必修修改代码,重新编译打包
b)邮件内容与代码相分离.将邮件的内容文件化,java代码中只是引用模板的位置,然后解析模块中的内容输出,这种方案有着更高的可维护性,扩展起来也更方便
2.2发送邮件的效率
发邮件是一件很耗费性能的操作,如果系统中会频繁用到邮件发送,邮件发送不要影响正常的业务操作
2.3自动记录错误和重发
邮件发送失败时,出错的邮件要保存起来,以便日后重发
3.关键技术点
3.1.email发送可以通过javamail api实现
3.2邮件内容模板采用的是freemarker技术来实现
3.3异步发送邮件,采用的是java的多线程机制
4.设计细节
4.1整体类图
4.2类描述
EmailServer:邮件服务器,用来进行邮件服务器的配置和实际的邮件发送,这里调用底层的javamail实现,核心方法
send(EmailInfo emailInfo)这个是个邮件发送的模板方法
EmailSendListener:邮件发送器监听程序,一个observer模式的实现,当有邮件要发送时触发,可以为邮件服务器配置一个或多个监听程序,定义了三个核心接口方法
before(EmailContext emailContext)邮件发送前做的操作
after(EmailContext emailContext)邮件发送结束后做的操作
afterThrowable(EmailContext emailContext)邮件发送出现异常时做的处理
EmailTemplateService:邮件的内容采用了模板技术来实现, 定义一个统一的顶层接口getText,对于不同的模板技术实现Freemarker或Velocity分别实现该方法
EmailSendFacade:邮件发送模块对外暴露的外部接口,用来封装各个底层实现细节
EmailContext:邮件监听器用到的邮件发送上线文信息,主要有EmailInfo邮件基本信息和Throwable两个字段
4.3系统时序图
4.4项目整体目录结构
4.5核心类源码解读
- package com.crazycoder2010.email;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Properties;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import javax.mail.Authenticator;
- import javax.mail.BodyPart;
- import javax.mail.Message;
- import javax.mail.MessagingException;
- import javax.mail.Multipart;
- import javax.mail.PasswordAuthentication;
- import javax.mail.Session;
- import javax.mail.Transport;
- import javax.mail.internet.AddressException;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeBodyPart;
- import javax.mail.internet.MimeMessage;
- import javax.mail.internet.MimeMultipart;
- /**
- * 郵件服務器
- *
- * @author Kevin
- *
- */
- public class EmailServer {
- private static final int POOL_SIZE = 5;
- private Session session;
- private ExecutorService theadPool;
- /**
- * 郵件監聽器
- */
- private List<EmailSendListener> emailSendListeners = new ArrayList<EmailSendListener>();
- public void init() {
- final Properties properties = SysConfig.getConfiguration();
- this.theadPool = Executors.newFixedThreadPool(POOL_SIZE);
- this.session = Session.getDefaultInstance(properties,
- new Authenticator() {
- @Override
- protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication(properties
- .getProperty("mail.smtp.username"), properties
- .getProperty("mail.smtp.password"));
- }
- });
- this.session.setDebug(true);//生产环境把其设置为false
- }
- /**
- * 發送單條email
- *
- * @param emailInfo
- */
- public void send(final EmailInfo emailInfo) {
- this.theadPool.execute(new Runnable() {
- public void run() {
- EmailContext emailContext = new EmailContext();
- emailContext.setEmailInfo(emailInfo);
- doBefore(emailContext);
- try {
- Message msg = buildEmailMessage(emailInfo);
- Transport.send(msg);
- doAfter(emailContext);
- } catch (Exception e) {
- emailContext.setThrowable(e);
- doAfterThrowable(emailContext);
- }
- }
- });
- }
- private Message buildEmailMessage(EmailInfo emailInfo)
- throws AddressException, MessagingException {
- MimeMessage message = new MimeMessage(this.session);
- message.setFrom(convertString2InternetAddress(emailInfo.getFrom()));
- message.setRecipients(Message.RecipientType.TO,
- converStrings2InternetAddresses(emailInfo.getTo()));
- message.setRecipients(Message.RecipientType.CC,
- converStrings2InternetAddresses(emailInfo.getCc()));
- Multipart multipart = new MimeMultipart();
- BodyPart messageBodyPart = new MimeBodyPart();
- messageBodyPart.setContent(emailInfo.getContent(), "text/html;charset=UTF-8");
- multipart.addBodyPart(messageBodyPart);
- message.setContent(multipart);
- message.setSubject(emailInfo.getTitle());
- message.saveChanges();
- return message;
- }
- private InternetAddress convertString2InternetAddress(String address)
- throws AddressException {
- return new InternetAddress(address);
- }
- private InternetAddress[] converStrings2InternetAddresses(String[] addresses)
- throws AddressException {
- final int len = addresses.length;
- InternetAddress[] internetAddresses = new InternetAddress[len];
- for (int i = 0; i < len; i++) {
- internetAddresses[i] = convertString2InternetAddress(addresses[i]);
- }
- return internetAddresses;
- }
- public void addEmailListener(EmailSendListener emailSendListener) {
- this.emailSendListeners.add(emailSendListener);
- }
- /**
- * 發送多條email
- *
- * @param emailInfos
- */
- public void send(List<EmailInfo> emailInfos) {
- for (EmailInfo emailInfo : emailInfos) {
- send(emailInfo);
- }
- }
- private void doBefore(EmailContext emailContext) {
- for (EmailSendListener emailSendListener : this.emailSendListeners) {
- emailSendListener.before(emailContext);
- }
- }
- private void doAfter(EmailContext emailContext) {
- for (EmailSendListener emailSendListener : this.emailSendListeners) {
- emailSendListener.after(emailContext);
- }
- }
- private void doAfterThrowable(EmailContext emailContext) {
- for (EmailSendListener emailSendListener : this.emailSendListeners) {
- emailSendListener.afterThrowable(emailContext);
- }
- }
- }
邮件服务器的配置参数
- mail.transport.protocol=smtp
- mail.smtp.port=25
- mail.smtp.host=smtp.163.com
- mail.smtp.username=chongzi1266
- mail.smtp.password=*********
- mail.smtp.connectiontimeout=10000
- mail.smtp.timeout=10000
- mail.smtp.auth=true
FreemarkerEmalTemplateService
- package com.crazycoder2010.email;
- import java.io.StringWriter;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
- import freemarker.cache.ClassTemplateLoader;
- import freemarker.template.Configuration;
- import freemarker.template.Template;
- /**
- * 基于Freemarker模板技术的邮件模板服务
- * @author Administrator
- *
- */
- public class FreemarkerEmailTemplateService implements EmailTemplateService {
- /**
- * 邮件模板的存放位置
- */
- private static final String TEMPLATE_PATH = "/email/";
- /**
- * 启动模板缓存
- */
- private static final Map<String, Template> TEMPLATE_CACHE = new HashMap<String, Template>();
- /**
- * 模板文件后缀
- */
- private static final String SUFFIX = ".ftl";
- /**
- * 模板引擎配置
- */
- private Configuration configuration;
- public void init(){
- configuration = new Configuration();
- configuration.setTemplateLoader(new ClassTemplateLoader(FreemarkerEmailTemplateService.class, TEMPLATE_PATH));
- configuration.setEncoding(Locale.getDefault(), "UTF-8");
- configuration.setDateFormat("yyyy-MM-dd HH:mm:ss");
- }
- public String getText(String templateId, Map<Object, Object> parameters) {
- String templateFile = templateId + SUFFIX;
- try {
- Template template = TEMPLATE_CACHE.get(templateFile);
- if(template == null){
- template = configuration.getTemplate(templateFile);
- TEMPLATE_CACHE.put(templateFile, template);
- }
- StringWriter stringWriter = new StringWriter();
- template.process(parameters, stringWriter);
- return stringWriter.toString();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
EmailSendFacade
门面模式的应用,封装了EmailServer和EmailTemplateService,对外部封装内部实现细节
- package com.crazycoder2010.email;
- /**
- * 邮件发送门面类,用于客户端直接调用
- * @author Administrator
- *
- */
- public class EmailSendFacade {
- private EmailTemplateService emailTemplateService;
- private EmailServer emailServer;
- public void setEmailTemplateService(EmailTemplateService emailTemplateService) {
- this.emailTemplateService = emailTemplateService;
- }
- public void setEmailServer(EmailServer emailServer) {
- this.emailServer = emailServer;
- }
- /**
- * 发送邮件
- * @param emailInfo 邮件参数封装,emailInfo的title和content字段的值将被重置为实际的值
- */
- public void send(EmailInfo emailInfo){
- String title = emailTemplateService.getText(emailInfo.getTemplateId()+"-title", emailInfo.getParameters());
- String content = emailTemplateService.getText(emailInfo.getTemplateId()+"-body", emailInfo.getParameters());
- emailInfo.setContent(content);
- emailInfo.setTitle(title);
- emailServer.send(emailInfo);
- }
- }
客户端调用(junit)
- package com.crazycoder2010.email;
- import org.junit.Test;
- public class EmailSendFacadeTest {
- @Test
- public void testSend() throws InterruptedException {
- //启动邮件服务器
- EmailServer emailServer = new EmailServer();
- emailServer.init();
- emailServer.addEmailListener(new ConsoleEmailSendListener());
- emailServer.addEmailListener(new DatabaseEmailSendListener());
- //启动模板服务
- EmailTemplateService emailTemplateService = new FreemarkerEmailTemplateService();
- emailTemplateService.init();//模板引擎初始化
- //组装邮件发送门面类
- EmailSendFacade emailSendFacade = new EmailSendFacade();
- emailSendFacade.setEmailServer(emailServer);//注册邮件服务器
- emailSendFacade.setEmailTemplateService(emailTemplateService);//注册模板
- //测试数据
- EmailInfo emailInfo = new EmailInfo();
- emailInfo.setFrom("chongzi1266@163.com");
- //emailInfo.setTo(new String[]{"to_01@localhost","to_02@localhost"});
- //emailInfo.setCc(new String[]{"cc_01@localhost","cc_02@localhost"});
- emailInfo.setTo(new String[]{"wangxuzheng@gmail.com","12708826@qq.com"});
- emailInfo.setCc(new String[]{"kwang2003@msn.com","wangxuzheng1983@hotmail.com"});
- emailInfo.setTemplateId("reset_password");
- emailInfo.addParameter("name", "Kevin");
- emailInfo.addParameter("newPassword", "123456");
- //发送
- emailSendFacade.send(emailInfo);
- Thread.sleep(10000);
- }
- }
总结:
这个模块的设计参考了junit3.8优秀的设计思想,采用observer+template来实现灵活扩展邮件功能的方式,采用了邮件模板技术来实现邮件发送内容多样化,配置化,多线程的引入提高了系统的执行效率
其他:
项目中统一编码为UTF-8,包括工程(文件编码),模板编码,邮件内容编码,否则会出现纠结的中文乱码问题
工程源码下载链接
- 基于Freemarker模板技术的邮件发送模块设计
- 基于Freemarker模板技术的邮件发送模块设计
- 基于Freemarker模板技术的邮件发送模块设计
- 基于JavaMail和freemarker模板的邮件发送java工具包
- 使用Spring发送基于freemarker模板的邮件
- 基于Freemarker模板技术的分页组件设计
- Struts2 下 基于Freemarker模板技术的分页组件设计
- 利用Freemarker模板发送邮件
- 基于maven的邮件发送模块
- SpringBoot使用FreeMarker模板发送邮件
- springboot 普通发送邮件 和 freemarker模板发送邮件
- 【java】javamail+freemarker生成邮件模板,并发送邮件
- 几种基于ASP页面的邮件发送技术大全
- Spring 发送邮件 (3) Spring使用模板Freemarker
- DWR+freemarker+commons.mail 实现模板定制动态邮件发送
- Spring 4 使用Freemarker模板发送邮件&添加附件
- spring使用FreeMarker模板发送邮件及附件笔记
- Velocity 、Freemarker模板及Spring Api实现发送邮件
- 积累——非IT(一)
- (华为)求最大公共字符串长度,大小写部分
- Java构造和解析Json数据的两种方法详解一
- leetcode - Divide Two Integers
- 代理后获取客户端IP地址
- 基于Freemarker模板技术的邮件发送模块设计
- 对imp中的fromuser参数的偏差理解
- git 命令 以及 搭建多人开发环境
- java
- a star算法+list,减少遍历的开销
- 发现一个比SOAP,RPC等更爽的工具hessian 及在PHP中的使用介绍
- Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’解决方法
- HDU1521 排列组合 (指数型母函数)
- php单例模式