從spring boot發郵件聊到開發的友好性

来源:https://www.cnblogs.com/wphmoon/archive/2019/10/09/11644937.html
-Advertisement-
Play Games

前些天幫一個朋友做網站,全站都是靜態頁面,唯一需要用到後端開發的是他需要一個留言板。傳統的留言板一般都是提交後保存到資料庫,然後提供一個後臺的留言列表給管理人員看,我嫌麻煩,就決定留言提交到後臺直接發郵件出去,這樣就不用開發後臺頁面了,他也不需要登錄一個什麼後臺才能看留言,兩全其美,豈不美哉。 1、 ...


   前些天幫一個朋友做網站,全站都是靜態頁面,唯一需要用到後端開發的是他需要一個留言板。傳統的留言板一般都是提交後保存到資料庫,然後提供一個後臺的留言列表給管理人員看,我嫌麻煩,就決定留言提交到後臺直接發郵件出去,這樣就不用開發後臺頁面了,他也不需要登錄一個什麼後臺才能看留言,兩全其美,豈不美哉。

1、最簡版spring boot發郵件

spring boot發郵件還是挺簡單的,首先把發郵件的start加到pom裡面:

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>

然後在application.properties裡面配置好關於發郵件的參數

spring.mail.host=smtp.163.com
spring.mail.port=25
[email protected]
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("[email protected]");
	        //標題
	        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省略
}
  • note對象是留言板的內容
  • JavaMailSender和MimeMessageHelper是官方推薦的好基友,一般配套使用
  • fromMail就是在application.properties裡面配置的spring.mail.username屬性,也就是發郵件的一方,用helper.setTo(...)配置
  • 額外說一下,由於填留言板時一般也會留郵件地址,但那個郵件地址和這裡的任何一個設置郵件的地方毫無關係,只需要在郵件的文本裡面做記錄就好了,我之前就弱智了一把,把留言板裡面的郵件地址填到helper.setTo(...),結果浪費了我半個小時查這種bug,真是一言難盡...

2、授權碼發郵件

     很多時候,按照上面的方法發郵件會出現下麵的錯誤:

javax.mail.AuthenticationFailedException: 535 Error: authentication failed

其中的一個原因是郵件伺服器使用了授權碼登錄方式,也就是第三方登錄不能直接使用賬號密碼,而需要使用一種授權碼的方式,比如上面的163.com郵箱,就可以設置授權碼登錄,設置界面如下:

QQ郵箱更是預設就需要用授權碼登錄,QQ郵箱的授權登錄操作文檔請看這裡

使用授權碼登錄後,需要把之前application.properties裡面spring.mail.password的內容從密碼換成授權碼,就能夠正常發郵件了。

3、簡單聊下JavaMailSender和MimeMessageHelper

我們查下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,把幾乎所有郵件發送需要處理的問題就集中到了這個類裡面,使用方便又好找。下麵是這個類所有的方法。

這個類不可謂不複雜,基本上涵蓋了所有發郵件方面的功能,但因為都集成在一個類裡面,非常方便好用,可以說是一個對程式員友好的典範了,值得大家在開發時做借鑒。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ① 向資料庫發送SQL查詢語句 首先使用Statement聲明一個SQL語句對象,然後讓已創建的連接對象con調用方法createStatement()創建SQL語句對象。 Statement sql = con.createStatement(); Connection con = null; S ...
  • 一、主方法註意 每一個類都可以編寫一個主方法,但是一般情況下,一個系統只有一個入口,所以主方法一般寫一個 二、Myeclipse的使用 1.在workspace中工作區中有一個文件夾.metadata:在該文件夾中存儲了當前MyEclipse的工作狀態 2.將.metadata文件夾刪除之後,下一次 ...
  • 本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫里查看 https://github.com/h2pl/Java Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人博客: www.how2playlife.com <! more 從JVM結構開始 ...
  • 本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫里查看 https://github.com/h2pl/Java Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人博客: www.how2playlife.com <! more 本文參考 htt ...
  • 本文的記憶體模型只寫虛擬機記憶體模型,物理機的不予描述。 Java記憶體模型 在Java中,虛擬機將運行時區域分成6中,如下圖: 由於類和方法的信息難以確定,不好設定大小,太大則影響年老代,太小容易記憶體溢出。 GC不好處理,回收效率低下,調優困難。 在上面的6種類型中,前三種是線程私有的,也就是說裡面存放 ...
  • 本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫里查看 https://github.com/h2pl/Java Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人博客: www.how2playlife.com <! more 這是一個Java ...
  • 類型關係 Scala 支持在泛型類上使用型變註釋,用來表示覆雜類型、組合類型的子類型關係間的相關性 協變 ,變化方向相同,通常用在生產 假設 , 對於 ,則 也可看做 由於 Scala 標準庫中不可變 的定義為 ,因此 是 的子類型, 也是 的子類型,所以可直接將他們當作 使用。 逆變 ,變化方向相 ...
  • 前言 說真的,平常看源碼都是自己看完自己懂,很少有寫出來的衝動。 但是在寫演算法的時候,經常用到java中各種集合,其中也比較常用到remove方法。 remove有重載函數,分別傳入參數是索引index或者數據Object(指定泛型後自動轉換),如果指定泛型是其他數據類型還好,但是指定的是Integ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...