分享

JavaMail异步邮件发送

 大明明小珠珠 2015-10-30

      今天把之前写的使用JavaMail异步发送邮件的demo程序贴出来。

 

      最近一段时间,发现新浪微博手机客户端也开始支持异步发送信息了。不管是发微博,还是评论微博,点击过“发送”按钮之后,马上会被告知“已经进入发送队列”,我觉得这明显增加了用户体验,并且这个提升也不存在任何技术困难。这样一种情况,比如我发一个带图的微博消息,在不使用wifi的情况下,上传一个稍大些的图片可能会耗费不少时间。假如微博客户端不支持异步发送,也许就因为图片的上传,这个客户端得卡上好半天,直到上传完成为止。这种完全阻塞的方式,对用户来说可不是种好的体验。 

 

      发送邮件的时候同样存在着类似上面的情况。整个邮件的发送过程是比较耗时的,假如使用普通的单线程串行处理方式,当并发量大时,必然带来灾难性的后果。在下面的例子中,我使用多线程的方式来解决这个问题,使得邮件支持异步发送。

 

      要支持新浪微博的异步发送,可以使用多线程方式,也可以使用消息服务。我本身对于JMS的方式不太了解,因此选择了一种相对熟悉和容易实现的方式,即每个邮件发送请求都作为一个线程任务,由线程池中的线程来处理每一个邮件发送任务。


      首先,介绍邮件的JavaBean对象Mail。很简单,无需赘言。

 

Java代码  收藏代码
  1. package org.tang.financial.domain;  
  2.   
  3. import java.util.List;  
  4.   
  5. public class Mail {  
  6.     /** 
  7.      * 发送人 
  8.      */  
  9.     private String sender;  
  10.     /** 
  11.      * 收件人 
  12.      */  
  13.     private List<String> recipientsTo;  
  14.     /** 
  15.      * 抄送人 
  16.      */  
  17.     private List<String> recipientsCc;  
  18.     /** 
  19.      * 密送人 
  20.      */  
  21.     private List<String> recipientsBcc;  
  22.     /** 
  23.      * 主题 
  24.      */  
  25.     private String subject;  
  26.     /** 
  27.      * 正文 
  28.      */  
  29.     private String body;  
  30.     /** 
  31.      * 附件列表 
  32.      */  
  33.     private List<String> attachments;  
  34.       
  35.       
  36.     public String getSender() {  
  37.         return sender;  
  38.     }  
  39.     public void setSender(String sender) {  
  40.         this.sender = sender;  
  41.     }  
  42.     public List<String> getRecipientsTo() {  
  43.         return recipientsTo;  
  44.     }  
  45.     public void setRecipientsTo(List<String> recipientsTo) {  
  46.         this.recipientsTo = recipientsTo;  
  47.     }  
  48.     public List<String> getRecipientsCc() {  
  49.         return recipientsCc;  
  50.     }  
  51.     public void setRecipientsCc(List<String> recipientsCc) {  
  52.         this.recipientsCc = recipientsCc;  
  53.     }  
  54.     public List<String> getRecipientsBcc() {  
  55.         return recipientsBcc;  
  56.     }  
  57.     public void setRecipientsBcc(List<String> recipientsBcc) {  
  58.         this.recipientsBcc = recipientsBcc;  
  59.     }  
  60.     public String getSubject() {  
  61.         return subject;  
  62.     }  
  63.     public void setSubject(String subject) {  
  64.         this.subject = subject;  
  65.     }  
  66.     public String getBody() {  
  67.         return body;  
  68.     }  
  69.     public void setBody(String body) {  
  70.         this.body = body;  
  71.     }  
  72.     public List<String> getAttachments() {  
  73.         return attachments;  
  74.     }  
  75.     public void setAttachments(List<String> attachments) {  
  76.         this.attachments = attachments;  
  77.     }  
  78.       
  79. }  

 

 

      其次,是邮件发送程序当中需要用到的常量。各个常量的含义都已经有说明,也无需赘言。

 

Java代码  收藏代码
  1. package org.tang.financial.mail;  
  2.   
  3. public abstract class MailProperties {  
  4.     /** 
  5.      * SMTP服务器 
  6.      */  
  7.     public static final String MAIL_SMTP_HOST = "mail.smtp.host";  
  8.     /** 
  9.      * SMTP服务器端口号 
  10.      */  
  11.     public static final String MAIL_SMTP_PORT = "mail.smtp.port";  
  12.     /** 
  13.      * 登录SMTP服务器是否需要通过授权。可选值为true和false 
  14.      */  
  15.     public static final String MAIL_SMTP_AUTH = "mail.smtp.auth";  
  16.     /** 
  17.      * 登录SMTP服务器默认邮箱账号 
  18.      */  
  19.     public static final String MAIL_SMTP_USER = "mail.smtp.user";  
  20.     /** 
  21.      * 登录SMTP服务器默认邮箱账号对应密码 
  22.      */  
  23.     public static final String MAIL_SMTP_PASSWORD = "mail.smtp.password";  
  24.     /** 
  25.      * 是否打开程序调试。可选值包括true和false 
  26.      */  
  27.     public static final String MAIL_DEBUG = "mail.debug";  
  28. }  
 

 

      接着,是邮件发送程序需要使用到得properties属性配置文件。各个键值的含义参考上面的说明。

 

Java代码  收藏代码
  1. mail.smtp.host = smtp.example.com  
  2. mail.smtp.port = 25  
  3. mail.smtp.auth = true  
  4. mail.smtp.user = username@example.com  
  5. mail.smtp.password = password  
  6. mail.debug = true  
 

 

      最后,邮件发送的处理程序。

 

Java代码  收藏代码
  1. package org.tang.financial.service;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.util.Date;  
  6. import java.util.List;  
  7. import java.util.Properties;  
  8. import java.util.concurrent.Executor;  
  9. import java.util.concurrent.Executors;  
  10.   
  11. import javax.mail.Authenticator;  
  12. import javax.mail.Message;  
  13. import javax.mail.MessagingException;  
  14. import javax.mail.Multipart;  
  15. import javax.mail.PasswordAuthentication;  
  16. import javax.mail.Session;  
  17. import javax.mail.Transport;  
  18. import javax.mail.internet.AddressException;  
  19. import javax.mail.internet.InternetAddress;  
  20. import javax.mail.internet.MimeBodyPart;  
  21. import javax.mail.internet.MimeMessage;  
  22. import javax.mail.internet.MimeMultipart;  
  23.   
  24. import org.apache.commons.logging.Log;  
  25. import org.apache.commons.logging.LogFactory;  
  26. import org.springframework.stereotype.Component;  
  27. import org.tang.financial.domain.Mail;  
  28. import org.tang.financial.mail.MailProperties;  
  29. import org.tang.financial.util.CollectionUtils;  
  30. import org.tang.financial.util.StringUtils;  
  31.   
  32. @Component  
  33. public class MailService {  
  34.     private static Log logger = LogFactory.getLog(MailService.class);  
  35.     private static final String MAIL_PROPERTIE_NAME = "JavaMail.properties";  
  36.     private static Properties mailPro = new Properties();  
  37.     private static Executor executor = Executors.newFixedThreadPool(10);  
  38.   
  39.     static {  
  40.         //初始化,读取属性文件的过程  
  41.         InputStream in = null;  
  42.         try {  
  43.             in = MailService.class.getResourceAsStream(MAIL_PROPERTIE_NAME);  
  44.             mailPro.load(in);  
  45.         } catch (IOException e) {  
  46.             if (logger.isErrorEnabled()) {  
  47.                 logger.error(e);  
  48.             }  
  49.         } finally {  
  50.             if (in != null) {  
  51.                 try {  
  52.                     in.close();  
  53.                 } catch (IOException e) {  
  54.                     if (logger.isErrorEnabled()) {  
  55.                         logger.error(e);  
  56.                     }  
  57.                 }  
  58.             }  
  59.         }  
  60.   
  61.     }  
  62.   
  63.     public boolean sendMail(final Mail mail) {  
  64.         if(mail == null){  
  65.             return false;  
  66.         }  
  67.         //创建邮件发送任务  
  68.         Runnable task = new Runnable() {  
  69.             @Override  
  70.             public void run() {  
  71.                 final String username = mailPro.getProperty(MailProperties.MAIL_SMTP_USER);  
  72.                 final String password = mailPro.getProperty(MailProperties.MAIL_SMTP_PASSWORD);  
  73.                 //创建发送邮件的会话  
  74.                 Session session = Session.getDefaultInstance(mailPro, new Authenticator() {  
  75.                             protected PasswordAuthentication getPasswordAuthentication() {  
  76.                                 return new PasswordAuthentication(username, password);  
  77.                             }  
  78.                         });  
  79.                   
  80.                 try {  
  81.                     //创建邮件消息  
  82.                     MimeMessage msg = new MimeMessage(session);  
  83.                     //设置邮件发送人  
  84.                     msg.setFrom(new InternetAddress(StringUtils.isEmpty(mail  
  85.                             .getSender()) ? mailPro  
  86.                             .getProperty(MailProperties.MAIL_SMTP_USER) : mail  
  87.                             .getSender()));  
  88.                     //分别设置邮件的收件人、抄送人和密送人  
  89.                     msg.setRecipients(Message.RecipientType.TO, strListToInternetAddresses(mail.getRecipientsTo()));  
  90.                     msg.setRecipients(Message.RecipientType.CC, strListToInternetAddresses(mail.getRecipientsCc()));  
  91.                     msg.setRecipients(Message.RecipientType.BCC, strListToInternetAddresses(mail.getRecipientsBcc()));  
  92.                     //设置邮件主题  
  93.                     msg.setSubject(mail.getSubject());  
  94.                       
  95.                     Multipart mp = new MimeMultipart();  
  96.                       
  97.                     //创建邮件主体内容  
  98.                     MimeBodyPart mbp1 = new MimeBodyPart();  
  99.                     mbp1.setText(mail.getBody());  
  100.                     mp.addBodyPart(mbp1);  
  101.                       
  102.                     if(!CollectionUtils.isEmpty(mail.getAttachments())){  
  103.                         //循环添加邮件附件  
  104.                         MimeBodyPart attach = null;  
  105.                         for(String path : mail.getAttachments()){  
  106.                             attach = new MimeBodyPart();  
  107.                             try {  
  108.                                 attach.attachFile(path);  
  109.                                 mp.addBodyPart(attach);  
  110.                             } catch (IOException e) {  
  111.                                 if (logger.isErrorEnabled()) {  
  112.                                     logger.error(e);  
  113.                                 }  
  114.                             }  
  115.   
  116.                         }  
  117.                     }  
  118.                       
  119.                     msg.setContent(mp);  
  120.                     msg.setSentDate(new Date());  
  121.                       
  122.                     //邮件开始发送  
  123.                     Transport.send(msg);  
  124.                 } catch (AddressException e) {  
  125.                     if (logger.isErrorEnabled()) {  
  126.                         logger.error(e);  
  127.                     }  
  128.                 } catch (MessagingException e) {  
  129.                     if (logger.isErrorEnabled()) {  
  130.                         logger.error(e);  
  131.                     }  
  132.                 }  
  133.                   
  134.                   
  135.             }  
  136.   
  137.         };  
  138.         //使用Executor框架的线程池执行邮件发送任务  
  139.         executor.execute(task);  
  140.         return true;  
  141.     }  
  142.       
  143.     /** 
  144.      * 将列表中的字符串转换成InternetAddress对象 
  145.      * @param list 邮件字符串地址列表 
  146.      * @return InternetAddress对象数组 
  147.      */  
  148.     private InternetAddress[] strListToInternetAddresses(List<String> list) {  
  149.         if (list == null || list.isEmpty()) {  
  150.             return null;  
  151.         }  
  152.         int size = list.size();  
  153.         InternetAddress[] arr = new InternetAddress[size];  
  154.         for (int i = 0; i < size; i++) {  
  155.             try {  
  156.                 arr[i] = new InternetAddress(list.get(i));  
  157.             } catch (AddressException e) {  
  158.                 e.printStackTrace();  
  159.             }  
  160.         }  
  161.         return arr;  
  162.     }  
  163.   
  164. }  
 

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约