论坛首页 Java版

使用模板语言(Velocity)定义、发送邮件

浏览 4262 次
该帖已经被评为良好帖
作者 正文
最后更新时间:2007-01-23 关键字: Mail, JavaMail, Velocity, Spring

发送邮件时,比如激活邮件、订单邮件等等往往都需要动态数据的填入,比如客户名、订单号,或者密码。类似于web设计:

邮件的模板:是一个可能具有html格式或类似的模板,是View的角色;
邮件的数据:是Model的角色,填入模板,形成最后的邮件内容。

一个与用户交互加多的系统,邮件的种类是“啥都有的”:各种提醒,各种状态告知,。。。
所以基于配置做法相对比较能够统一模式以及省时省力。

我的做法是使用Velocity定义邮件,通过提供良好设计的API,和配置规定实现。最后使用代码大致如下:
 
 /**
  * 激活服务加密解密功能支持
  */
 private Encryptor encryptor;
 
 /**
  * 激活邮件功能支持
  */
 private VelocityMailSupport velocityMailSupport;

      public void xxxx(User user) { 
              Map model = new HashMap();
              model.put("user", user);
              model.put("token", encryptor.encode(user));
              velocityMailSupport.sendMime(user.getRealname(),  user.getEmail(),  model);
 }

velocityMailSupport是一个包含了模板和邮件标题信息的JavaMailSender实现类,sendMime是提供的简易方法,发送具有html样式的代码。
同时,velocityMailSupport本身使用的内嵌的JavaMailSender是自己实现的PooledMailSender(异步邮件发送)。经过实战使用效果良好。

   
最后更新时间:2007-01-23
附件是核心代码(不是上面的示例代码)和配置:

VelocityMailSupport.java -- 提供给程序使用,通过Spring配置注入到被需要的类中,利用本类可方便发送由Velocity模板生成的邮件内容。客户程序可以组合或继承本类,使具有发送邮件功能。

PoolMailSender.java --对Springframework MailSender和JavaMailSender的装饰, 实现装饰器模式。PooledMailSender将要发送邮件进行排队,按FIFO方式发送得到的邮件消息。

applicationContext-mail-template.xml -- 配置所有的邮件模板的VelocityMailSupport实例,比如激活,找回密码等具体velocity模板和邮件subject

applicationContext-mail.xml -- 基础配置applicationContext-mail-template.xml依赖于它

mail_template.properties -- 配置模板位置和subject文本
   
0 请登录后投票
最后更新时间:2007-01-23
VelocityMailSupport.java
public class VelocityMailSupport implements MailSender, JavaMailSender{

	// -----------------------------------------------------------------------------
	
	private Properties mailHeaders = new Properties();

	/**
	 * 邮件发送者,包括发送者姓名和地址,用于设置在邮件的from栏目中
	 */
	private String from;

	/**
	 * 邮件主题
	 */
	private String subject;

	/**
	 * 邮件内容模板地址/名称
	 */
	private String templateName;

	/**
	 * velocity引擎
	 */
	private VelocityEngine velocityEngine;

	/**
	 * mail发送器
	 */
	private MailSender mailSender;

	// -----------------------------------------------------------------------------
	
	/**
	 * 日志
	 */
	protected static final Log log = LogFactory
			.getLog(VelocityMailSupport.class);
	
	// -----------------------------------------------------------------------------

	/**
	 * 使用提供的数据,套入velocity模板,生成最后文本信息。
	 * 
	 * 大部分情况下,这个信息应该就是邮件的内容。
	 */
	public String renderText(Map model) throws VelocityException {
		return VelocityEngineUtils.mergeTemplateIntoString(getVelocityEngine(),
				getTemplateName(), model);
	}
	

	// -----------------------------------------------------------------------------
	//省略getter和setter
	/**
	 * 
	 * 发送Mime邮件简易方法。
	 * 
	 * 以Mime的方式发送邮件,这主要是为能够支持发送html或类似功能(非简单文本)的邮件
	 * 
	 * @param nameOfTo 邮件接收收人姓名 或 昵称
	 * @param emailOfTo 邮件接收人邮件地址
	 * @param model 邮件velocity模板中变量的值
	 * @throws MailException
	 */
	public void sendMime(String nameOfTo, String emailOfTo, Map<String, Object> model) throws MailException {
		sendMime(nameOfTo + "<" + emailOfTo + ">", model);
	}

	
	/**
	 *  发送Mime邮件简易方法。
	 * 
	 * 以Mime的方式发送邮件,这主要是为能够支持发送html或类似功能(非简单文本)的邮件
	 * 
	 * @param to 邮件接收人邮件地址以及可能的收件人姓名或昵称
	 * @param model 邮件velocity模板中变量的值
	 */
	public void sendMime(String to, Map<String, Object> model) throws MailException {
		sendMime(mergeSimpleMessage(to, model));
	}
	

	/**
	 * 发送Mime邮件简易方法。
	 * 
	 * 以Mime的方式发送SimpleMailMessage,这主要是为能够支持发送html或类似功能(非简单文本)的邮件
	 * 
	 * @param simpleMessage
	 * @throws MailException
	 */
	public void sendMime(SimpleMailMessage simpleMessage) throws MailException {
		send(toMimeMessage(simpleMessage));
	}
	

	//----------------------------------------------------------------------------------
	
	public SimpleMailMessage mergeSimpleMessage(String to, Map<String, Object> model) {
		//render text of mail from velocity template with the data
		String text = null;
		try {
			text = renderText(model);
		} catch (VelocityException e) {
			log.error(e);
			e.printStackTrace();
		}
		
		//mail message setting
		SimpleMailMessage message = new SimpleMailMessage();
		message.setSubject(getSubject());
		message.setFrom(getFrom());
		message.setTo(to);
		message.setText(text);
		return message;
	}
	
	/**
	 * 
	 * @param simpleMailMessage
	 * @return
	 */
	public MimeMessage toMimeMessage(SimpleMailMessage simpleMailMessage) {
		MimeMessage mimeMessage = createMimeMessage();	
		MimeMailMessage mmm = new MimeMailMessage(mimeMessage);
		simpleMailMessage.copyTo(mmm);
		return mmm.getMimeMessage();
			
	}

	//----------------------------------------------------------------------------------
	// MailSender接口实现 - 代理到配置的实际mailSender --仅发布一个代理方法,其它省略
	public void send(SimpleMailMessage simpleMessage) throws MailException {
		getMailSender().send(simpleMessage);
	}


	//----------------------------------------------------------------------------------

	
	protected void injectMailHeader(MimeMessage mm){
		for (Object name : mailHeaders.keySet()) {
			try {
				mm.setHeader((String)name, mailHeaders.getProperty((String)name));
			} catch (MessagingException e) {
				e.printStackTrace();
			}
		}
		
	}

}//end
   
0 请登录后投票
最后更新时间:2007-01-23
PooledMailSender.java

public class PooledMailSender implements MailSender, JavaMailSender,
		Runnable {

	// -------------------------------------------------------------------------

	/**
	 * 实际发送邮件的邮件发送器,可以是MailSender,或JavaMailSender
	 */
	private MailSender mailSender;
	
	// -------------------------------------------------------------------------
	
	/**
	 * 日志
	 */
	protected static final Log log = LogFactory
			.getLog(PooledMailSender.class);

	
	/**
	 * 邮件排队点号、发送线程,
	 */
	private Thread thread;

	/**
	 * 邮件排队队列
	 */
	private Queue<Object> queue = new LinkedBlockingQueue<Object>();
	
	/**
	 * 锁,仅此而已
	 *  
	 * @see #run()
	 * @see #add(Object)
	 */
	private Object mutex = new Object();

	// -------------------------------------------------------------------------

	/**
	 * 构造本类对象,同时启动侦听邮件的到达。
	 * 
	 * 如果要阻止侦听和发送,应该调用close方法,在Spring的Context中"最好"配置destroy-method="close",不过这不是必须的。
	 * 
	 */	
	public PooledMailSender() {
		thread = new Thread(this);
		thread.setDaemon(true);
		thread.start();
	}

    // -------------------------------------------------------------------------
    
	/**
	 * 邮件发送循环,它被作为thread runnable的run实现。
	 * 
	 * @see #add(Object)
	 * 
	 */
	public void run() {
		while (!isClose()) {
			if (!isEmpty()) {
				Object object = poll();
				try {
					//执行实际发送
					doSend(object);
				} catch (Exception ex) {
					log.error(ex);
					ex.printStackTrace();
				}
			}
			//等~直到add方法的通知!
			synchronized (mutex) {
				try {
					mutex.wait();
				} catch (InterruptedException e) {
				}
			}
		}
	}

	// -------------------------------------------------------------------------

	public MailSender getMailSender() {
		return mailSender;
	}

	/**
	 * 设置实际的邮件发送器
	 * 
	 * @param mailSender
	 */
	public void setMailSender(MailSender mailSender) {
		this.mailSender = mailSender;
	}


	// -------------------------------------------------------------------------

	/**
	 * 关闭PooledMailSender!
	 * 
	 * 关闭的PooledMailSender不可再接收和发送邮件
	 */
	public void close() {
		queue.clear();
		queue = null;
	}

	/**
	 * 已经关闭?
	 * 
	 * 关闭的PooledMailSender不可再接收和发送邮件
	 * 
	 * @return
	 */
	public boolean isClose() {
		return queue == null;
	}

	// -------------------------------------------------------------------------
	// 邮件排队机操作方法代理

	protected boolean isEmpty() {
		return queue != null && queue.isEmpty();
	}
	
	/**
	 * 邮件加入排队机
	 * 
	 * @param obj
	 * @see #run()
	 */
	protected void add(Object obj) {
		Assert.notNull(queue);
		queue.add(obj);
		//我来了!
		synchronized (mutex) {
			mutex.notify();
		}
	}

	protected Object poll() {
		Assert.notNull(queue);
		return queue.poll();
	}

	protected Object peek() {
		Assert.notNull(queue);
		return queue.peek();
	}



	// -------------------------------------------------------------------------
	// MailSender接口实现-将邮件放入排队机
	
	public void send(SimpleMailMessage simpleMessage) throws MailException {
		add(simpleMessage);
	}

	public void send(SimpleMailMessage[] simpleMessages) throws MailException {
		for (SimpleMailMessage message : simpleMessages) {
			add(message);
		}
	}
	
	
	// -------------------------------------------------------------------------
	// JavaMailSender接口实现-将邮件放入排队机
	
	public void send(MimeMessage mimeMessage) throws MailException {
		add(mimeMessage);
	}

	public void send(MimeMessage[] mimeMessages) throws MailException {
		for (MimeMessage message : mimeMessages) {
			add(message);
		}
	}

	public void send(MimeMessagePreparator mimeMessagePreparator)
			throws MailException {
		add(mimeMessagePreparator);
	}

	public void send(MimeMessagePreparator[] mimeMessagePreparators)
			throws MailException {
		for (MimeMessagePreparator preparator : mimeMessagePreparators) {
			add(preparator);
		}
	}

	public MimeMessage createMimeMessage() {
		return ((JavaMailSender) mailSender).createMimeMessage();
	}

	public MimeMessage createMimeMessage(InputStream contentStream)
			throws MailException {
		return ((JavaMailSender) mailSender).createMimeMessage(contentStream);
	}

	// -------------------------------------------------------------------------
    // 实际发送代理方法
	
	public void doSend(Object object) {
        if (object instanceof SimpleMailMessage) {
            doSend((SimpleMailMessage) object);
        } else if (object instanceof MimeMessage) {
            doSend((MimeMessage) object);
        } else if (object instanceof MimeMessagePreparator) {
            doSend((MimeMessagePreparator) object);
        }
    }

	public void doSend(SimpleMailMessage simpleMessage) throws MailException {
		mailSender.send(simpleMessage);
	}
	
	public void doSend(MimeMessage mimeMessage) throws MailException {
		((JavaMailSender) mailSender).send(mimeMessage);
	}

	public void doSend(MimeMessagePreparator mimeMessagePreparator)
			throws MailException {
		((JavaMailSender) mailSender).send(mimeMessagePreparator);
	}


}

   
0 请登录后投票
最后更新时间:2007-03-14
终于让我找到"使用模板语言(Velocity)定义、发送邮件"方面的资料了,谢谢.
   
0 请登录后投票
最后更新时间:2007-05-04
我想问问,我也是用模板来发送信件的,但是其中发现有一个乱码问题:具体情况为
model.put("country","中国")
model.put("city","哈尔滨")
模板为自定义的,但是模板当中还有其它静态的中文信息
text=VelocityEngineUtils.mergeTemplateIntoString("xxx.html", model); 
然后我把这个text保存在数据库当中,然后在数据库中我发现模板的中文信息和model中的中文信息的编码不一致,所以在收到的信中model中的内容显示为乱码,但是标题显示中文正常。
我觉得原因是不是经过模板的上面那个方法导致中文信息的编码不一致,请帮忙!
   
0 请登录后投票
最后更新时间:2007-05-05
myqhit 写道
我想问问,我也是用模板来发送信件的,但是其中发现有一个乱码问题:具体情况为
model.put("country","中国")
model.put("city","哈尔滨")
模板为自定义的,但是模板当中还有其它静态的中文信息
text=VelocityEngineUtils.mergeTemplateIntoString("xxx.html", model); 
然后我把这个text保存在数据库当中,然后在数据库中我发现模板的中文信息和model中的中文信息的编码不一致,所以在收到的信中model中的内容显示为乱码,但是标题显示中文正常。
我觉得原因是不是经过模板的上面那个方法导致中文信息的编码不一致,请帮忙!
在velocity.properties中设置输入输出编码,例如
input.encoding=GBK
output.encoding=GBK
default.contentType=text/html; charset=GBK
   
0 请登录后投票
最后更新时间:2007-05-05
问题已经解决,谢谢!
不过我没有用上面写的配置,还不知道这个配置往哪里加
不过我通过调用String text = VelocityEngineUtils.mergeTemplateIntoString(
velocityEngine, template, "UTF-8", model);

然后再通过发信的时候设置 MimeMessageHelper help = new MimeMessageHelper(message, true, "UTF-8");
就可以了.
这下从数据库读出的时候,与模板融合就可以统一编码了.
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐