電子郵件

本節說明如何使用 Spring 框架傳送電子郵件。

函式庫依賴

為了使用 Spring 框架的電子郵件支援,您的應用程式的 classpath 中需要有以下 JAR 檔

這個函式庫可在網路上免費取得 — 例如,在 Maven Central 中以 com.sun.mail:jakarta.mail 提供。請務必使用最新的 2.x 版本(使用 jakarta.mail 套件命名空間),而不是 Jakarta Mail 1.6.x(使用 javax.mail 套件命名空間)。

Spring 框架提供了一個有用的工具函式庫,用於傳送電子郵件,它可以讓您不必處理底層郵件系統的細節,並代表客户端負責低階資源處理。

org.springframework.mail 套件是 Spring 框架電子郵件支援的根層級套件。用於傳送電子郵件的核心介面是 MailSender 介面。一個簡單的值物件,封裝了簡單郵件的屬性,例如 fromto(以及許多其他屬性)是 SimpleMailMessage 類別。此套件還包含一個檢查型例外階層,它提供了比低階郵件系統例外更高的抽象層級,根例外是 MailException。請參閱 javadoc 以取得有關豐富的郵件例外階層的更多資訊。

org.springframework.mail.javamail.JavaMailSender 介面將專門的 JavaMail 功能(例如 MIME 訊息支援)新增到 MailSender 介面(它從中繼承)。JavaMailSender 也提供了一個稱為 org.springframework.mail.javamail.MimeMessagePreparator 的回呼介面,用於準備 MimeMessage

用法

假設我們有一個名為 OrderManager 的業務介面,如下列範例所示

  • Java

  • Kotlin

public interface OrderManager {

	void placeOrder(Order order);
}
interface OrderManager {

	fun placeOrder(order: Order)
}

進一步假設我們有一個需求,指出需要產生一封包含訂單號碼的電子郵件訊息,並傳送給下達相關訂單的客戶。

基本 MailSenderSimpleMailMessage 用法

以下範例示範如何在有人下訂單時使用 MailSenderSimpleMailMessage 傳送電子郵件

  • Java

  • Kotlin

public class SimpleOrderManager implements OrderManager {

	private MailSender mailSender;
	private SimpleMailMessage templateMessage;

	public void setMailSender(MailSender mailSender) {
		this.mailSender = mailSender;
	}

	public void setTemplateMessage(SimpleMailMessage templateMessage) {
		this.templateMessage = templateMessage;
	}

	@Override
	public void placeOrder(Order order) {

		// Do the business calculations...

		// Call the collaborators to persist the order...

		// Create a thread-safe "copy" of the template message and customize it
		SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
		msg.setTo(order.getCustomer().getEmailAddress());
		msg.setText(
				"Dear " + order.getCustomer().getFirstName()
						+ order.getCustomer().getLastName()
						+ ", thank you for placing order. Your order number is "
						+ order.getOrderNumber());
		try {
			this.mailSender.send(msg);
		}
		catch (MailException ex) {
			// simply log it and go on...
			System.err.println(ex.getMessage());
		}
	}

}
class SimpleOrderManager : OrderManager {

	lateinit var mailSender: MailSender
	lateinit var templateMessage: SimpleMailMessage

	override fun placeOrder(order: Order) {
		// Do the business calculations...

		// Call the collaborators to persist the order...

		// Create a thread-safe "copy" of the template message and customize it

		val msg = SimpleMailMessage(this.templateMessage)
		msg.setTo(order.customer.emailAddress)
		msg.text = ("Dear " + order.customer.firstName
				+ order.customer.lastName
				+ ", thank you for placing order. Your order number is "
				+ order.orderNumber)
		try {
			mailSender.send(msg)
		} catch (ex: MailException) {
			// simply log it and go on...
			System.err.println(ex.message)
		}
	}
}

以下範例顯示了前面程式碼的 Bean 定義

  • Java

  • Kotlin

  • Xml

@Bean
JavaMailSender mailSender() {
	JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
	mailSender.setHost("mail.mycompany.example");
	return mailSender;
}

@Bean // this is a template message that we can pre-load with default state
SimpleMailMessage templateMessage() {
	SimpleMailMessage message = new SimpleMailMessage();
	message.setFrom("[email protected]");
	message.setSubject("Your order");
	return message;
}

@Bean
SimpleOrderManager orderManager(JavaMailSender mailSender, SimpleMailMessage templateMessage) {
	SimpleOrderManager orderManager = new SimpleOrderManager();
	orderManager.setMailSender(mailSender);
	orderManager.setTemplateMessage(templateMessage);
	return orderManager;
}
@Bean
fun mailSender(): JavaMailSender {
	return JavaMailSenderImpl().apply {
		host = "mail.mycompany.example"
	}
}

@Bean // this is a template message that we can pre-load with default state
fun templateMessage() = SimpleMailMessage().apply {
	from = "[email protected]"
	subject = "Your order"
}


@Bean
fun orderManager(javaMailSender: JavaMailSender, simpleTemplateMessage: SimpleMailMessage) = SimpleOrderManager().apply {
	mailSender = javaMailSender
	templateMessage = simpleTemplateMessage
}
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
	<property name="host" value="mail.mycompany.example"/>
</bean>

<!-- this is a template message that we can pre-load with default state -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
	<property name="from" value="[email protected]"/>
	<property name="subject" value="Your order"/>
</bean>

<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
	<property name="mailSender" ref="mailSender"/>
	<property name="templateMessage" ref="templateMessage"/>
</bean>

使用 JavaMailSenderMimeMessagePreparator

本節說明 OrderManager 的另一個實作,它使用 MimeMessagePreparator 回呼介面。在以下範例中,mailSender 屬性的類型為 JavaMailSender,因此我們可以使用 JavaMail MimeMessage 類別

import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;

public class SimpleOrderManager implements OrderManager {

	private JavaMailSender mailSender;

	public void setMailSender(JavaMailSender mailSender) {
		this.mailSender = mailSender;
	}

	public void placeOrder(final Order order) {
		// Do the business calculations...
		// Call the collaborators to persist the order...

		MimeMessagePreparator preparator = new MimeMessagePreparator() {
			public void prepare(MimeMessage mimeMessage) throws Exception {
				mimeMessage.setRecipient(Message.RecipientType.TO,
						new InternetAddress(order.getCustomer().getEmailAddress()));
				mimeMessage.setFrom(new InternetAddress("[email protected]"));
				mimeMessage.setText("Dear " + order.getCustomer().getFirstName() + " " +
						order.getCustomer().getLastName() + ", thanks for your order. " +
						"Your order number is " + order.getOrderNumber() + ".");
			}
		};

		try {
			this.mailSender.send(preparator);
		}
		catch (MailException ex) {
			// simply log it and go on...
			System.err.println(ex.getMessage());
		}
	}

}
郵件程式碼是一個跨領域關注點,很可能是一個重構到 自訂 Spring AOP 切面 的候選者,然後可以在 OrderManager 目標上的適當連接點執行它。

Spring 框架的郵件支援隨附標準 JavaMail 實作。請參閱相關的 javadoc 以取得更多資訊。

使用 JavaMail MimeMessageHelper

在處理 JavaMail 訊息時,org.springframework.mail.javamail.MimeMessageHelper 類別非常方便,它可以讓您不必使用冗長的 JavaMail API。使用 MimeMessageHelper,可以非常輕鬆地建立 MimeMessage,如下列範例所示

// of course you would use DI in any real-world cases
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("[email protected]");
helper.setText("Thank you for ordering!");

sender.send(message);

傳送附件和內嵌資源

Multipart 電子郵件訊息允許附件和內嵌資源。內嵌資源的範例包括您想要在訊息中使用的影像或樣式表,但不希望顯示為附件。

附件

以下範例示範如何使用 MimeMessageHelper 傳送一封帶有單一 JPEG 影像附件的電子郵件

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("[email protected]");

helper.setText("Check out this image!");

// let's attach the infamous windows Sample file (this time copied to c:/)
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addAttachment("CoolImage.jpg", file);

sender.send(message);

內嵌資源

以下範例示範如何使用 MimeMessageHelper 傳送一封帶有內嵌影像的電子郵件

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("[email protected]");

// use the true flag to indicate the text included is HTML
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);

// let's include the infamous windows Sample file (this time copied to c:/)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);

sender.send(message);
內嵌資源透過使用指定的 Content-ID(在上面的範例中為 identifier1234)新增到 MimeMessage。您新增文字和資源的順序非常重要。請務必先新增文字,然後再新增資源。如果您以相反的方式執行,則它將無法運作。

使用範本函式庫建立電子郵件內容

先前章節中顯示的範例中的程式碼明確地建立了電子郵件訊息的內容,方法是使用方法呼叫,例如 message.setText(..)。這對於簡單的情況來說是可以的,並且在上述範例的上下文中是可以的,因為其目的是向您展示 API 的最基本知識。

但是,在典型的企業應用程式中,開發人員通常不會使用先前顯示的方法來建立電子郵件訊息的內容,原因有很多

  • 在 Java 程式碼中建立基於 HTML 的電子郵件內容既繁瑣又容易出錯。

  • 顯示邏輯和業務邏輯之間沒有明確的分隔。

  • 變更電子郵件內容的顯示結構需要編寫 Java 程式碼、重新編譯、重新部署等等。

通常,為了解決這些問題而採用的方法是使用範本函式庫(例如 FreeMarker)來定義電子郵件內容的顯示結構。這使您的程式碼僅負責建立要在電子郵件範本中呈現的資料並傳送電子郵件。當您的電子郵件訊息的內容變得稍微複雜時,這絕對是一種最佳實務,並且藉助 Spring 框架對 FreeMarker 的支援類別,這變得非常容易做到。