前些天幫一個朋友作網站,全站都是靜態頁面,惟一須要用到後端開發的是他須要一個留言板。傳統的留言板通常都是提交後保存到數據庫,而後提供一個後臺的留言列表給管理人員看,我嫌麻煩,就決定留言提交到後臺直接發郵件出去,這樣就不用開發後臺頁面了,他也不須要登陸一個什麼後臺才能看留言,一箭雙鵰,豈不美哉。java
spring boot發郵件仍是挺簡單的,首先把發郵件的start加到pom裏面:程序員
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>複製代碼
而後在application.properties裏面配置好關於發郵件的參數spring
spring.mail.host=smtp.163.com
spring.mail.port=25
spring.mail.username=yourmail@163.com
spring.mail.password=yourpassword複製代碼
其中spring.mail.host和spring.mail.username是一一對應的,哪裏的郵箱就要用哪裏的smtp服務器數據庫
而後我寫了一個controller來接收留言板的內容:後端
@Value("${spring.mail.username}")
private String fromMail;
@Autowired
private JavaMailSender mailSender;
@RequestMapping(value = "/getNote", method = RequestMethod.POST)
public String getNote(Note note) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper;
try {
helper = new MimeMessageHelper(mimeMessage, true);
//發件人
helper.setFrom(fromMail,note.yourName);
//收件人(留言內容最終發往的郵箱地址)
helper.setTo("recieve@mail.com");
//標題
helper.setSubject(note.yourSubject);
//文本
helper.setText("from email:"+note.yourEmail+"\n"+note.yourMessage);
mailSender.send(mimeMessage);
} catch (MessagingException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "redirect:return.htm";
}複製代碼
public class Note {
String yourName;
String yourEmail;
String yourSubject;
String yourMessage;
//getter,setter省略
}複製代碼
不少時候,按照上面的方法發郵件會出現下面的錯誤:bash
javax.mail.AuthenticationFailedException: 535 Error: authentication failed複製代碼
其中的一個緣由是郵件服務器使用了受權碼登陸方式,也就是第三方登陸不能直接使用帳號密碼,而須要使用一種受權碼的方式,好比上面的163.com郵箱,就能夠設置受權碼登陸,設置界面以下:服務器
QQ郵箱更是默認就須要用受權碼登陸,QQ郵箱的受權登陸操做文檔請看這裏app
使用受權碼登陸後,須要把以前application.properties裏面spring.mail.password的內容從密碼換成受權碼,就可以正常發郵件了。spring-boot
咱們查下JavaMailSender的代碼就知道,它其實背景比較複雜。首先它繼承了org.springframework.mail.MailSender接口。網站
public interface JavaMailSender extends MailSender {
...
}複製代碼
而它自己也是一個接口,實現類只有一個,JavaMailSenderImpl
咱們再來翻JavaMailSenderImpl的代碼,發現spring並無本身來作發送郵件的功能,而是直接用了java自身的郵件發送功能,核心是這一段
protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
Map<Object, Exception> failedMessages = new LinkedHashMap<>();
Transport transport = null;
try {
for (int i = 0; i < mimeMessages.length; i++) {
// Check transport connection first...
if (transport == null || !transport.isConnected()) {
if (transport != null) {
try {
transport.close();
}
catch (Exception ex) {
// Ignore - we're reconnecting anyway } transport = null; } try { transport = connectTransport(); } catch (AuthenticationFailedException ex) { throw new MailAuthenticationException(ex); } catch (Exception ex) { // Effectively, all remaining messages failed... for (int j = i; j < mimeMessages.length; j++) { Object original = (originalMessages != null ? originalMessages[j] : mimeMessages[j]); failedMessages.put(original, ex); } throw new MailSendException("Mail server connection failed", ex, failedMessages); } } // Send message via current transport... MimeMessage mimeMessage = mimeMessages[i]; try { if (mimeMessage.getSentDate() == null) { mimeMessage.setSentDate(new Date()); } String messageId = mimeMessage.getMessageID(); mimeMessage.saveChanges(); if (messageId != null) { // Preserve explicitly specified message id... mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId); } Address[] addresses = mimeMessage.getAllRecipients(); transport.sendMessage(mimeMessage, (addresses != null ? addresses : new Address[0])); } catch (Exception ex) { Object original = (originalMessages != null ? originalMessages[i] : mimeMessage); failedMessages.put(original, ex); } } } finally { try { if (transport != null) { transport.close(); } } catch (Exception ex) { if (!failedMessages.isEmpty()) { throw new MailSendException("Failed to close server connection after message failures", ex, failedMessages); } else { throw new MailSendException("Failed to close server connection after message sending", ex); } } } if (!failedMessages.isEmpty()) { throw new MailSendException(failedMessages); } }複製代碼
doSend方法中調用的核心類就是Transport類,這個類的包名是javax.mail。spring不愧是集成大師,java自帶的mail功能通過spring的標準化包裝就成了spring自身功能的一部分,再經過spring boot的包裝,用starter的方式再次作簡化,咱們就可以直接經過極簡的方式使用了。
固然,簡化的方法多種多樣,另外的一種形式的包裝就是使用helper類的方法,spring使用的就是MimeMessageHelper。在javax.mail在處理郵件的方式上,使用的是分而治之的辦法,不一樣的類處理不一樣的問題,因此看到不少的類在處理各類問題和狀況。
這種作法在實現功能上是很好的,把一個複雜的問題分解成若干個小問題,分別實現。但對使用的開發人員就談不上友好了,容易出現如下幾個問題:
針對上述狀況,spring經過MimeMessageHelper,把幾乎全部郵件發送須要處理的問題就集中到了這個類裏面,使用方便又好找。下面是這個類全部的方法。
這個類不可謂不復雜,基本上涵蓋了全部發郵件方面的功能,但由於都集成在一個類裏面,很是方便好用,能夠說是一個對程序員友好的典範了,值得你們在開發時作借鑑。