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:http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eeplat-419426.html#javamail-1.4.5-oth-JPR
JavaMail spec:http://www.oracle.com/technetwork/java/javamail-1-149769.pdf
JAF:http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jaf-1.1.1-fcs-oth-JPR
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)首先使用
2)如果1)不存在相应的配置文件,使用类路径下mail.jar中META-INF目录下的javamail.providers;
3)如果2)不存在相应的配置文件,使用类路径下的mail.jar中META-INF目录下的javamail.default.providers;
所以开发者可以在
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("你好啊", "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