電子郵件
本節描述瞭如何使用 Spring Framework 傳送電子郵件。
Spring Framework 提供了一個有用的工具庫來發送電子郵件,它使您無需關注底層郵件系統的具體細節,並代表客戶端負責低階資源處理。
org.springframework.mail
包是 Spring Framework 電子郵件支援的根級別包。傳送電子郵件的核心介面是 MailSender
介面。一個封裝簡單郵件屬性(如 from
和 to
以及許多其他屬性)的簡單值物件是 SimpleMailMessage
類。此包還包含一個受檢異常層次結構,它在較低級別的郵件系統異常之上提供了更高級別的抽象,其根異常是 MailException
。有關豐富的郵件異常層次結構的更多資訊,請參見 javadoc。
org.springframework.mail.javamail.JavaMailSender
介面為 MailSender
介面(它繼承自該介面)添加了專門的 JavaMail 特性,例如 MIME 訊息支援。JavaMailSender
還提供了一個名為 org.springframework.mail.javamail.MimeMessagePreparator
的回撥介面,用於準備 MimeMessage
。
用法
假設我們有一個業務介面,名為 OrderManager
,如下例所示:
-
Java
-
Kotlin
public interface OrderManager {
void placeOrder(Order order);
}
interface OrderManager {
fun placeOrder(order: Order)
}
進一步假設我們有一個需求,即需要生成一封包含訂單號的電子郵件,併發送給下了相關訂單的客戶。
MailSender
和 SimpleMailMessage
的基本用法
以下示例展示瞭如何在客戶下訂單時使用 MailSender
和 SimpleMailMessage
傳送電子郵件:
-
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>
使用 JavaMailSender
和 MimeMessagePreparator
本節描述了 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 Framework 的郵件支援附帶標準的 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 Framework 對 FreeMarker 的支援類,實現起來非常容易。