29. 电子邮件

29.1 介绍

依赖库:使用Spring框架的邮件功能需要将JavaMail的Jar包添加到依赖中。这个库可以Maven中心找到:com.sun.mail:javax.mail

Spring提供了一个实用的发送电子邮件库,它为使用者屏蔽了邮件系统的底层细节和客户端的底层资源处理。

Spring邮件相关功能在org.springframework.mail包下,其中MailSender是发送邮件的核心接口;SimpleMailMessage类是对邮件属性(发件人、收件人以等)进行简单的封装。这个包中也包含一系列的检查异常,它们是对邮件系统低级别的异常进行抽象,且均继承自MailException。有关异常的更多信息,请参阅相关javadoc。

org.springframework.mail.javamail.JavaMailSender接口继承自MailSender接口,并增加了一些特有的JavaMail功能,如MIME邮件的支持。JavaMailSender还提供了一个用于编写MIME消息的回调org.springframework.mail.javamail.MimeMessagePreparator接口。

29.2 使用

我们假设有一个OrderManager业务接口:

  1. public interface OrderManager {
  2. void placeOrder(Order order);
  3. }

假设我们需要生成一个有订单号的邮件,并发送给相关的客户。

29.2.1MailSenderSimpleMailMessage的基本用法

  1. import org.springframework.mail.MailException;
  2. import org.springframework.mail.MailSender;
  3. import org.springframework.mail.SimpleMailMessage;
  4. public class SimpleOrderManager implements OrderManager {
  5. private MailSender mailSender;
  6. private SimpleMailMessage templateMessage;
  7. public void setMailSender(MailSender mailSender) {
  8. this.mailSender = mailSender;
  9. }
  10. public void setTemplateMessage(SimpleMailMessage templateMessage) {
  11. this.templateMessage = templateMessage;
  12. }
  13. public void placeOrder(Order order) {
  14. // Do the business calculations...
  15. // Call the collaborators to persist the order...
  16. // Create a thread safe "copy" of the template message and customize it
  17. SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
  18. msg.setTo(order.getCustomer().getEmailAddress());
  19. msg.setText(
  20. "Dear " + order.getCustomer().getFirstName()
  21. + order.getCustomer().getLastName()
  22. + ", thank you for placing order. Your order number is "
  23. + order.getOrderNumber());
  24. try{
  25. this.mailSender.send(msg);
  26. }
  27. catch (MailException ex) {
  28. // simply log it and go on...
  29. System.err.println(ex.getMessage());
  30. }
  31. }
  32. }

在xml中添加相关的Bean定义:

  1. <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  2. <property name="host" value="mail.mycompany.com"/>
  3. </bean>
  4. <!-- this is a template message that we can pre-load with default state -->
  5. <bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
  6. <property name="from" value="customerservice@mycompany.com"/>
  7. <property name="subject" value="Your order"/>
  8. </bean>
  9. <bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
  10. <property name="mailSender" ref="mailSender"/>
  11. <property name="templateMessage" ref="templateMessage"/>
  12. </bean>

29.2.2 使用JavaMailSenderMimeMessagePreparator

下面的例子是OrderManager接口的另一种实现,其中使用了MimeMessagePreparator类。 在这里,mailSenderJavaMailSender类型,因此我们可以使用JavaMail MimeMessage类:

  1. import javax.mail.Message;
  2. import javax.mail.MessagingException;
  3. import javax.mail.internet.InternetAddress;
  4. import javax.mail.internet.MimeMessage;
  5. import javax.mail.internet.MimeMessage;
  6. import org.springframework.mail.MailException;
  7. import org.springframework.mail.javamail.JavaMailSender;
  8. import org.springframework.mail.javamail.MimeMessagePreparator;
  9. public class SimpleOrderManager implements OrderManager {
  10. private JavaMailSender mailSender;
  11. public void setMailSender(JavaMailSender mailSender) {
  12. this.mailSender = mailSender;
  13. }
  14. public void placeOrder(final Order order) {
  15. // Do the business calculations...
  16. // Call the collaborators to persist the order...
  17. MimeMessagePreparator preparator = new MimeMessagePreparator() {
  18. public void prepare(MimeMessage mimeMessage) throws Exception {
  19. mimeMessage.setRecipient(Message.RecipientType.TO,
  20. new InternetAddress(order.getCustomer().getEmailAddress()));
  21. mimeMessage.setFrom(new InternetAddress(&amp;amp;quot;mail@mycompany.com&amp;amp;quot;));
  22. mimeMessage.setText(
  23. &amp;amp;quot;Dear &amp;amp;quot; + order.getCustomer().getFirstName() + &amp;amp;quot; &amp;amp;quot;
  24. + order.getCustomer().getLastName()
  25. + &amp;amp;quot;, thank you for placing order. Your order number is &amp;amp;quot;
  26. + order.getOrderNumber());
  27. }
  28. };
  29. try {
  30. this.mailSender.send(preparator);
  31. }
  32. catch (MailException ex) {
  33. // simply log it and go on...
  34. System.err.println(ex.getMessage());
  35. }
  36. }
  37. }

上面中邮件代码只是作为示例,最好方式是将邮件发送代码重构到其它Bean中,并在OrderManager合适的地方调用它。

Spring框架邮件也支持标准的JavaMail实现。 了解更多信息,请参阅相关的javadocs。

29.2 使用MimeMessageHelper

org.springframework.mail.javamail.MimeMessageHelper是一个处理JavaMail消息的好工具,它屏蔽了很多JavaMail API的细节,所以使用MimeMessageHelper可以很简便的创建一个MimeMessage

  1. // of course you would use DI in any real-world cases
  2. JavaMailSenderImpl sender = new JavaMailSenderImpl();
  3. sender.setHost(&amp;amp;quot;mail.host.com&amp;amp;quot;);
  4. MimeMessage message = sender.createMimeMessage();
  5. MimeMessageHelper helper = new MimeMessageHelper(message);
  6. helper.setTo(&amp;amp;quot;test@host.com&amp;amp;quot;);
  7. helper.setText(&amp;amp;quot;Thank you for ordering!&amp;amp;quot;);
  8. sender.send(message);

29.3.1 附件和嵌入资源

邮件允许添加附件和内联资源。嵌入资源是你嵌入到邮件中的图片或样式,但又不希望显示为附件。

附件

下面的例子将展示如何使用MimeMessageHelper发送一个带JPEG图片附件的邮件:

  1. JavaMailSenderImpl sender = new JavaMailSenderImpl();
  2. sender.setHost(&amp;amp;quot;mail.host.com&amp;amp;quot;);
  3. MimeMessage message = sender.createMimeMessage();
  4. // use the true flag to indicate you need a multipart message
  5. MimeMessageHelper helper = new MimeMessageHelper(message, true);
  6. helper.setTo(&amp;amp;quot;test@host.com&amp;amp;quot;);
  7. helper.setText(&amp;amp;quot;Check out this image!&amp;amp;quot;);
  8. // let's attach the infamous windows Sample file (this time copied to c:/)
  9. FileSystemResource file = new FileSystemResource(new File(&amp;amp;quot;c:/Sample.jpg&amp;amp;quot;));
  10. helper.addAttachment(&amp;amp;quot;CoolImage.jpg&amp;amp;quot;, file);
  11. sender.send(message);

嵌入资源

下面的例子将展示如何使用MimeMessageHelper发送一个嵌入图片的邮件:

  1. JavaMailSenderImpl sender = new JavaMailSenderImpl();
  2. sender.setHost(&amp;amp;quot;mail.host.com&amp;amp;quot;);
  3. MimeMessage message = sender.createMimeMessage();
  4. // use the true flag to indicate you need a multipart message
  5. MimeMessageHelper helper = new MimeMessageHelper(message, true);
  6. helper.setTo(&amp;amp;quot;test@host.com&amp;amp;quot;);
  7. // use the true flag to indicate the text included is HTML
  8. helper.setText(&amp;amp;quot;&amp;amp;lt;html&amp;amp;gt;&amp;amp;lt;body&amp;amp;gt;&amp;amp;lt;img src='cid:identifier1234'&amp;amp;gt;&amp;amp;lt;/body&amp;amp;gt;&amp;amp;lt;/html&amp;amp;gt;&amp;amp;quot;, true);
  9. // let's include the infamous windows Sample file (this time copied to c:/)
  10. FileSystemResource res = new FileSystemResource(new File(&amp;amp;quot;c:/Sample.jpg&amp;amp;quot;));
  11. helper.addInline(&amp;amp;quot;identifier1234&amp;amp;quot;, res);
  12. sender.send(message);

嵌入资源需要使用Content-ID(上面的例子identifier1234)添加到MIME消息中。文本和嵌入资源添加是有顺序的,需要按照先添加文本,再添加嵌入资源的顺序。否则,它将不会工作!

29.3.2 使用模板库创建电子邮件内容

在前面的例子中,我们通常使用message.setText(..)等方法创建邮件内容。在简单的情况下,像前面例子那样使用API就可以满足我们的需要了。

在典型的企业应用程序中,下面的原因让你不定会使用上面的方法创建你的邮件内容。

  • 在Java代码中创建HTML的电子邮件内容冗长,且容易出错
  • 呈现逻辑和业务逻辑混杂
  • 更改电子邮件内容的展示结构需要编写Java代码,重新编译,重新部署…

通常解决方法是使用模板框架定义电子邮件的呈现逻辑,如FreeMarker。分离呈现逻辑和业务逻辑使得你的代码更清晰。当你的邮件的内容变的复杂时,这绝对是一个最佳实践,而且Spring框架对FreeMarker有很好的支持。