`

利用Spring框架封装的JavaMail现实同步或异步邮件发送

阅读更多
J2EE简单地讲是在JDK上扩展了各类应用的标准规范,邮件处理便是其中一个重要的应用。它既然是规范,那么我们就可以通过JDK遵照邮件协议编写一个邮件处理系统,但事实上已经有很多厂商和开源组织这样做了。Apache是J2EE最积极的实现者之一,当然还有我们的老大——SUN。

    聊起老大,感慨万端!他已经加入Oracle——甲骨文(不是刻在乌龟壳上的那种文字吗?是我中华,也是人类上最早的语言啊,比Java早几千年哦),其掌门人拉里·埃里森是个不错的水手,别以为那只是在帆船上,至少他不至于盖茨那么不仁道——开源万岁。有理由相信Java世界还有一段辉煌的历程。Google的Android和Chrome OS两大操作系统,还会首选Java作应用开发基础语言,即便是推出自己的易语言。同时笔者预感到ChromeOS前景不可估量,它可能是推动云计算一个重要组成部分,Andtroid(主用在移动设备上,未来可能是手机上主流的操作系统),乃至微软的Windows将来可能都是该系统的小窗口而已。微软已显得老态龙钟了,再与大势已去的雅虎合作,前进步伐必将大大减缓。投资者此时可以长线买入Google股票(投资建议,必自判断)。笔者也常用google搜索引擎、gmail。

    好了,闲话少聊,言归主题。

    可能大家如笔者一样用的最多的是老大的javamail,虽然老大实现了邮件功能,但调用起来还是需要较复杂的代码来完成,而且初学者调用成功率很低(因为它还要与外界服务器通信),这就使得初学者对于它越学越迷茫。不过这方面的例子很多,因此笔者不再在此重复这些示例代码,而着重利用Spring框架封装的邮件处理功能。

    开工之前,我们先了解下环境。笔者开的是web工程,所需要的基础配置如下:

▲ JDK 1.6

▲ J2EE 1.5


▲ JavaMail 1.4 稍作说明:J2EE 1.5中已经纳入了邮件规范,因此在开发期不要导入javamail中的jar包,运行期则需要,因此可以将jar包放入到web容器的java库中(例如Tomcat的lib目录下),要了解其意可以参考数据库驱动包的运用。文章结尾会对其进一步说明;

▲ Spring 2.5


▲ 一个邮箱  如上所述,笔者爱用Google的Gmail邮箱。

    主要文件清单:

■ MailService.java      邮件处理对象接口

■ MailServiceImpl.java  上述实现

■ Email.java                  一个普通的JavaBean,用于封装邮件数据,并与html页面中form表单对应

■ MailController.java         动作处理器

■ spring-core-config.xml      Spring核心配置

    笔者的web工程中,WEB层使用的是Spring @MVC,当然我们仅需要了解其原理,利用servlet或struts框架来做web层动作处理器都能实现。其它的文件,例如web.xml,WEB层配置均略。

下面开始代码:

spring-core-config.xml:
<!--①邮件服务器-->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="protocol" value="smtp"/>
<property name="host" value="smtp.gmail.com"/>
<property name="port" value="465" /><!--Gmail的SMTP端口居然是这个,去google网站上了解吧-->
<property name="username" value="到google注册一个gmail账户"/>
<property name="password" value="这里是密码"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>                        <!--gmail要求的ssl连接-->
</props>
</property>
</bean>

<!--②异步线程执行器-->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10"/>
<property name="maxPoolSize" value="30"/>
</bean>    这是邮件处理的两个核心配置,第一个配置(①)是往容器中装配一个JavaMailSender Bean,它就是JavaMail的封装,其中最关键的是装配过程的属性参数,这些属性既要严格遵照JavaMail规范,又要满足邮件提供商的要求,例如SMTP服务器端口是多少、发送时是否要身份验证、服务器是否采用安全连接、连接时是否加密以及采用什么样的加密方式,邮件服务商提供的这些参数直接影响到上述的配置,这往往是新手最容易忽视的环节,因此配置之前一定要到邮件提供商的站点上详细了解邮箱的技术参数。

    同步异步发送问题:JavaMail邮件处理是同步的,即用户触发事件、与SMTP Server通信、服务器返回状态消息、程序结束是单线程内,这时往往因Socket通信、服务器业务处理速度等原因而使得处理时间是个未知数。举个简单的应用实例:若用户在提交注册的同时发送一封激活账户邮件,用户有可能不知道是因为邮件服务器那儿阻塞致半天没有反应而以为注册失败并放弃,这将是失败的设计,但异步方式能解决这些问题。异步方式简单地说就是将邮件处理任务交给另外一个线程,J2EE有两种解决方案,一是种利用JMS,JMS可以实现同步和异步的消息处理,将邮件作为一个异步的消息,就可以实现异步邮件发送。JMS属于J2EE的高级应用,所以对于仅以WEB功能的容器还不支持这种服务,例如Tomcat(当然可以找到插件来解决),由于篇幅限制,本文不再牵涉到新的模块。另一种方案是利用JDK中Executor的支持,JDK 5.0后继版本增加了java.util.concurrent一个强大的并发工具包,它包含了执行器、计时器、锁、线程安全队列、线程任务框架等等。Executor——执行器,它可以将任务的“提交”与“执行”分离解耦,我们的邮件处理任务完全可以借用它实现异步执行。而Spring框架提供了封装,见②。下面我们来看如何使用它,代码如下。

MailServiceImpl.java :

package com.zhangjihao.service.impl;
import java.io.IOException;
import javax.annotation.Resource;import javax.mail.MessagingException;import javax.mail.internet.MimeMessage;
import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.core.io.ByteArrayResource;import org.springframework.core.task.TaskExecutor;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessageHelper;import org.springframework.stereotype.Service;import org.springframework.web.multipart.MultipartFile;
import com.zhangjihao.bean.Email;import com.zhangjihao.service.MailService;import com.zhangjihao.util.StringUtil;
/** * 说明:<br> *  * @author 张纪豪 * @version * Build Time Jul 24, 2009 */@Service("mailService")public class MailServiceImpl implements MailService {
@Resource JavaMailSender mailSender;//注入Spring封装的javamail,Spring的xml中已让框架装配 @Resource TaskExecutor taskExecutor;//注入Spring封装的异步执行器  private Log log = LogFactory.getLog(getClass()); private StringBuffer message = new StringBuffer();  public void sendMail(Email email) throws MessagingException, IOException {  if(email.getAddress() == null || email.getAddress().length == 0){   this.message.append("没有收件人");   return;  }  if(email.getAddress().length > 5){//收件人大于5封时,采用异步发送   sendMailByAsynchronousMode(email);   this.message.append("收件人过多,正在采用异步方式发送...<br/>");  }else{   sendMailBySynchronizationMode(email);   this.message.append("正在同步方式发送邮件...<br/>");  } }  /**   * 异步发送  * @see com.zhangjihao.service.MailService#sendMailByAsynchronousMode(com.zhangjihao.bean.Email)  */ public void sendMailByAsynchronousMode(final Email email){  taskExecutor.execute(new Runnable(){   public void run(){    try {     sendMailBySynchronizationMode(email);    } catch (Exception e) {     log.info(e);    }   }  }); }  /**   * 同步发送  * @throws IOException   * @see com.zhangjihao.service.MailServiceMode#sendMail(com.zhangjihao.bean.Email)  */ public void sendMailBySynchronizationMode(Email email) throws MessagingException, IOException {  MimeMessage mime = mailSender.createMimeMessage();  MimeMessageHelper helper = new MimeMessageHelper(mime, true, "utf-8");  helper.setFrom("cs@chinaptp.com");//发件人  helper.setTo(email.getAddress());//收件人  helper.setBcc("administrator@chinaptp.com");//暗送  if(StringUtil.hasLength(email.getCc())){   String cc[] = email.getCc().split(";");   helper.setCc(cc);//抄送  }  helper.setReplyTo("cs@chinaptp.com");//回复到  helper.setSubject(email.getSubject());//邮件主题  helper.setText(email.getContent(), true);//true表示设定html格式    //内嵌资源,这种功能很少用,因为大部分资源都在网上,只需在邮件正文中给个URL就足够了.  //helper.addInline("logo", new ClassPathResource("logo.gif"));    //处理附件  for(MultipartFile file : email.getAttachment()){   if(file == null || file.isEmpty()){    continue;   }   String fileName = file.getOriginalFilename();   try {    fileName = new String(fileName.getBytes("utf-8"),"ISO-8859-1");   } catch (Exception e) {}   helper.addAttachment(fileName, new ByteArrayResource(file.getBytes()));  }  mailSender.send(mime); }
public StringBuffer getMessage() {  return message; }
public void setMessage(StringBuffer message) {  this.message = message; }}    此类实现了MailService接口,该接口仅三个方法(接口文件代码省略):一个发送分流器、一个同步发送方法、一个异步发送方法。通过其实现者MailServiceImpl的代码可以看出,邮件发送仅在同步发送这个方法中,当需要异步执行的时候,只需要将其扔进taskExecutor异步执行器中,就这么简单。这三个方法都是public修饰的,所以在上层随意调用哪个都行。以下看一个简单的调用代码。

    调用之前,为让初学者能更好地接受,先列出Email.java代码:

Email.java:

package com.zhangjihao.bean;
import java.io.Serializable;
import org.springframework.web.multipart.MultipartFile;
import com.zhangjihao.util.StringUtil;
/** * 说明:<br> *  * @author 张纪豪 * @version * Build Time Jul 24, 2009 */public class Email implements Serializable {
private static final long serialVersionUID = 9063903350324510652L;  /**用户组:可以按用户组来批量发送邮件**/ private UserGroups userGroups;
/**收件人**/ private String addressee;  /**抄送给**/ private String cc;  /**邮件主题**/ private String subject;  /**邮件内容**/ private String content;  /**附件**/ private MultipartFile[] attachment = new MultipartFile[0];  //////////////////////////解析邮件地址//////////////////////////////  public String[] getAddress() {  if(!StringUtil.hasLength(this.addressee)) {   return null;  }  addressee = addressee.trim();  addressee.replaceAll(";", ";");  addressee.replaceAll(" ", ";");  addressee.replaceAll(",", ";");  addressee.replaceAll(",", ";");  addressee.replaceAll("|", ";");  return addressee.split(";"); }
/////////////////////////////Getter && Setter///////////////////////////////
...... }    这个类就是一个简单的JavaBean,用于封装邮件数据,对于习惯使用Struts框架的读者,完全可以把它理解为一个ActionForm。但对于MultipartFile类型且是数组的attachment属性可能较难理解,熟悉Struts框架的可以看作是FormFile,在Struts2中可能好理解些。笔者使用的是Spring MVC,框架中内置了这种属性编辑器,因此很容易地将form表单上传的文件进行转换成这个字段。

    我们来看看WEB层调用,其实到此为止,就已经完成本文的主题了,因此WEB怎么调用都是围绕MailService中的三个方法,为便有全面的认识,将代码列出,不过最好需要了解Spring @MVC的一些知识。

MailController.java:

package com.zhangjihao.web.controller.system;
import java.util.List;
import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;
import org.springframework.mail.javamail.JavaMailSender;import org.springframework.stereotype.Controller;import org.springframework.ui.ModelMap;import org.springframework.validation.BindingResult;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;
import com.zhangjihao.bean.Email;import com.zhangjihao.domain.user.User;import com.zhangjihao.service.MailService;import com.zhangjihao.service.UserService;import com.zhangjihao.util.StringUtil;import com.zhangjihao.web.controller.MasterController;import com.zhangjihao.web.validator.EmailValidator;
/** * 说明:<br> * 邮件发送处理器 * @author 张纪豪 * @version * Build Time Jul 24, 2009 */@Controllerpublic class MailController extends MasterController {
@Resource MailService mailService; @Resource UserService userService;
@RequestMapping(value = "/sendEmail", method=RequestMethod.GET) public String sendEmail(@RequestParam(value="email",required=false) String singleEmailAddress , HttpServletRequest request){  Email email = new Email();  if(StringUtil.hasLength(singleEmailAddress)){   email.setAddressee(singleEmailAddress);  }  request.setAttribute("email", email);  return "system/sendMail"; }  @RequestMapping(value = "/sendEmail", method=RequestMethod.POST) public String send(   @ModelAttribute Email email,  //Spring MVC将form表单的数据封装到这个对象中   BindingResult result,   ModelMap model,   HttpServletRequest request){  try {   new EmailValidator().validate(email, result);   if(result.hasErrors()){    throw new RuntimeException("数据填写不正确");   }   if(email.getEmailGroup()!=null){    List<User> users = userService.getUserByUserGroups(email.getEmailGroup(), "userName,email", null, null);    StringBuffer sb = new StringBuffer(StringUtil.hasLengthBytrim(email.getAddressee()) ? (email.getAddressee().trim() + ";") : "");    for(User user : users){     sb.append(user.getEmail()).append(";");    }    email.setAddressee(sb.toString());   }   if(email.getAddress()==null || email.getAddress().length==0){    request.setAttribute("message", "没有收件人!");    return "message";   }      mailService.sendMail(email);  //大于5个收件人时,分流器会自动选择异步方式发送   request.setAttribute("message", mailService.getMessage().append("本次共发送了 ").append(email.getAddress().length).append(" 封邮件.").toString());     } catch (Exception e) {   request.setAttribute("message", "Has errors! The info by "+e.getMessage()+"<br/>Can log in to view more detailed information on abnormalities.");   log.error(this.getClass().getName()+"中发生异常---------------:\n", e);  }  return BACK + "message"; }}    当一个get方法请求的连接进来,此控制器会转向一个html页面,其页面中有form表单,表单中的字段与Email.java对应,当post方法过来后,Spring MVC会把表单中的数据填充到Email对象中,交给MailService处理就ok了。

    最后讲述下最容易出现的错误:

    网上很多人都说J2EE5兼容性不好,例如典型的javamail1.4中包与J2EE5中包接口包引起冲突,导致单元测试经常报如下错误:

    java.lang.NoClassDefFoundError: com/sun/mail/util/BEncoderStream

    当然这个错误是没有将javamail的实现者引进工程(没有导包),但导包后,就会出现另外一个错误:

    java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream

    此时甚至web容器都无法启动,经常会有网友们为这两个异常搞得焦头烂额,如此更换J2EE1.4,会对工程造成影响。但是一定要把概念弄清楚,问题就好解决。J2EE5中mail.jar包定义的只是接口,没有实现,是不能真正发送邮件的,但开发编译肯定是可以过去的,因为我们是针对J2EE规范编的程序。而运行期用Sun公司的JavaMail1.4的实现才可以开始发送邮件,但老大为什么把这两个弄冲突了?

    笔者的解决办法是:

    开发期不要导包,运行期将javamail1.4压缩文件中的mail.jar包放入到tomcat\lib目录下,这样完全可以通过开发和运行。若要做单元测试则新开一个Java Project,注意,不是web工程,此时可以将javamail1.4压缩包中的mail.jar放入到工程的classpath下。

    如果有疑问或建议请MSN至jihao_zh@hotmail.com,共同探讨。

分享到:
评论

相关推荐

    springboot整合JavaMail,实现异步发送邮件功能

    发送邮件时,首先创建出封装了邮件数据的 Message 对象, 然后把这个对象传递给邮件发送Transport 类,执行发送。 接收邮件时,把接收到的邮件数据封装在Message 类的实例中,从这个对象中解析收到的邮件数据。 ...

    spring结合javamail开发文档

    ### Spring结合JavaMail开发文档知识点...通过上述知识点的学习,开发者可以更加高效地利用Spring与JavaMail组合来实现邮件发送的功能,无论是简单的纯文本邮件还是复杂的HTML邮件甚至是带有附件的邮件都能轻松应对。

    Spring 3.x企业应用开发实战 附录 A - JavaMail 发送邮件

    ### Spring 3.x企业应用开发实战之JavaMail发送邮件知识点详解 #### 一、JavaMail基础知识的快速学习 ##### 1.1 JavaMail概述 JavaMail是Java领域中最为知名的邮件解决方案之一,由Sun Microsystems(现已被...

    SpringMail

    SpringMail 是一个在 Java 应用中发送电子邮件的库,它是 Spring 框架的一个扩展,使得在基于 Spring 的应用程序中集成邮件服务变得非常简单。SpringMail 提供了一种方便的方式来配置和使用 JavaMail API,避免了...

    Spring in practice

    - **邮件发送**:Spring提供了邮件发送的支持,可以轻松集成JavaMail等邮件发送库。 - **消息队列**:支持与消息中间件(如RabbitMQ、ActiveMQ)的集成,实现异步通信。 - **即时通讯**:Spring还支持WebSocket等...

    发送邮箱前后端代码.zip

    这个文件很可能是后端的一个工具类,封装了发送邮件的逻辑。通常,后端会使用SMTP(Simple Mail Transfer Protocol)协议来发送邮件。Java中,我们可以使用JavaMail API或Apache Commons Email等库来实现。开发者...

    自己开发的JAVA Web Email邮件系统

    JavaMail API是实现这一功能的Java库,它封装了SMTP交互,使得发送邮件变得更加方便。 3. **POP3/IMAP协议**:为了接收邮件,系统需要支持POP3(邮局协议版本3)或IMAP(因特网消息访问协议)。这些协议允许用户从...

    一个简单的email发送

    确保在Web环境中正确配置了JavaMail的相关属性,例如在`web.xml`或Spring的配置文件中。 值得注意的是,为了在实际生产环境中安全地处理用户名和密码,通常会使用环境变量或配置中心来存储这些敏感信息,而不是直接...

    用java发邮件代码

    Java发送邮件是软件开发中常见的任务之一,尤其在企业级应用中,用于自动化通知、报告或者用户...对于更复杂的邮件服务,可以使用开源库如JavaMailSenderImpl(Spring框架)进行封装,以简化代码并提供更强大的功能。

    struts2发送Email

    以上就是如何在Struts2框架中利用JavaMail API发送电子邮件的详细过程。这个功能可以极大地提升应用程序的用户体验,为用户提供实时的反馈和通信渠道。通过灵活地调整配置,你可以适应各种邮件服务提供商,并根据...

    java 工具类-----email

    对于更复杂的应用场景,还可以使用开源库,如Apache Commons Email或Spring框架中的MailSender,它们提供了更友好的API来简化邮件发送操作。 在提供的标签"源码 工具"中,我们可以理解到,深入学习和理解JavaMail ...

    企业级j2ee应用开发框架套件

    8. **JavaMail API**:用于发送和接收电子邮件,常用于系统通知和验证。 9. **Java WebSocket API**:提供全双工、低延迟的客户端-服务器通信,适合实时应用。 这个"企业级j2ee应用开发框架套件"可能还包括了流行...

    Flex、Spring、Hibernate 集成

    通过上述技术和示例程序的介绍,我们可以看到**Flex**、**Spring** 和**Hibernate** 的集成不仅可以创建出功能强大、用户体验优秀的RIA应用,而且还能充分利用各个框架的优势,提高开发效率和应用质量。这对于希望...

    javaweb开发要学习的所有技术和框架总结:分享.pdf

    - **JavaMail**:处理电子邮件的API,可用于发送、接收邮件。 - **Session和Cookie**:用于跟踪用户状态,实现会话管理。 4. **框架**: - **Struts**:MVC(Model-View-Controller)架构框架,简化Web应用的...

    JAVA销售管理系统

    5. **辅助工具模块**:可能包含邮件发送、通知提醒等功能,需要用到JavaMail API进行邮件服务的集成,以及Java的定时任务框架Quartz进行任务调度。 6. **系统设置模块**:允许管理员配置系统参数,如权限管理、角色...

    jsp常用模块源代码

    例如,使用Spring Security或Apache Shiro这样的框架可以简化这部分的工作。 5. **邮件管理模块**:邮件服务在Web应用中常用于发送验证码、通知、提醒等。JavaMail API是Java平台中处理邮件的主要工具,它可以用来...

    javaweb开发要学习的所有技术和框架总结:.docx

    - **JavaMail**:用于发送和接收电子邮件的API,支持多种邮件协议如SMTP、POP3、IMAP。 - **Javabean**:封装数据的对象,遵循一定的规范,常用于前后端的数据传输。 - **EL表达式**:Expression Language简化了...

    「javaweb开发要学习的所有技术和框架总结:」.docx

    - **定义**:JavaMail API为Java应用程序提供了发送和接收电子邮件的功能。 - **用途**:实现自动发送邮件、通知等功能,增强应用的交互性。 ##### EL (Expression Language) - **定义**:EL是一种简单的表达式语言...

Global site tag (gtag.js) - Google Analytics