利用Spring框架封装的JavaMail现实同步或异步邮件发送
生活随笔
收集整理的這篇文章主要介紹了
利用Spring框架封装的JavaMail现实同步或异步邮件发送
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
利用Spring框架封裝的JavaMail現(xiàn)實(shí)同步或異步郵件發(fā)送
作者:張紀(jì)豪
J2EE簡(jiǎn)單地講是在JDK上擴(kuò)展了各類應(yīng)用的標(biāo)準(zhǔn)規(guī)范,郵件處理便是其中一個(gè)重要的應(yīng)用。它既然是規(guī)范,那么我們就可以通過JDK遵照郵件協(xié)議編寫一個(gè)郵件處理系統(tǒng),但事實(shí)上已經(jīng)有很多廠商和開源組織這樣做了。Apache是J2EE最積極的實(shí)現(xiàn)者之一,當(dāng)然還有我們的老大——SUN。
聊起老大,感慨萬端!他已經(jīng)加入Oracle——甲骨文(不是刻在烏龜殼上的那種文字嗎?是我中華,也是人類上最早的語言啊,比Java早幾千年哦),其掌門人拉里·埃里森是個(gè)不錯(cuò)的水手,別以為那只是在帆船上,至少他不至于蓋茨那么不仁道——開源萬歲。有理由相信Java世界還有一段輝煌的歷程。Google的Android和Chrome OS兩大操作系統(tǒng),還會(huì)首選Java作應(yīng)用開發(fā)基礎(chǔ)語言,即便是推出自己的易語言。同時(shí)筆者預(yù)感到ChromeOS前景不可估量,它可能是推動(dòng)云計(jì)算一個(gè)重要組成部分,Andtroid(主用在移動(dòng)設(shè)備上,未來可能是手機(jī)上主流的操作系統(tǒng)),乃至微軟的Windows將來可能都是該系統(tǒng)的小窗口而已。微軟已顯得老態(tài)龍鐘了,再與大勢(shì)已去的雅虎合作,前進(jìn)步伐必將大大減緩。投資者此時(shí)可以長(zhǎng)線買入Google股票(投資建議,必自判斷)。筆者也常用google搜索引擎、gmail。
好了,閑話少聊,言歸主題。
可能大家如筆者一樣用的最多的是老大的javamail,雖然老大實(shí)現(xiàn)了郵件功能,但調(diào)用起來還是需要較復(fù)雜的代碼來完成,而且初學(xué)者調(diào)用成功率很低(因?yàn)樗€要與外界服務(wù)器通信),這就使得初學(xué)者對(duì)于它越學(xué)越迷茫。不過這方面的例子很多,因此筆者不再在此重復(fù)這些示例代碼,而著重利用Spring框架封裝的郵件處理功能。
開工之前,我們先了解下環(huán)境。筆者開的是web工程,所需要的基礎(chǔ)配置如下:
▲ JDK 1.6
▲ J2EE 1.5
▲ JavaMail 1.4 稍作說明:J2EE 1.5中已經(jīng)納入了郵件規(guī)范,因此在開發(fā)期不要導(dǎo)入javamail中的jar包,運(yùn)行期則需要,因此可以將jar包放入到web容器的java庫中(例如Tomcat的lib目錄下),要了解其意可以參考數(shù)據(jù)庫驅(qū)動(dòng)包的運(yùn)用。文章結(jié)尾會(huì)對(duì)其進(jìn)一步說明;
▲ Spring 2.5
▲ 一個(gè)郵箱 如上所述,筆者愛用Google的Gmail郵箱。
主要文件清單:
■ MailService.java 郵件處理對(duì)象接口
■ MailServiceImpl.java 上述實(shí)現(xiàn)
■ Email.java 一個(gè)普通的JavaBean,用于封裝郵件數(shù)據(jù),并與html頁面中form表單對(duì)應(yīng)
■ MailController.java 動(dòng)作處理器
■ spring-core-config.xml Spring核心配置
筆者的web工程中,WEB層使用的是Spring @MVC,當(dāng)然我們僅需要了解其原理,利用servlet或struts框架來做web層動(dòng)作處理器都能實(shí)現(xiàn)。其它的文件,例如web.xml,WEB層配置均略。
下面開始代碼:
spring-core-config.xml:
Xml代碼<!--①郵件服務(wù)器--> <beanid="mailSender"class="org.springframework.mail.javamail.JavaMailSenderImpl"> <propertyname="protocol"value="smtp"/> <propertyname="host"value="smtp.gmail.com"/> <propertyname="port"value="465"/><!--Gmail的SMTP端口居然是這個(gè),去google網(wǎng)站上了解吧--> <propertyname="username"value="到google注冊(cè)一個(gè)gmail賬戶"/> <propertyname="password"value="這里是密碼"/> <propertyname="javaMailProperties"> <props> <propkey="mail.smtp.auth">true</prop> <propkey="mail.smtp.starttls.enable">true</prop> <propkey="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop><!--gmail要求的ssl連接--> </props> </property> </bean> <!--②異步線程執(zhí)行器--> <beanid="taskExecutor"class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <propertyname="corePoolSize"value="10"/> <propertyname="maxPoolSize"value="30"/> </bean> <!--①郵件服務(wù)器-->
<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端口居然是這個(gè),去google網(wǎng)站上了解吧--><property name="username" value="到google注冊(cè)一個(gè)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><!--②異步線程執(zhí)行器-->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><property name="corePoolSize" value="10"/><property name="maxPoolSize" value="30"/>
</bean>
這是郵件處理的兩個(gè)核心配置,第一個(gè)配置(①)是往容器中裝配一個(gè)JavaMailSender Bean,它就是JavaMail的封裝,其中最關(guān)鍵的是裝配過程的屬性參數(shù),這些屬性既要嚴(yán)格遵照J(rèn)avaMail規(guī)范,又要滿足郵件提供商的要求,例如SMTP服務(wù)器端口是多少、發(fā)送時(shí)是否要身份驗(yàn)證、服務(wù)器是否采用安全連接、連接時(shí)是否加密以及采用什么樣的加密方式,郵件服務(wù)商提供的這些參數(shù)直接影響到上述的配置,這往往是新手最容易忽視的環(huán)節(jié),因此配置之前一定要到郵件提供商的站點(diǎn)上詳細(xì)了解郵箱的技術(shù)參數(shù)。
同步異步發(fā)送問題:JavaMail郵件處理是同步的,即用戶觸發(fā)事件、與SMTP Server通信、服務(wù)器返回狀態(tài)消息、程序結(jié)束是單線程內(nèi),這時(shí)往往因Socket通信、服務(wù)器業(yè)務(wù)處理速度等原因而使得處理時(shí)間是個(gè)未知數(shù)。舉個(gè)簡(jiǎn)單的應(yīng)用實(shí)例:若用戶在提交注冊(cè)的同時(shí)發(fā)送一封激活賬戶郵件,用戶有可能不知道是因?yàn)猷]件服務(wù)器那兒阻塞致半天沒有反應(yīng)而以為注冊(cè)失敗并放棄,這將是失敗的設(shè)計(jì),但異步方式能解決這些問題。異步方式簡(jiǎn)單地說就是將郵件處理任務(wù)交給另外一個(gè)線程,J2EE有兩種解決方案,一是種利用JMS,JMS可以實(shí)現(xiàn)同步和異步的消息處理,將郵件作為一個(gè)異步的消息,就可以實(shí)現(xiàn)異步郵件發(fā)送。JMS屬于J2EE的高級(jí)應(yīng)用,所以對(duì)于僅以WEB功能的容器還不支持這種服務(wù),例如Tomcat(當(dāng)然可以找到插件來解決),由于篇幅限制,本文不再牽涉到新的模塊。另一種方案是利用JDK中Executor的支持,JDK 5.0后繼版本增加了java.util.concurrent一個(gè)強(qiáng)大的并發(fā)工具包,它包含了執(zhí)行器、計(jì)時(shí)器、鎖、線程安全隊(duì)列、線程任務(wù)框架等等。Executor——執(zhí)行器,它可以將任務(wù)的“提交”與“執(zhí)行”分離解耦,我們的郵件處理任務(wù)完全可以借用它實(shí)現(xiàn)異步執(zhí)行。而Spring框架提供了封裝,見②。下面我們來看如何使用它,代碼如下。
MailServiceImpl.java :
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 張紀(jì)豪 * @version * Build Time Jul 24, 2009 */ @Service("mailService") public class MailServiceImplimplements MailService { @Resource JavaMailSender mailSender;//注入Spring封裝的javamail,Spring的xml中已讓框架裝配 @Resource TaskExecutor taskExecutor;//注入Spring封裝的異步執(zhí)行器 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封時(shí),采用異步發(fā)送 sendMailByAsynchronousMode(email); this.message.append("收件人過多,正在采用異步方式發(fā)送...<br/>"); }else{ sendMailBySynchronizationMode(email); this.message.append("正在同步方式發(fā)送郵件...<br/>"); } } /** * 異步發(fā)送 * @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); } } }); } /** * 同步發(fā)送 * @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");//發(fā)件人 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");//回復(fù)到 helper.setSubject(email.getSubject());//郵件主題 helper.setText(email.getContent(), true);//true表示設(shè)定html格式 //內(nèi)嵌資源,這種功能很少用,因?yàn)榇蟛糠仲Y源都在網(wǎng)上,只需在郵件正文中給個(gè)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; }} 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 張紀(jì)豪
* @version
* Build Time Jul 24, 2009
*/@Service("mailService")
public class MailServiceImpl implements MailService {@Resource JavaMailSender mailSender;//注入Spring封裝的javamail,Spring的xml中已讓框架裝配
@Resource TaskExecutor taskExecutor;//注入Spring封裝的異步執(zhí)行器 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封時(shí),采用異步發(fā)送 sendMailByAsynchronousMode(email); this.message.append("收件人過多,正在采用異步方式發(fā)送...<br/>");}else{ sendMailBySynchronizationMode(email); this.message.append("正在同步方式發(fā)送郵件...<br/>"); }
}
/**
* 異步發(fā)送
* @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); } }
});
} /**
* 同步發(fā)送
* @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");//發(fā)件人
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");//回復(fù)到 helper.setSubject(email.getSubject());//郵件主題
helper.setText(email.getContent(), true);//true表示設(shè)定html格式 //內(nèi)嵌資源,這種功能很少用,因?yàn)榇蟛糠仲Y源都在網(wǎng)上,只需在郵件正文中給個(gè)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; }}
此類實(shí)現(xiàn)了MailService接口,該接口僅三個(gè)方法(接口文件代碼省略):一個(gè)發(fā)送分流器、一個(gè)同步發(fā)送方法、一個(gè)異步發(fā)送方法。通過其實(shí)現(xiàn)者M(jìn)ailServiceImpl的代碼可以看出,郵件發(fā)送僅在同步發(fā)送這個(gè)方法中,當(dāng)需要異步執(zhí)行的時(shí)候,只需要將其扔進(jìn)taskExecutor異步執(zhí)行器中,就這么簡(jiǎn)單。這三個(gè)方法都是public修飾的,所以在上層隨意調(diào)用哪個(gè)都行。以下看一個(gè)簡(jiǎn)單的調(diào)用代碼。
調(diào)用之前,為讓初學(xué)者能更好地接受,先列出Email.java代碼:
Email.java:
Java代碼package com.zhangjihao.bean; import java.io.Serializable; import org.springframework.web.multipart.MultipartFile; import com.zhangjihao.util.StringUtil; /** * 說明:<br> * * @author 張紀(jì)豪 * @version * Build Time Jul 24, 2009 */ public class Emailimplements Serializable { private staticfinal long serialVersionUID = 9063903350324510652L; /**用戶組:可以按用戶組來批量發(fā)送郵件**/ private UserGroups userGroups; /**收件人**/ private String addressee; /**抄送給**/ private String cc; /**郵件主題**/ private String subject; /**郵件內(nèi)容**/ 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/// ...... } package com.zhangjihao.bean;
import java.io.Serializable;
import org.springframework.web.multipart.MultipartFile;
import com.zhangjihao.util.StringUtil;
/**
* 說明:<br>
*
* @author 張紀(jì)豪
* @version
* Build Time Jul 24, 2009
*/
public class Email implements Serializable {private static final long serialVersionUID = 9063903350324510652L; /**用戶組:可以按用戶組來批量發(fā)送郵件**/
private UserGroups userGroups;/**收件人**/private String addressee;
/**抄送給**/
private String cc;
/**郵件主題**/private String subject; /**郵件內(nèi)容**/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///...... }
這個(gè)類就是一個(gè)簡(jiǎn)單的JavaBean,用于封裝郵件數(shù)據(jù),對(duì)于習(xí)慣使用Struts框架的讀者,完全可以把它理解為一個(gè)ActionForm。但對(duì)于MultipartFile類型且是數(shù)組的attachment屬性可能較難理解,熟悉Struts框架的可以看作是FormFile,在Struts2中可能好理解些。筆者使用的是Spring MVC,框架中內(nèi)置了這種屬性編輯器,因此很容易地將form表單上傳的文件進(jìn)行轉(zhuǎn)換成這個(gè)字段。
我們來看看WEB層調(diào)用,其實(shí)到此為止,就已經(jīng)完成本文的主題了,因此WEB怎么調(diào)用都是圍繞MailService中的三個(gè)方法,為便有全面的認(rèn)識(shí),將代碼列出,不過最好需要了解Spring @MVC的一些知識(shí)。
MailController.java:
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> * 郵件發(fā)送處理器 * @author 張紀(jì)豪 * @version * Build Time Jul 24, 2009 */ @Controllerpublic class MailControllerextends 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表單的數(shù)據(jù)封裝到這個(gè)對(duì)象中 BindingResult result, ModelMap model, HttpServletRequest request){ try { new EmailValidator().validate(email, result); if(result.hasErrors()){ throw new RuntimeException("數(shù)據(jù)填寫不正確"); } 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個(gè)收件人時(shí),分流器會(huì)自動(dòng)選擇異步方式發(fā)送 request.setAttribute("message", mailService.getMessage().append("本次共發(fā)送了 ").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()+"中發(fā)生異常---------------:\n", e); } return BACK + "message"; } } 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>
* 郵件發(fā)送處理器
* @author 張紀(jì)豪
* @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表單的數(shù)據(jù)封裝到這個(gè)對(duì)象中 BindingResult result, ModelMap model, HttpServletRequest request){ try { new EmailValidator().validate(email, result); if(result.hasErrors()){ throw new RuntimeException("數(shù)據(jù)填寫不正確"); } 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個(gè)收件人時(shí),分流器會(huì)自動(dòng)選擇異步方式發(fā)送
request.setAttribute("message", mailService.getMessage().append("本次共發(fā)送了 ").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()+"中發(fā)生異常---------------:\n", e);
}
return BACK + "message";
}
}
當(dāng)一個(gè)get方法請(qǐng)求的連接進(jìn)來,此控制器會(huì)轉(zhuǎn)向一個(gè)html頁面,其頁面中有form表單,表單中的字段與Email.java對(duì)應(yīng),當(dāng)post方法過來后,Spring MVC會(huì)把表單中的數(shù)據(jù)填充到Email對(duì)象中,交給MailService處理就ok了。
最后講述下最容易出現(xiàn)的錯(cuò)誤:
網(wǎng)上很多人都說J2EE5兼容性不好,例如典型的javamail1.4中包與J2EE5中包接口包引起沖突,導(dǎo)致單元測(cè)試經(jīng)常報(bào)如下錯(cuò)誤:
java.lang.NoClassDefFoundError: com/sun/mail/util/BEncoderStream
當(dāng)然這個(gè)錯(cuò)誤是沒有將javamail的實(shí)現(xiàn)者引進(jìn)工程(沒有導(dǎo)包),但導(dǎo)包后,就會(huì)出現(xiàn)另外一個(gè)錯(cuò)誤:
java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream
此時(shí)甚至web容器都無法啟動(dòng),經(jīng)常會(huì)有網(wǎng)友們?yōu)檫@兩個(gè)異常搞得焦頭爛額,如此更換J2EE1.4,會(huì)對(duì)工程造成影響。但是一定要把概念弄清楚,問題就好解決。J2EE5中mail.jar包定義的只是接口,沒有實(shí)現(xiàn),是不能真正發(fā)送郵件的,但開發(fā)編譯肯定是可以過去的,因?yàn)槲覀兪轻槍?duì)J2EE規(guī)范編的程序。而運(yùn)行期用Sun公司的JavaMail1.4的實(shí)現(xiàn)才可以開始發(fā)送郵件,但老大為什么把這兩個(gè)弄沖突了?
筆者的解決辦法是:
開發(fā)期不要導(dǎo)包,運(yùn)行期將javamail1.4壓縮文件中的mail.jar包放入到tomcat\lib目錄下,這樣完全可以通過開發(fā)和運(yùn)行。若要做單元測(cè)試則新開一個(gè)Java Project,注意,不是web工程,此時(shí)可以將javamail1.4壓縮包中的mail.jar放入到工程的classpath下。?
作者:張紀(jì)豪
J2EE簡(jiǎn)單地講是在JDK上擴(kuò)展了各類應(yīng)用的標(biāo)準(zhǔn)規(guī)范,郵件處理便是其中一個(gè)重要的應(yīng)用。它既然是規(guī)范,那么我們就可以通過JDK遵照郵件協(xié)議編寫一個(gè)郵件處理系統(tǒng),但事實(shí)上已經(jīng)有很多廠商和開源組織這樣做了。Apache是J2EE最積極的實(shí)現(xiàn)者之一,當(dāng)然還有我們的老大——SUN。
聊起老大,感慨萬端!他已經(jīng)加入Oracle——甲骨文(不是刻在烏龜殼上的那種文字嗎?是我中華,也是人類上最早的語言啊,比Java早幾千年哦),其掌門人拉里·埃里森是個(gè)不錯(cuò)的水手,別以為那只是在帆船上,至少他不至于蓋茨那么不仁道——開源萬歲。有理由相信Java世界還有一段輝煌的歷程。Google的Android和Chrome OS兩大操作系統(tǒng),還會(huì)首選Java作應(yīng)用開發(fā)基礎(chǔ)語言,即便是推出自己的易語言。同時(shí)筆者預(yù)感到ChromeOS前景不可估量,它可能是推動(dòng)云計(jì)算一個(gè)重要組成部分,Andtroid(主用在移動(dòng)設(shè)備上,未來可能是手機(jī)上主流的操作系統(tǒng)),乃至微軟的Windows將來可能都是該系統(tǒng)的小窗口而已。微軟已顯得老態(tài)龍鐘了,再與大勢(shì)已去的雅虎合作,前進(jìn)步伐必將大大減緩。投資者此時(shí)可以長(zhǎng)線買入Google股票(投資建議,必自判斷)。筆者也常用google搜索引擎、gmail。
好了,閑話少聊,言歸主題。
可能大家如筆者一樣用的最多的是老大的javamail,雖然老大實(shí)現(xiàn)了郵件功能,但調(diào)用起來還是需要較復(fù)雜的代碼來完成,而且初學(xué)者調(diào)用成功率很低(因?yàn)樗€要與外界服務(wù)器通信),這就使得初學(xué)者對(duì)于它越學(xué)越迷茫。不過這方面的例子很多,因此筆者不再在此重復(fù)這些示例代碼,而著重利用Spring框架封裝的郵件處理功能。
開工之前,我們先了解下環(huán)境。筆者開的是web工程,所需要的基礎(chǔ)配置如下:
▲ JDK 1.6
▲ J2EE 1.5
▲ JavaMail 1.4 稍作說明:J2EE 1.5中已經(jīng)納入了郵件規(guī)范,因此在開發(fā)期不要導(dǎo)入javamail中的jar包,運(yùn)行期則需要,因此可以將jar包放入到web容器的java庫中(例如Tomcat的lib目錄下),要了解其意可以參考數(shù)據(jù)庫驅(qū)動(dòng)包的運(yùn)用。文章結(jié)尾會(huì)對(duì)其進(jìn)一步說明;
▲ Spring 2.5
▲ 一個(gè)郵箱 如上所述,筆者愛用Google的Gmail郵箱。
主要文件清單:
■ MailService.java 郵件處理對(duì)象接口
■ MailServiceImpl.java 上述實(shí)現(xiàn)
■ Email.java 一個(gè)普通的JavaBean,用于封裝郵件數(shù)據(jù),并與html頁面中form表單對(duì)應(yīng)
■ MailController.java 動(dòng)作處理器
■ spring-core-config.xml Spring核心配置
筆者的web工程中,WEB層使用的是Spring @MVC,當(dāng)然我們僅需要了解其原理,利用servlet或struts框架來做web層動(dòng)作處理器都能實(shí)現(xiàn)。其它的文件,例如web.xml,WEB層配置均略。
下面開始代碼:
spring-core-config.xml:
Xml代碼
這是郵件處理的兩個(gè)核心配置,第一個(gè)配置(①)是往容器中裝配一個(gè)JavaMailSender Bean,它就是JavaMail的封裝,其中最關(guān)鍵的是裝配過程的屬性參數(shù),這些屬性既要嚴(yán)格遵照J(rèn)avaMail規(guī)范,又要滿足郵件提供商的要求,例如SMTP服務(wù)器端口是多少、發(fā)送時(shí)是否要身份驗(yàn)證、服務(wù)器是否采用安全連接、連接時(shí)是否加密以及采用什么樣的加密方式,郵件服務(wù)商提供的這些參數(shù)直接影響到上述的配置,這往往是新手最容易忽視的環(huán)節(jié),因此配置之前一定要到郵件提供商的站點(diǎn)上詳細(xì)了解郵箱的技術(shù)參數(shù)。
同步異步發(fā)送問題:JavaMail郵件處理是同步的,即用戶觸發(fā)事件、與SMTP Server通信、服務(wù)器返回狀態(tài)消息、程序結(jié)束是單線程內(nèi),這時(shí)往往因Socket通信、服務(wù)器業(yè)務(wù)處理速度等原因而使得處理時(shí)間是個(gè)未知數(shù)。舉個(gè)簡(jiǎn)單的應(yīng)用實(shí)例:若用戶在提交注冊(cè)的同時(shí)發(fā)送一封激活賬戶郵件,用戶有可能不知道是因?yàn)猷]件服務(wù)器那兒阻塞致半天沒有反應(yīng)而以為注冊(cè)失敗并放棄,這將是失敗的設(shè)計(jì),但異步方式能解決這些問題。異步方式簡(jiǎn)單地說就是將郵件處理任務(wù)交給另外一個(gè)線程,J2EE有兩種解決方案,一是種利用JMS,JMS可以實(shí)現(xiàn)同步和異步的消息處理,將郵件作為一個(gè)異步的消息,就可以實(shí)現(xiàn)異步郵件發(fā)送。JMS屬于J2EE的高級(jí)應(yīng)用,所以對(duì)于僅以WEB功能的容器還不支持這種服務(wù),例如Tomcat(當(dāng)然可以找到插件來解決),由于篇幅限制,本文不再牽涉到新的模塊。另一種方案是利用JDK中Executor的支持,JDK 5.0后繼版本增加了java.util.concurrent一個(gè)強(qiáng)大的并發(fā)工具包,它包含了執(zhí)行器、計(jì)時(shí)器、鎖、線程安全隊(duì)列、線程任務(wù)框架等等。Executor——執(zhí)行器,它可以將任務(wù)的“提交”與“執(zhí)行”分離解耦,我們的郵件處理任務(wù)完全可以借用它實(shí)現(xiàn)異步執(zhí)行。而Spring框架提供了封裝,見②。下面我們來看如何使用它,代碼如下。
MailServiceImpl.java :
Java代碼
此類實(shí)現(xiàn)了MailService接口,該接口僅三個(gè)方法(接口文件代碼省略):一個(gè)發(fā)送分流器、一個(gè)同步發(fā)送方法、一個(gè)異步發(fā)送方法。通過其實(shí)現(xiàn)者M(jìn)ailServiceImpl的代碼可以看出,郵件發(fā)送僅在同步發(fā)送這個(gè)方法中,當(dāng)需要異步執(zhí)行的時(shí)候,只需要將其扔進(jìn)taskExecutor異步執(zhí)行器中,就這么簡(jiǎn)單。這三個(gè)方法都是public修飾的,所以在上層隨意調(diào)用哪個(gè)都行。以下看一個(gè)簡(jiǎn)單的調(diào)用代碼。
調(diào)用之前,為讓初學(xué)者能更好地接受,先列出Email.java代碼:
Email.java:
Java代碼
這個(gè)類就是一個(gè)簡(jiǎn)單的JavaBean,用于封裝郵件數(shù)據(jù),對(duì)于習(xí)慣使用Struts框架的讀者,完全可以把它理解為一個(gè)ActionForm。但對(duì)于MultipartFile類型且是數(shù)組的attachment屬性可能較難理解,熟悉Struts框架的可以看作是FormFile,在Struts2中可能好理解些。筆者使用的是Spring MVC,框架中內(nèi)置了這種屬性編輯器,因此很容易地將form表單上傳的文件進(jìn)行轉(zhuǎn)換成這個(gè)字段。
我們來看看WEB層調(diào)用,其實(shí)到此為止,就已經(jīng)完成本文的主題了,因此WEB怎么調(diào)用都是圍繞MailService中的三個(gè)方法,為便有全面的認(rèn)識(shí),將代碼列出,不過最好需要了解Spring @MVC的一些知識(shí)。
MailController.java:
Java代碼
當(dāng)一個(gè)get方法請(qǐng)求的連接進(jìn)來,此控制器會(huì)轉(zhuǎn)向一個(gè)html頁面,其頁面中有form表單,表單中的字段與Email.java對(duì)應(yīng),當(dāng)post方法過來后,Spring MVC會(huì)把表單中的數(shù)據(jù)填充到Email對(duì)象中,交給MailService處理就ok了。
最后講述下最容易出現(xiàn)的錯(cuò)誤:
網(wǎng)上很多人都說J2EE5兼容性不好,例如典型的javamail1.4中包與J2EE5中包接口包引起沖突,導(dǎo)致單元測(cè)試經(jīng)常報(bào)如下錯(cuò)誤:
java.lang.NoClassDefFoundError: com/sun/mail/util/BEncoderStream
當(dāng)然這個(gè)錯(cuò)誤是沒有將javamail的實(shí)現(xiàn)者引進(jìn)工程(沒有導(dǎo)包),但導(dǎo)包后,就會(huì)出現(xiàn)另外一個(gè)錯(cuò)誤:
java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream
此時(shí)甚至web容器都無法啟動(dòng),經(jīng)常會(huì)有網(wǎng)友們?yōu)檫@兩個(gè)異常搞得焦頭爛額,如此更換J2EE1.4,會(huì)對(duì)工程造成影響。但是一定要把概念弄清楚,問題就好解決。J2EE5中mail.jar包定義的只是接口,沒有實(shí)現(xiàn),是不能真正發(fā)送郵件的,但開發(fā)編譯肯定是可以過去的,因?yàn)槲覀兪轻槍?duì)J2EE規(guī)范編的程序。而運(yùn)行期用Sun公司的JavaMail1.4的實(shí)現(xiàn)才可以開始發(fā)送郵件,但老大為什么把這兩個(gè)弄沖突了?
筆者的解決辦法是:
開發(fā)期不要導(dǎo)包,運(yùn)行期將javamail1.4壓縮文件中的mail.jar包放入到tomcat\lib目錄下,這樣完全可以通過開發(fā)和運(yùn)行。若要做單元測(cè)試則新開一個(gè)Java Project,注意,不是web工程,此時(shí)可以將javamail1.4壓縮包中的mail.jar放入到工程的classpath下。?
轉(zhuǎn)載于:https://www.cnblogs.com/yht520/archive/2012/10/01/3589671.html
總結(jié)
以上是生活随笔為你收集整理的利用Spring框架封装的JavaMail现实同步或异步邮件发送的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中文字符读写
- 下一篇: ios 跳转到某 app 的评价区域、由