最近剛完成一個任務-付款提醒郵件的發送,對於java郵件的發送有了更深入的認識,的確java提供的郵件發送機制的確讓郵件發送這個問題變得靈活而又簡單。而且因爲項目組其餘人負責了短信的發送,巧的是這個郵件發送的藉口與短信發送的藉口都被封裝到了消息發送的藉口,我也順便學習了一下短信發送的原理,呵呵,算是一舉兩得吧。java
那先來講說郵件發送。按照按接口編程的習慣,固然先要定義一個郵件發送的接口,再實現其接口,完成郵件發送Service層的代碼。這個順序我想你們沒什麼反對意見吧。編程
首先我們先定義一個消息發送接口,它是郵件發送與短信發送的上層接口。ide
/** * 功能: 系統消息發送服務 <p> * 用法: * @version 1.0 */ public interface MessageService { /** * 根據消息模板表中的消息編號取得消息模板,填充,發送 * * @param bmtCode 消息模板表中的消息編號 * @param params 填充模板內容的參數 * @param to 消息的接收人 * @throws CheckException 模板不存在,或是發送消息出現異常 */ public void sendMessage(String bmtCode,Map params, String... to) throws CheckException; }
再來定義一下郵件發送器接口,因爲我這裏是經過velocity模板發送郵件的,因此以下定義了接口:學習
/** * 郵件發送器 * @usage */ public interface TempletEmailSender { /** * @param from 發件人郵箱 * @param to 收件人郵箱 * @param subject 郵件主題 * @param templet 模板 * @param paramMap 模板參數信息 * @throws CheckException */ public void sendEmailByTemplet(String from, String to, String subject,String templet, Map<String, Object> paramMap) throws CheckException; /** * @param from 發件人郵箱 * @param to 收件人郵箱(多個) * @param subject 郵件主題 * @param templet 模板 * @param paramMap 模板參數信息 * @throws CheckException */ public void sendEmailByTemplet(String from, String[] to, String subject,String templet, Map<String, Object> paramMap) throws CheckException; /** * @param mail 郵件對象 * @param templet 模板 * @param paramMap 模板參數信息 * @throws CheckException */ public void sendEmailByTemplet(Mail mail,String templet, Map<String, Object> paramMap) throws CheckException; }
接着實現郵件發送的接口:測試
/** * 郵件發送器默認實現 */ public class TempletEmailSenderImpl implements TempletEmailSender{ @Autowired @Qualifier("emailSenderImpl_commons") private EmailSender emailSender; @Override public void sendEmailByTemplet(String from, String to,String subject, String templet, Map<String, Object> paramMap) throws CheckException { sendEmailByTemplet(from, new String[]{to}, subject, templet, paramMap); } @Override public void sendEmailByTemplet(String from, String[] to, String subject, String templet, Map<String, Object> paramMap) throws CheckException { // 解析模板 String content ; try { content = VelocityParserUtil.getInstance().parseVelocityTemplate(templet, paramMap); } catch (Throwable t) { throw new CheckException(t); } emailSender.sendEmail(from, to, subject, content); }
你們看到了上面的實現裏注入了EmailSender,它也是一個接口,它的實現裏注入了JavaMail提供的郵件發送接口。定義了兩層是爲了區分有模板的發送和無模板的發送。咱們來看看它是什麼樣的:this
/** * 郵件發送器 * @usage */ public interface EmailSender { /** * 發送郵件 * @param from 發件人 * @param to 收件人 * @param subject 郵件主題 * @param mailBody 郵件內容 * @throws CheckException 參數校驗失敗或發送郵件失敗時,拋出此異常 */ void sendEmail(String from, String to, String subject, String mailBody) throws CheckException; /** * 發送郵件 * @param from 發件人 * @param to 多個收件人 * @param subject 郵件主題 * @param mailBody 郵件內容 * @throws CheckException 參數校驗失敗或發送郵件失敗時,拋出此異常 */ void sendEmail(String from, String[] to, String subject, String mailBody) throws CheckException; /** * 發送郵件 * @param mail 郵件 * @throws CheckException 參數校驗失敗或發送郵件失敗時,拋出此異常 */ void sendEmail(Mail mail) throws CheckException; }
接着實現這個EmailSender接口:lua
/** * JAVA MAIL實現 */ public class EmailSenderImpl implements EmailSender,InitializingBean{ /** * Logger for this class */ private static final Logger logger = Logger.getLogger(EmailSenderImpl.class); @Autowired private ConfigService configService; private JavaMailSenderImpl sender; // 實際的發送實現 @Override public void sendEmail(String from, String to, String subject, String mailBody) throws CheckException { sendEmail(from, new String[]{to}, subject, mailBody); } @Override public void sendEmail(String from, String[] to, String subject, String mailBody) throws CheckException { // 構造MAIL對象 Mail mail = new Mail(); mail.setFrom(from); mail.setTo(to); mail.setSubject(subject); mail.setContent(mailBody); sendEmail(mail); } @Override public void sendEmail(Mail mail) throws CheckException { // 檢查必要參數 if (mail == null ){ throw new CheckException("mail can not be null."); } if (ArrayUtils.isEmpty(mail.getTo())){ throw new CheckException("收件人不能爲空"); } MimeMessageHelper helper = null; try { helper = new MimeMessageHelper(sender.createMimeMessage(), true, "UTF-8"); // 發件人 if (mail.getFrom() != null) { if (mail.getFromName() == null) { helper.setFrom(mail.getFrom()); } else { helper.setFrom(mail.getFrom(), mail.getFromName()); } } // 收件人 helper.setTo(mail.getTo()); // 抄送人 if (mail.getCc() != null) { helper.setCc(mail.getCc()); } // 密送人 if (mail.getBcc() != null) { helper.setBcc(mail.getBcc()); } // 郵件主題 helper.setSubject(mail.getSubject()); // 郵件內容 helper.setText(mail.getContent(), mail.isHtmlFormat()); // 附件 if (mail.getAttachments() != null) { for ( MailAttachment attachment : mail.getAttachments()) { helper.addAttachment(attachment.getFileName(),attachment.getFile()); } } // 發送時間 helper.setSentDate(new Date()); } catch (UnsupportedEncodingException e) { logger.error("sendEmail(Mail)", e); throw new CheckException(e) ; } catch (MessagingException e) { logger.error("sendEmail(Mail)", e); throw new CheckException(e) ; } // 發送 try { sender.send(helper.getMimeMessage()); } catch (MailException e) { logger.error("sendEmail(Mail)", e); throw new CheckException(e) ; } } @Override public void afterPropertiesSet() throws Exception { sender = new JavaMailSenderImpl(); // configService讀出參數 Properties pros = new Properties(); pros.setProperty("mail.smtp.user", configService.getConfig(BasePropertyID.MAIL_SMTP_USER_ID)); pros.setProperty("mail.smtp.host", configService.getConfig(BasePropertyID.MAIL_SMTP_HOST_ID)); pros.setProperty("mail.smtp.port", configService.getConfig(BasePropertyID.MAIL_SMTP_PORT_ID)); pros.setProperty("mail.smtp.connectiontimeout", configService.getConfig(BasePropertyID.MAIL_SMTP_CONNECTIONTIMEOUT_ID)); pros.setProperty("mail.smtp.timeout", configService.getConfig(BasePropertyID.MAIL_SMTP_TIMEOUT_ID)); pros.setProperty("mail.smtp.from", configService.getConfig(BasePropertyID.MAIL_SMTP_FROM_ID)); pros.setProperty("mail.smtp.auth", configService.getConfig(BasePropertyID.MAIL_SMTP_AUTH_ID)); sender.setJavaMailProperties(pros); sender.setPassword(configService.getConfig(BasePropertyID.MAIL_SMTP_PASSWORD_ID)); } public ConfigService getConfigService() { return configService; } public void setConfigService(ConfigService configService) { this.configService = configService; }
O(∩_∩)O~你們又注意到了 這個接口實現裏又注入了一個接口ConfigService 它是去讀取郵件發送的相關配置信息,如上所示:spa
// configService讀出參數
Properties pros = new Properties();debug
pros.setProperty("mail.smtp.user", configService.getConfig(BasePropertyID.MAIL_SMTP_USER_ID));
pros.setProperty("mail.smtp.host", configService.getConfig(BasePropertyID.MAIL_SMTP_HOST_ID));
pros.setProperty("mail.smtp.port", configService.getConfig(BasePropertyID.MAIL_SMTP_PORT_ID));
pros.setProperty("mail.smtp.connectiontimeout", configService.getConfig(BasePropertyID.MAIL_SMTP_CONNECTIONTIMEOUT_ID));
pros.setProperty("mail.smtp.timeout", configService.getConfig(BasePropertyID.MAIL_SMTP_TIMEOUT_ID));
pros.setProperty("mail.smtp.from", configService.getConfig(BasePropertyID.MAIL_SMTP_FROM_ID));
pros.setProperty("mail.smtp.auth", configService.getConfig(BasePropertyID.MAIL_SMTP_AUTH_ID));
sender.setJavaMailProperties(pros);
sender.setPassword(configService.getConfig(BasePropertyID.MAIL_SMTP_PASSWORD_ID));code
並且因爲涉及到參數的數據成員較多,就將他們一塊兒封裝到了Mail類:
/** * 功能: 封裝郵件對象 <p> * 用法: * @version 1.0 */ public class Mail { /** * 發件人 */ private String from; /** * 發件人(顯示) */ private String fromName; /** * 收件人 */ private String[] to; /** * 抄送 */ private String[] cc; /** * 祕密抄送 */ private String[] bcc; /** * 郵件主題 */ private String subject; /** * 郵件內容 */ private String content; /** * 附件 */ private MailAttachment[] attachments; /** * 是否以HTML格式發送 */ boolean isHtmlFormat = true; //getter與setter方法省略 }
好了 整個接口都實現了,其實排除了你發送郵件提供給郵件發送接口的幾個參數,剩下的也就是調用java提供的郵件發送的API和一些郵件發送必備的配置信息,必不是很難懂吧。你們確定注意到了郵件發送接口的velocity模板解析方法:
content = VelocityParserUtil.getInstance().parseVelocityTemplate(templet, paramMap);
它具體的實現以下所示:
/** * 功能:解析velocity模板 * <p> * 用法: * * @version 1.0 */ public class VelocityParserUtil { /** * Logger for this class */ private static final Logger logger = Logger.getLogger(VelocityParserUtil.class); private static VelocityParserUtil instance = new VelocityParserUtil(); private VelocityEngine engine = null; private VelocityParserUtil() { // init engine engine = new VelocityEngine(); try { engine.init(); } catch (Exception e) { logger.warn("VelocityParserUtil() - exception ignored", e); //$NON-NLS-1$ } } /** * 返回VelocityParserUtil實例 * @return */ public static VelocityParserUtil getInstance() { return instance; } /** * 解析velocity模板 * @param vtl * @param model * @return String * @throws ParseErrorException * @throws MethodInvocationException * @throws ResourceNotFoundException * @throws IOException */ public String parseVelocityTemplate(String vtl, Map model) throws ParseErrorException, MethodInvocationException, ResourceNotFoundException, IOException { if (logger.isDebugEnabled()) { logger.debug("parseVelocityTemplate(String, Map) - start"); //$NON-NLS-1$ } VelocityContext velocityContext = new VelocityContext(model); StringWriter result = new StringWriter(); engine.evaluate(velocityContext, result, null, vtl); String returnString = result.toString(); if (logger.isDebugEnabled()) { logger.debug("parseVelocityTemplate(String, Map) - end"); //$NON-NLS-1$ } return returnString; } }
咱們會在其餘的Service中去調用郵件發送的接口,只須要在業務層裏構造好郵件發送的接口所需參數,咱們的郵件就能夠發送出去了。還有一點請你們注意,我這裏主要強調的是運用velocity模板發送郵件,接口所須要的參數templat你們不要誤解爲velocity模板的文件名,它其實velocity文件的文件流,是一個已經被讀入的字符串。你們能夠參考一下測試用例,大體能夠明白是怎麼回事了。
public class TempletEmailSenderTest extends BaseTestBaseForJUnit4{ @Autowired @Qualifier("templetEmailSenderImpl_commons") private TempletEmailSender sender; // @Test public void sendEmailByTemplet() throws CheckException{ String templet = "$!{user}提醒您付款$!{amount}元"; Map paramMap = new HashMap(); paramMap.put("user", "吳欣"); paramMap.put("amount", "99.9"); sender.sendEmailByTemplet("wuxin.wystan@snda.com", "wuxin.wystan@snda.com", "模板郵件", templet, paramMap); }
整個郵件的發送你們是否明瞭了呢,呵呵!
後面咱們會將這個接口向上抽象,爲了實現咱們短信發送的實現,下一篇敬請期待哦