JavaMail是SUN提供給開發人員在應用程序中實現“郵件發送和接收功能”的一套標準開發類庫,支持常用的郵件協議,如SMTP、POP3、IMAP。
SMTP:簡單郵件傳輸協議,用於發送電子郵件的傳輸協議;
POP3:郵局協議,用於接收電子郵件的標準協議;
IMAP:互聯網消息訪問協議,是POP3的替代協議。
(這三種協議都有對應SSL加密傳輸的協議,分別是SMTPS,POP3S和IMAP3)。
MIME:多用途因特網郵件擴展別準,它不是郵件傳輸消息,但對傳輸內容的消息、附件及其他的內容定義了格式。
開發人員使用JavaMail編寫郵件程序時,無需考慮底層的通信細節(Socket),JavaMail也提供了能夠創建出各種複雜MIME格式的郵件內容的API。使用JavaMail,我們可以實現類似OutLook、FoxMail的軟件。雖然JavaMail(僅支持JDK4及以上)也是Java的API之一,但是卻沒有直接加入到JDK中,所以我們需要另行下載。另外,JavaMail依賴JAF(JavaBeans Activation Framework)來處理不是純文本的郵件內容(如MIME多用途互聯網郵件擴展、URL頁面和文件附件等內容),JAF在Java6之後已經合併到JDK中,而JDK5之前需要另外下載JAF的類庫。下載地址如下:
JavaMail spec:http://www.oracle.com/technetwork/java/javamail-1-149769.pdf
JavaMail下載好後,我們來看一下其主要內容:
README.txt:整體介紹JavaMail,需要看一下 docs/javadocs:The JavaMail API javadocs,需要看一下 mail.jar:包括JavaMail API和所有service providers,大部分用戶只需要該jar包 lib/mailapi.jar :只有JavaMail API lib/imap.jar:The IMAP service provider lib/smtp.jar:The SMTP service provider lib/pop3.jar:The POP3 service provider lib/dsn.jar:multipart/report DSN message support demo:demo示例,簡單瞭解,有需要再看
JavaMail包含兩部分內容,一部分是JavaMail API,定義了一組平臺無關、獨立於通訊協議的郵件程序框架,該部分稱爲應用級接口,也就是供我們調用的部分,另一部分是service provider,該部分使用特定的協議語言來實現第一部分定義的抽象類和接口,這些協議包括:SMTP、NNTP、POP3、IMAP,如果讓JavaMail與郵件服務器通信,就需要相應的協議支持,該部分稱爲服務提供者接口,也就是JavaMail自身需要的協議支持。在使用JavaMail時,通常我們只需將mail.jar放在classpath下使用,它包含了JavaMail API部分和SUN自己實現的service provider部分。可能也有特殊的時候,我們應用程序中需要自己實現service provider部分,那我們只需要mailapi.jar。下面通過幾個類來簡單認識下JavaMail API:
javax.mail.Session:上下文環境信息,如服務器的主機名、端口號、協議名稱等 javax.mail.Message:郵件模型,發送郵件和接收郵件的媒介,封裝了郵件的信息,如發件人、收件人、郵件標題、郵件內容等 javax.mail.Transport:連接郵件SMTP服務器,發送郵件 javax.mail.Store:連接郵件POP3、IMAP服務器,收取郵件
通過這些類,最終就可以實現收發郵件,一個發送郵件的簡單示例:
public class JavaMailTest1 { public static void main(String[] args) throws MessagingException { Properties props = new Properties(); // 開啓debug調試 props.setProperty("mail.debug", "true"); // 發送服務器需要身份驗證 props.setProperty("mail.smtp.auth", "true"); // 設置郵件服務器主機名 props.setProperty("mail.host", "smtp.163.com"); // 發送郵件協議名稱 props.setProperty("mail.transport.protocol", "smtp"); // 設置環境信息 Session session = Session.getInstance(props); // 創建郵件對象 Message msg = new MimeMessage(session); msg.setSubject("JavaMail測試"); // 設置郵件內容 msg.setText("這是一封由JavaMail發送的郵件!"); // 設置發件人 msg.setFrom(new InternetAddress("[email protected]")); Transport transport = session.getTransport(); // 連接郵件服務器 transport.connect("java_mail_001", "javamail"); // 發送郵件 transport.sendMessage(msg, new Address[] {new InternetAddress("[email protected]")}); // 關閉連接 transport.close(); } }
最終運行後,郵件發送成功。由於我們開啓了debug調試,在控制檯可以看到JavaMail和服務器之間的交互信息記錄。
PS:文中示例以及以後的示例中所用的郵箱賬戶均爲在163申請的測試賬戶,分別爲java_mail_001至java_mail_004,密碼均爲javamail。
JavaMail API使用上非常靈活。它對收發郵件進行了高級的抽象,形成了一些關鍵的接口和類,它們構成了程序的基礎,下面分別介紹這些對象:
Properties
屬性對象。由於JavaMail需要和郵件服務器進行通信,這就要求程序提供許多諸如服務器地址、端口、用戶名、密碼等信息,JavaMail通過Properties對象封裝這些屬性信息。例如
Properties props = new Properties(); props.put("mail.smtp.host", "smtp.sina.com.cn"); props.put("mail.smtp.auth", "true");
針對不同的郵件協議,JavaMail規定了服務提供者必須支持一系列屬性,下表是針對SMTP協議的一些常見屬性(屬性值都以String類型進行設置,屬性類型欄僅表示屬性是如何被解析的):
屬性名 | 屬性類型 | 說明 |
mail.stmp.host | String | SMTP服務器地址,如smtp.sina.com.cn |
mail.stmp.port | int | SMTP服務器端口號,默認爲25 |
mail.stmp.auth | boolean | SMTP服務器是否需要用戶認證,默認爲false |
mail.stmp.user | String | SMTP默認的登陸用戶名 |
mail.stmp.from | String | 默認的郵件發送源地址 |
mail.stmp.socketFactory.class | String | socket工廠類類名,通過設置該屬性可以覆蓋提供者默認的實現,必須實現javax.net.SocketFactory接口 |
mail.stmp.socketFactory.port | int | 指定socket工廠類所用的端口號,如果沒有規定,則使用默認的端口號 |
mail.smtp.socketFactory.fallback | boolean | 設置爲true時,當使用指定的socket類創建socket失敗後,將使用java.net.Socket創建socket,默認爲true |
mail.stmp.timeout | int | I/O連接超時時間,單位爲毫秒,默認爲永不超時 |
其他幾個協議也有類似的一系列屬性,如POP3的mail.pop3.host、mail.pop3.port以及IMAP的mail.imap.host、mail.imap.port等。更詳細的信息請查看com.sun.mail.smtp、com.sun.mail.pop3和com.sun.mail.imap這三個包的Javadoc:http://java.sun.com/products/javamail/javadocs/index.html。
Session
會話對象。千萬別以爲這裏的Session向HTTPSession一樣代表真實的交互會話,創建Session對象時,並沒有對應的物理連接,它只不過是一對配置信息的集合。主要作用包括兩個方面:
1)接收各種配置屬性信息:通過Properties對象設置的屬性信息;
2)初始化JavaMail環境:根據JavaMail的配置文件,初始化JavaMail環境,以便通過Session對象創建其他重要類的實例。
JavaMail分爲API和service provider兩部分,API定義了相關接口(eg.:Transport and Store),service provider中實現了這些接口。JavaMail提供的mail.jar/smtp.jar/pop3.jar/imap.jar等jar包的META-INF目錄下,通過javamail.providers或javamail.default.provider、javamail.address.map和javamail.default.address.map文件提供了基本的配置信息,以便session能夠根據這個配置文件家在提供者的實現類。javamail.default.provider文件的配置信息如:
# JavaMail IMAP provider Sun Microsystems, Inc protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc; protocol=imaps; type=store; class=com.sun.mail.imap.IMAPSSLStore; vendor=Sun Microsystems, Inc; # JavaMail SMTP provider Sun Microsystems, Inc protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc; protocol=smtps; type=transport; class=com.sun.mail.smtp.SMTPSSLTransport; vendor=Sun Microsystems, Inc; # JavaMail POP3 provider Sun Microsystems, Inc protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store; vendor=Sun Microsystems, Inc; protocol=pop3s; type=store; class=com.sun.mail.pop3.POP3SSLStore; vendor=Sun Microsystems, Inc;
每一行聲明瞭協議名稱、協議類型、實現類、供應商、版本等信息,如果需要自己實現相應的協議,必須按照該格式配置好,Java Mail API中才能正確的調用到。
Session在加載配置文件時會按照以下優先級順序進行:
1)首先使用<JAVA_HOME>/lib中的javamail.providers;
2)如果1)不存在相應的配置文件,使用類路徑下mail.jar中META-INF目錄下的javamail.providers;
3)如果2)不存在相應的配置文件,使用類路徑下的mail.jar中META-INF目錄下的javamail.default.providers;
所以開發者可以在<JAVA_HOME>/lib目錄下提供配置文件覆蓋mail.jar/META-INF目錄中廠商的配置。但是,一般情況下,我們無須這樣做。
Session用於收集JavaMail運行過程中的環境信息,它可以創建一個單例的對象,也可以每次創建新的對象,Session沒有構造器,但擁有多個靜態工廠方法用於創造實例:
static Session | getDefaultInstance(Properties props) Get the default Session object. |
static Session | getDefaultInstance(Properties props,Authenticator authenticator) Get the default Session object. |
static Session | getInstance(Properties props) Get a new Session object. |
static Session | getInstance(Properties props,Authenticator authenticator) Get a new Session object. |
l static Session getDefaultInstance(Properties props, Authenticator authenticator):當JVM中已經存在默認的Session實例中,直接返回這個實例,否則創建一個新的Session實例,並將其作爲JVM中默認Session實例。這個API很詭異,我們將對它進行詳細的講解。由於這個默認Session實例可以被同一個JVM所有的代碼訪問到,而Session中本身又可能包括密碼、用戶名等敏感信息在內的所有屬性信息,所以後續調用也必須傳入和第一次相同的Authenticator實例,否則將拋出java.lang.SecurityException異常。如果第一次調用時Authenticator入參爲null,則後續調用通過null的Authenticator入參或直接使用getDefaultInstance(Properties props)即可返回這個默認的Session實例。值得一提的是,雖然後續調用也會傳入Properties,但新屬性並不會起作用,如果希望採用新的屬性值,則可以通過getDefaultInstance(Properties props)創建一個新的Session實例達到目的。Authenticator在這裏承當了兩個功能:首先,對JVM中默認Session實例進行認證保護,後續調用執行getDefaultInstance(Properties props, Authenticator authenticator)方法時必須和第一次一樣;其次,在具體和郵件服務器交互時,又作爲認證的信息;
l static Session getDefaultInstance(Properties props):返回JVM中默認的Session實例,如果第一次創建Session未指定Authenticator入參,後續調用可以使用該訪問獲取Session;
l static Session getInstance(Properties props, Authenticator authenticator):創建一個新的Session實例,它不會在JVM中被作爲默認實例共享;
l static Session getInstance(Properties props):根據相關屬性創建一個新的Session實例,未使用安全認證信息;
getDefaultInstance得到的始終是該方法初次創建的缺省的對象,而getInstance得到的始終是新的對象,Authenticator的使用後面會說到。Session是JavaMail提供者配置文件以及設置屬性信息的“容器”,其本身不會和郵件服務器進行任何的通信。所以,一般情況下,我們僅需要通過getDefaultInstance獲取一個共享的Session實例就可以了。例如:
Properties props = System.getProperties(); props.setProperty("mail.transport.protocol", "smtp"); … Session session = Session.getDefaultInstance(props);
Message
消息對象。Message是郵件的載體,用於封裝郵件的所有信息,Message是一個抽象類,已知的實現類有MimeMessage。
MimeMessage message=new MimeMessage(session)
ps:Address類:地址。一旦你創建了Session和Message,並將內容填入消息後,就可以用Address確定信件地址了。和Message一樣,Address也是一個抽象類,常用InternetAddress類實例。
若創建的地址只包含電子郵件地址,只要傳遞電子郵件地址到構造器就行了。 Address address = new InternetAddress("[email protected]"); 若希望名字緊挨着電子郵件顯示,也可以把它傳遞給構造器: Address address = new InternetAddress("[email protected]", "George Bush");
下面來看下Message中設置郵件信息的一些方法。
發件人
abstract void | setFrom() Set the "From" attribute in this Message. |
abstract void | setFrom(Address address) Set the "From" attribute in this Message. |
現在大多數SMTP服務器要求發件人與連接賬戶必須一致,否則會拋出驗證失敗的異常,這樣是防止用戶僞裝成其它人的賬戶惡意發送郵件。
如果需要消息顯示多個 from 地址,可以使用 addFrom() 方法:
Address address[] = ...;
message.addFrom(address);
收件人/抄送人/暗送人
void | setRecipient(Message.RecipientType type,Address address) Set the recipient address. |
abstract void | setRecipients(Message.RecipientType type,Address[] addresses) Set the recipient addresses. |
第一個方法設置單個人,第二個方法設置多個人。
方法中第一個參數涉及到另一個類RecipientType(預定義地址),該類是Message的靜態內部類,期內有三個常量值:
static Message.RecipientType | BCC The "Bcc" (blind carbon copy) recipients. |
static Message.RecipientType | CC The "Cc" (carbon copy) recipients. |
static Message.RecipientType | TO The "To" (primary) recipients. |
TO - 收件人;CC - 抄送人;BCC - 暗送人。
回覆人
void | setReplyTo(Address[] addresses) Set the addresses to which replies should be directed. |
設置收件人收到郵件後的回覆地址。
標題
abstract void | setSubject(String subject) Set the subject of this message. |
設置郵件標題/主題。
內容
void | setContent(Multipart mp) This method sets the given Multipart object as this message's content. |
void | setContent(Object obj,String type) A convenience method for setting this part's content. |
void | setText(String text) A convenience method that sets the given String as this part's content with a MIME type of "text/plain". |
設置郵件文本內容、HTML內容、附件內容。
示例
如果消息是發給副總統的,同時發送一個副本(carbon copy)給總統夫人,以下做法比較恰當:
Address toAddress = new InternetAddress("[email protected]"); Address ccAddress = new InternetAddress("[email protected]"); message.addRecipient(Message.RecipientType.TO, toAddress); message.addRecipient(Message.RecipientType.CC, ccAddress);
Transport和Store
傳輸和存儲。通過Session可以創建Transport(用於發送郵件)和Store(用於接收郵件)。創建機制:我們知道提供者在javamail.providers配置文件中爲每一種支持的郵件協議定義了實現類,Session根據協議類型(stmp、pop3等)和郵件操作方式(傳輸和存儲)這兩個信息就可以定位到一個實例類上。比如,指定stmp協議和transport類型後,Session就會使用com.sun.mail.smtp.SMTPTransport實現類創建一個Transport實例,而指定pop3協議和store類型時,則會使用com.sun.mail.pop3.POP3Store實例類創建一個Store實例。Session提供了多個重載的getTransport()和getStore()方法,這些方法將根據Session中Properties屬性設置情況進行工作,影響這兩套方法工作的屬性包括:
屬性名 | 說明 |
mail.transport.protocol | 默認的郵件傳輸協議,例如,smtp |
mail.store.protocol | 默認的存儲郵件協議,例如:pop3 |
mail.host | 默認的郵件服務地址,例如:192.168.67.1 |
mail.user | 默認的登陸用戶名,例如:zapldy |
Session中提供的創建Trasnsport和Store的方法如下(當Session實例設置了mail.transport.protocol屬性時,該方法返回對應的Transport實例,否則拋出javax.mail.NoSuchProviderException。如果Session沒有設置mail.transport.protocol屬性,可以通過該方法返回指定類型的Transport,如transport = session.getTransport(“smtp”)。):
Store | getStore() Get a Store object that implements this user's desired Store protocol. |
Store | getStore(Provider provider) Get an instance of the store specified by Provider. |
Store | getStore(String protocol) Get a Store object that implements the specified protocol. |
Store | getStore(URLName url) Get a Store object for the given URLName. |
Transport | getTransport() Get a Transport object that implements this user's desired Transport protcol. |
Transport | getTransport(Address address) Get a Transport object that can transport a Message of the specified address type. |
Transport | getTransport(Provider provider) Get an instance of the transport specified in the Provider. |
Transport | getTransport(String protocol) Get a Transport object that implements the specified protocol. |
Transport | getTransport(URLName url) Get a Transport object for the given URLName. |
可以看到,重構了很多,這些方法最終都會去解析上文提到的配置文件,找到對應配置信息。
如果Session中未包含Authenticator,以上兩方法創建的Transport實例和郵件服務器交互時必須顯示提供用戶名/密碼的認證信息。如果Authenticator非空,則可以在和郵件服務器交互時被作爲認證信息使用。除了以上兩種提供認證信息的方式外,Session還可以使用以下的方法爲Transport提供認證信息。
Transport getTransport(URLName url):用戶可以通過URLName入參指定郵件協議、郵件服務器、端口、用戶名和密碼信息,請看下面的代碼:
URLName urln = new URLName(“smtp”, “smtp.sina.com.cn”, 25, null, “masterspring2”, “spring”); Transport transport = session.getTransport(urln);
這裏,指定了郵件協議爲smtp,郵件服務器是smtp.sina.com.cn,端口爲25,用戶名/密碼爲masterspring2/spring。
消息發送:使用 Transport 類。這個類用協議指定的語言發送消息(通常是 SMTP)。它是抽象類,它的工作方式與 Session 有些類似,僅調用靜態 send() 方法,就能使用類的缺省版本:
Transport.send(message);
或者,您也可以從針對您的協議的會話中獲得一個特定的實例,傳遞用戶名和密碼(如果不必要就不傳),發送消息,然後關閉連接。
message.saveChanges(); // implicit with send() Transport transport = session.getTransport("smtp"); transport.connect(host, username, password); transport.sendMessage(message, message.getAllRecipients()); transport.close();
後面這種方法在您要發送多條消息時最好,因爲它能保持郵件服務器在消息間的活動狀態。基本 send() 機制爲每個方法的調用設置與服務器獨立的連接。
注意:要觀察傳到郵件服務器上的郵件命令,請用 session.setDebug(true) 設置調試標誌。
獲取消息:用 Session 獲取消息與發送消息開始很相似。但是,在 session 得到後,很可能使用用戶名和密碼或使用 Authenticator 連接到一個 Store。類似於 Transport ,您告知 Store 使用什麼協議:
// Store store = session.getStore("imap"); Store store = session.getStore("pop3"); store.connect(host, username, password);
連接到 Store 之後,接下來,您就可以獲取一個 Folder,您必需先打開它,然後才能讀裏面的消息。
Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); Message message[] = folder.getMessages();
POP3 唯一可以用的文件夾是 INBOX。如果使用 IMAP,還可以用其它文件夾。
注意:Sun 的供應商有意變得聰明。雖然 Message message[] = folder.getMessages(); 看上去是個很慢的操作,它從服務器上讀取每一條消息,但僅在你實際需要消息的一部分時,消息的內容纔會被檢索。
一旦有了要讀的 Message,您可以用 getContent() 來獲取其內容,或者用 writeTo() 將內容寫入流。getContent() 方法只能得到消息內容,而 writeTo() 的輸出卻包含消息頭。
System.out.println(((MimeMessage)message).getContent());
一旦讀完郵件,要關閉與 folder 和 store 的連接。
folder.close(aBoolean); store.close();
傳遞給 folder 的 close() 方法的 boolean 表示是否清除已刪除的消息從而更新 folder。
示例
下面來看一個稍複雜點的示例:
public class JavaMailTest2 { public static void main(String[] args) throws MessagingException { Properties props = new Properties(); // 開啓debug調試 props.setProperty("mail.debug", "true"); // 發送服務器需要身份驗證 props.setProperty("mail.smtp.auth", "true"); // 設置郵件服務器主機名 props.setProperty("mail.host", "smtp.163.com"); // 發送郵件協議名稱 props.setProperty("mail.transport.protocol", "smtp"); // 設置環境信息 Session session = Session.getInstance(props, new Authenticator() { // 在session中設置賬戶信息,Transport發送郵件時會使用 protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("java_mail_001", "javamail"); } }); // 創建郵件對象 Message msg = new MimeMessage(session); // 發件人 msg.setFrom(new InternetAddress("[email protected]")); // 多個收件人 msg.setRecipients(RecipientType.TO, InternetAddress.parse("[email protected],[email protected]")); // 抄送人 msg.setRecipient(RecipientType.CC, new InternetAddress("[email protected]")); // 暗送人 msg.setRecipient(RecipientType.BCC, new InternetAddress("[email protected]")); // 主題 msg.setSubject("中文主題"); // HTML內容 msg.setContent("<div align=\"center\">你好啊</div>", "text/html;charset=utf-8"); // 連接郵件服務器、發送郵件、關閉連接,全乾了 Transport.send(msg); } }
本文來自:
高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17839983
zapldy,原文地址:http://blog.csdn.net/zapldy/article/details/3971579