1 電子郵件是如何工作的? ??
原則上首量,電子郵件很簡(jiǎn)單.使用郵件用戶代理(MUA)。MUA有多種形式,包括基于文本的加缘、基于Web的和GUI應(yīng)用程序鸭叙;MicrosoftOutlook和Netscape Messenger屬于最后一類。每個(gè)電子郵件客戶端被配置為將郵件發(fā)送到郵件傳送代理(MTA)拣宏,并可用于輪詢MTA以獲取發(fā)送到用戶地址的電子郵件沈贝。為此,您需要郵件服務(wù)器上的電子郵件帳戶(技術(shù)上是MTA)蚀浆,并且可以使用標(biāo)準(zhǔn)的Internet協(xié)議缀程,或者脫機(jī)處理電子郵件(使用POP 3),或者將電子郵件保留在服務(wù)器上(使用IMAP)市俊。用于從客戶端向MTA發(fā)送郵件以及在MTA之間發(fā)送郵件的協(xié)議是SMTP(簡(jiǎn)單郵件傳輸協(xié)議)。
MTA之間真正發(fā)生的事情只是稍微有趣一些滤奈。電子郵件服務(wù)器嚴(yán)重依賴dns和特定于電子郵件的記錄摆昧。郵件傳送(或MX)記錄。MX記錄與用于解析URL的DNS記錄略有不同蜒程,后者包含一些用于更有效地路由郵件的附加優(yōu)先級(jí)信息绅你。我不會(huì)在這里深入研究這些細(xì)節(jié),但重要的是要理解DNS是成功和高效地路由電子郵件的關(guān)鍵昭躺。James是一個(gè)MTA忌锯,而JavaMailAPI為MUA提供了一個(gè)框架。在本文中领炫,我們將使用JavaMail為我們的James安裝設(shè)置一個(gè)測(cè)試偶垮。在本系列文章中,我們將使用JamesMailetAPI來展示如何開發(fā)您自己的James應(yīng)用程序帝洪。
2 Javamail快速入門?
進(jìn)行Javamail開發(fā)需要用到兩個(gè)包:mail.jar和activation.jar似舵,在開始Javamail編程之前,請(qǐng)自己將這兩個(gè)包添加到IDE的Build path中或?qū)⑦@兩個(gè)包的路徑配置到環(huán)境變量中葱峡。?
2.1使用Javamail向James的郵箱帳戶發(fā)送郵件?
2.1.1業(yè)務(wù)描述?
????本例將使用Javamail實(shí)現(xiàn)郵件的發(fā)送功能砚哗。發(fā)送郵件需要配置郵件服務(wù)器屬性信息,配置郵件接收地址砰奕,使用SMTP認(rèn)證獲得會(huì)話(Session)蛛芥,構(gòu)建郵件體(MimeMessage),發(fā)送郵件军援。具體編碼如下:?
2.1.2編碼實(shí)現(xiàn)?
發(fā)送郵件需要兩個(gè)類:一個(gè)是SMTP用戶身份認(rèn)證類(James在默認(rèn)情況下仅淑,是需要SMTP身份認(rèn)證的);另一個(gè)就是我們的郵件發(fā)送類盖溺,為簡(jiǎn)單起見漓糙,我們直接將郵件的相關(guān)信息,如:標(biāo)題烘嘱、內(nèi)容昆禽、發(fā)送者蝗蛙、接收者等信息直接寫在類中,運(yùn)行main()函數(shù)即發(fā)送醉鳖。當(dāng)然捡硅,你同樣可以為自己的郵件發(fā)送系統(tǒng)構(gòu)造一個(gè)郵件發(fā)送介面,通過Servlet將相關(guān)參數(shù)傳遞至后臺(tái)進(jìn)行處理與發(fā)送盗棵。其主要代碼也就是此main()函數(shù)中的內(nèi)容壮韭,故不贅述。請(qǐng)各位按需修改纹因,demo代碼是截圖喷屋,不便之處請(qǐng)諒解,可以到我的git獲取代碼瞭恰。
?SendMail.java發(fā)送郵件代碼:
值得一提的是,本程序已經(jīng)實(shí)現(xiàn)了帶附件郵件的發(fā)送功能惊畏,如果要發(fā)送帶附件的郵件恶耽,則只需要將附件的路徑傳到fileAttachment變量中就可以了。郵件發(fā)送成功后颜启,程序?qū)⒃诤笈_(tái)打印出“發(fā)送成功”偷俭,這樣我們就完成了郵件發(fā)送功能。那么缰盏,我們應(yīng)該如何檢驗(yàn)服務(wù)器是否確實(shí)收到我們發(fā)送的測(cè)試郵件呢涌萤?Javamail可以發(fā)送郵件,當(dāng)然也能接收郵件啦乳规,下面讓我們一起使用Javamail編寫郵件接收功能來檢驗(yàn)吧形葬。?
2.2使用Javamail接收郵件?
2.2.1業(yè)務(wù)描述?
在上一節(jié),我們已經(jīng)向James的accidentaly用戶發(fā)送了一封測(cè)試郵件暮的,我們應(yīng)該如何使用Javamail來收取這封郵件呢笙以??
為讀取郵件,必須首先設(shè)置服務(wù)器屬性(Properties)冻辩,獲取一個(gè)會(huì)話(Session)猖腕,然后獲取并連接郵箱所在的存儲(chǔ)器(Store對(duì)象),打開該用戶的郵箱(Folder)恨闪,獲取所希望閱讀的消息倘感,最后關(guān)閉目錄和連接。?
下面的程序?qū)崿F(xiàn)了接收accidentaly@dascomyun郵箱中所有郵件咙咽,并將發(fā)送人和主題打印出來
程序運(yùn)行成功后老玛,將會(huì)把a(bǔ)ccidentaly用戶的郵件從James服務(wù)器中取出,并將此郵箱中所有郵件的發(fā)件人、主題打印在后臺(tái)蜡豹。若要打印該郵件的內(nèi)容等信息麸粮,則只要將message[i]對(duì)象中的郵件內(nèi)容等信息讀取出來就可以了。?
注:鑒于郵件的存儲(chǔ)結(jié)構(gòu)(將在第五章介紹)镜廉,讀取郵件附件是一個(gè)比較復(fù)雜的過程弄诲,因?yàn)猷]件的文本內(nèi)容和附件信息都是保存在BodyPart對(duì)象中的,BodyPart用于標(biāo)識(shí)類型的標(biāo)記不明確娇唯,造成對(duì)附件的判斷較為復(fù)雜齐遵。對(duì)于附件的操作本人將在今后的改進(jìn)版本中加以介紹。
3 Mailet快速入門?
Mailet API是一個(gè)用來創(chuàng)建郵件處理程序的簡(jiǎn)單的API,它被配置在郵件服務(wù)器端執(zhí)行,分匹配器Matcher和Mailet的接口兩種,匹配器根據(jù)特定的條件匹配郵件消息,并觸發(fā)相應(yīng)的Mailet.?
Mailet這個(gè)詞是跟Servlet相似,功能也相似,他們的共同之處都是在服務(wù)器端觸發(fā)并執(zhí)行,只是Servlet的Matcher通常是url的pattern,跟Servlet的接口一樣,Mailet也有init()方法,service()方法和destroy()方法.即他們都有類似的生命周期. Mailet的簡(jiǎn)單可編程接口可以用來做一些郵件處理,比如反垃圾郵件,檢查郵件病毒以及郵件博客等等,利用移動(dòng)設(shè)備可發(fā)送email的功能,可以做到手機(jī)通過mail發(fā)送信息到郵件服務(wù)器交給Mailet處理,形成移動(dòng)博客的模型.?
Mailet的運(yùn)行需要mailet-2.3.jar和mailet-api-2.3.jar兩個(gè)包的支持塔插,James本身就有這兩個(gè)包梗摇,可不作修改,但在開發(fā)的時(shí)候還是需要開發(fā)者自己將這兩個(gè)包導(dǎo)入到工程的Build path中或配置到系統(tǒng)環(huán)境變量中佑淀。?
3.1 用Mailet做一個(gè)Hello的例子?
3.1.1 業(yè)務(wù)描述?
我們要實(shí)現(xiàn)當(dāng)外部發(fā)送給James服務(wù)器中名字含accidentaly的郵箱時(shí)留美,服務(wù)器在這封郵件的主題前加入“Hello”,并在服務(wù)器后臺(tái)輸出“Received a piece of Email”伸刃。如前所述,Mailet包括匹配器Matcher和Mailet兩種接口逢倍,現(xiàn)在就讓我們用Mailet API實(shí)現(xiàn)這兩個(gè)接口吧捧颅。?
匹配器BizMatcher.java?
?BizMaillet.java?
?3.1.3 配置部署?
Mailet跟Servlet一樣,是服務(wù)器端程序较雕,是不能直接在客戶端運(yùn)行的碉哑,必須要部署到服務(wù)器端方可生效。部署具體步驟如下:?
1亮蒋、 將我們編寫的Matcher和Mailet打包成jar文件扣典;?
2、 在\james-2.3.1\apps\james\SAR-INF目錄下新建一個(gè)lib文件夾慎玖;?
3贮尖、 將打包好的jar文件復(fù)制到剛剛新建的lib文件夾下;?
4趁怔、 打開config.xml配置文件湿硝,找到以下這段代碼:?
XML代碼:
?前半部分是用于配置Mailet包所在位置,后半部分是用于配置Matcher包所在位置润努,我們把我們剛編寫的Mailet和Matcher所在位置配置進(jìn)去就可以了关斜。配置后的結(jié)果如下:?
Xml代碼?
這樣就完成了包的配置。我們都知道铺浇,Mailet的工作過程是:首先由Matcher來匹配所接收到的郵件痢畜,然后提交給相應(yīng)的Mailet處理,但是哪個(gè)匹配器對(duì)應(yīng)哪個(gè)Mailet呢?我們還需要配置Mailet的對(duì)應(yīng)關(guān)系丁稀。同樣在config.xml中找到下面的代碼,并在這段代碼下面加入我們自己的Mailet:
Xml代碼 ?
? ? <mailet ?match="All" class="PostmasterAlias"?/>
Xml代碼 ?
<mailet??match="BizMatcher" class="BizMaillet"?/>
?這樣就完成了我們自定義的Mailet的配置部署工作了吼拥。重啟James服務(wù)器,則此Mailet即可生效二驰。?
3.1.4 測(cè)試Mailet?
前面我們已經(jīng)完成了Mailet的編碼和部署工作扔罪,現(xiàn)在就讓我們來測(cè)試一下我們的Mailet是否生效吧。首先桶雀,需要在James服務(wù)器上新建一個(gè)名稱含accidentaly的用戶矿酵。前面已介紹過新建用戶的方法了,在此就不重復(fù)敘述了矗积。?
使用adduser accidentaly?881213命令新建一個(gè)accidentaly用戶全肮。?
使用上面所談及的“使用Javamail向James的郵箱帳戶發(fā)送郵件”來向accidentaly@dascomyun發(fā)送一封郵件(當(dāng)然,你同樣可以使用Foxmail或Outlook向此地址發(fā)送郵件)棘捣,郵件發(fā)送成功后辜腺,James服務(wù)器后臺(tái)將輸出“Receive a piece of email”。運(yùn)行效果如下圖所示:?
4 常用Javamail API簡(jiǎn)介?
核心JavaMail API可以分為兩部分乍恐,一部分由七個(gè)類組成:Session评疗、Message、Address茵烈、Authenticator百匆、Transport、Store及Folder呜投,它們都來自Javamail API頂級(jí)包(但開發(fā)者需要使用的具體子類可能在javax.mail.internet包內(nèi))加匈。可以用這些類完成大量常見的電子郵件任務(wù)仑荐,包括發(fā)送消息雕拼、檢索消息、刪除消息粘招、認(rèn)證啥寇、回復(fù)消息、轉(zhuǎn)發(fā)消息男图、管理附件示姿、處理基于HTML文件格式的消息以及搜索或過濾郵件列表,這類任務(wù)主要屬于MTA范疇逊笆。下圖描繪了Javamail郵件收發(fā)過程栈戳。?
下面給出這七個(gè)核心類的簡(jiǎn)單介紹,以使讀者能對(duì)Javamail框架有一個(gè)大體了解:?
4.1 javax.mail.Session?
Session類定義了一個(gè)基本郵件會(huì)話难裆,它是Javamail API最高層入口類子檀,所有其它類都必須經(jīng)由Session對(duì)象才得以生效镊掖。Session對(duì)象管理配置選項(xiàng)和用于與郵件系統(tǒng)交互的用戶認(rèn)證信息,它使用java.util.Properties對(duì)象獲取信息褂痰,如郵件服務(wù)器亩进、用戶名、密碼及整個(gè)應(yīng)用程序中共享的其它信息缩歪。?
Session類的構(gòu)造器是私有的归薛,它不能被繼承,也不能使用new語句來創(chuàng)建實(shí)例匪蝙,但它提供了兩個(gè)表態(tài)方法getInstance和getDefaultInstance來獲取Session實(shí)例主籍,前者創(chuàng)建一個(gè)獨(dú)立的會(huì)話,否則獲取缺省的共享會(huì)話逛球。?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Session.html?
4.2 javax.mail.Message?
獲得Session對(duì)象后千元,可以開始繼續(xù)創(chuàng)建要發(fā)送的郵件消息,這由Message類來完成颤绕,Message實(shí)現(xiàn)了Part接口幸海,它表示一個(gè)郵件消息,包含一系列屬性(attribute)和一個(gè)消息內(nèi)容(content)奥务。消息屬性標(biāo)識(shí)了消息地址信息物独,定義了消息內(nèi)容的結(jié)構(gòu)(包括內(nèi)容類型);消息內(nèi)容使用DataHandler對(duì)象包裝實(shí)際數(shù)據(jù)氯葬。當(dāng)郵件消息位于目錄(folder)中時(shí)议纯,系統(tǒng)還使用一個(gè)標(biāo)志位集合來描述它的狀態(tài)。?
Message是抽象類溢谤,實(shí)際使用時(shí)必需用一個(gè)子類代替以表示具體的郵件格式。比如說憨攒,Javamail API提供了MimeMessage(位于javax.mail.internet.MimeMessage包)類世杀,該類擴(kuò)展自Message,實(shí)現(xiàn)了RFC822和MIME標(biāo)準(zhǔn)肝集。Message的子類通常通過字節(jié)流構(gòu)建其實(shí)例瞻坝,相應(yīng)的,它們也可以生成字節(jié)流來傳輸自身杏瞻。?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Message.html?
4.3 javax.mail.Address?
Address類表示電子郵件地址所刀,它是一個(gè)抽象類。其子類(最經(jīng)常使用的子類是javax.mail.internet.InternetAddress)提供具體實(shí)現(xiàn)捞挥,且通掣〈矗可串行化。?
在創(chuàng)建了Session和Message砌函,并設(shè)置了消息內(nèi)容后斩披,可以用Address確定郵件消息的發(fā)送者和接收者地址溜族。?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Address.html?
4.4 javax.mail.Authenticator?
Authenticator代表一個(gè)可以為網(wǎng)絡(luò)連接獲取認(rèn)證信息的對(duì)象,它通常通過提示用戶輸入用戶名和密碼來收集認(rèn)證信息垦沉,使連接可以訪問受保護(hù)的資源煌抒。對(duì)于Javamail API來說,這些資源就是郵件服務(wù)器厕倍。Javamail Authenticator在javax.mail包中寡壮,它和java.net中同名的類Authenticator不同。?
要使用Authenticator讹弯,必須先創(chuàng)建一個(gè)它的子類實(shí)例况既,并且在會(huì)話對(duì)象創(chuàng)建時(shí)為會(huì)話注冊(cè)該Authenticator對(duì)象。在需要認(rèn)證的時(shí)候闸婴,就會(huì)通知Authenticator坏挠。程序可以彈出窗口,也可以從配置文件中(雖然沒有加密是不安全的)讀取用戶名和密碼邪乍,并使用它們作為構(gòu)造函數(shù)參數(shù)創(chuàng)建一個(gè)PasswordAuthentication對(duì)象返回給調(diào)用程序降狠。?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Authenticator.html?
4.5 javax.mail.Transport?
消息發(fā)送的最后步驟是使用Transport類。該類使用指定協(xié)議發(fā)送消息(通常是SMTP)庇楞。Transport是抽象類榜配,它的工作方式與Session有些類似,可以通過靜態(tài)方法或?qū)嵗椒òl(fā)送消息吕晌。Transport繼承自Service類蛋褥,而后者提供了很多通用方法,如命名傳輸睛驳、連接服務(wù)器烙心、監(jiān)聽傳輸事件等等。?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Transport.html?
4.6 javax.mail.Store?
Store是一個(gè)抽象類乏沸,它模擬了消息存儲(chǔ)器及其內(nèi)部目錄(Folder)訪問協(xié)議淫茵,以存儲(chǔ)和讀取消息,其子類提供具體實(shí)現(xiàn)蹬跃。?
Store定義的存儲(chǔ)器包括一個(gè)分層的目錄體系匙瘪,消息存儲(chǔ)在目錄內(nèi),蝶缀〉び鳎客戶程序可以通過獲取一個(gè)實(shí)現(xiàn)了數(shù)據(jù)庫(kù)訪問協(xié)議的Store對(duì)象來訪問消息存儲(chǔ)器,絕大多數(shù)存儲(chǔ)器要求用戶在訪問前提供認(rèn)證信息翁都,connect方法執(zhí)行了該認(rèn)證過程碍论。?
Store store = session.getStore("pop3");//指定協(xié)議?
store.connect(host,usename,password);//?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Store.html?
4.7 javax.mail.Folder?
Folder是一個(gè)抽象類,用于分級(jí)組織郵件荐吵,其子類提供針對(duì)具體協(xié)議的實(shí)現(xiàn)骑冗。Folder代表的目錄可以容納消息或子目錄赊瞬,存儲(chǔ)在目錄內(nèi)的消息被順序計(jì)數(shù)(從1開始到消息總數(shù)),該順序被稱為“郵箱順序”贼涩,通城山В基于郵件消息到達(dá)目錄的順序。郵件順序的變動(dòng)將改變消息的序列號(hào)遥倦,這種情況僅發(fā)生在客戶程序調(diào)用Expunge方法擦除目錄內(nèi)設(shè)置了Flags.Flag.DELETED標(biāo)志位的消息時(shí)谤绳。執(zhí)行擦除操作后,目錄內(nèi)消息將重新編號(hào)袒哥。?
客戶程序可以通過消息序列號(hào)或直接通過相應(yīng)的Message對(duì)象應(yīng)用目錄中的消息缩筛,由于消息序列號(hào)在會(huì)話中很可能改變,因此應(yīng)盡可能保存Message對(duì)象而非序列號(hào)來反復(fù)引用對(duì)象堡称。?
連接到Store之后瞎抛,接下來可以獲取一個(gè)文件夾(Folder)。該文件夾必須先使用open()方法打開却紧,然后才能讀取里面的消息:?
Folder folder = store.getDefaultFolder();?
//或 : Folder folder = store.getFolder("inbox");?
folder.open(Folder.READ_WRITE);?
Message message[] = folder.getMessage();?
open()方法指定了要打開的文件夾及打開方式(如Folder.READ_WRITE)桐臊。 inbox是POP3唯一可以使用的文件夾。如果使用IMAP晓殊,還可以用其它的文件夾断凶。獲得Message之后,就可以用getContent()獲得其內(nèi)容巫俺,或者用writeTo()將內(nèi)容寫入輸出流认烁。getContent()方法之能得到消息內(nèi)容,而writeTo()的輸出卻包含消息頭.讀完郵件之后要關(guān)閉與Folder和Store的連接:?
folder.close(aBoolean);?
store.close();?
API明細(xì):/javamail-1.4.1/docs/javadocs/javax/mail/Folder.html?
5 常用Mailet API簡(jiǎn)介?
Mailet主要包含兩個(gè)包:org.apache.mailet和org.apache.mailet.dates?
5.1 org.apache.mailet?
此包主要用于匹配器和Mailet的編寫介汹。自定義的Mailet類需要繼承org.apache.mailet.GenericMailet却嗡,自定義的Matcher類需要繼承org.apache.mailet.GenericMatcher或org.apache.mailet.GenericRecipientMatcher。例子詳見第四章Mailet快速入門嘹承。?
API明細(xì):/MailetSDK/javadocs/org/apache/mailet/package-summary.html?
5.2 org.apache.mailet.dates?
此包主要用于郵件中的日期格式的轉(zhuǎn)換稽穆。?
API明細(xì):/MailetSDK/javadocs/org/apache/mailet/dates/package-summary.html?
碼云:https://gitee.com/AccidentalyAcross/projects