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连接超时时间,单位为毫秒,默认为永不超时


其他几个协议也有类似的一系列属性,如POP3mail.pop3.hostmail.pop3.port以及IMAPmail.imap.hostmail.imap.port等。更详细的信息请查看com.sun.mail.smtpcom.sun.mail.pop3com.sun.mail.imap这三个包的Javadochttp://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)首先使用/lib中的javamail.providers

       2)如果1)不存在相应的配置文件,使用类路径下mail.jarMETA-INF目录下的javamail.providers

       3)如果2)不存在相应的配置文件,使用类路径下的mail.jarMETA-INF目录下的javamail.default.providers

       所以开发者可以在/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,则后续调用通过nullAuthenticator入参或直接使用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根据协议类型(stmppop3等)和邮件操作方式(传输和存储)这两个信息就可以定位到一个实例类上。比如,指定stmp协议和transport类型后,Session就会使用com.sun.mail.smtp.SMTPTransport实现类创建一个Transport实例,而指定pop3协议和store类型时,则会使用com.sun.mail.pop3.POP3Store实例类创建一个Store实例。Session提供了多个重载的getTransport()getStore()方法,这些方法将根据SessionProperties属性设置情况进行工作,影响这两套方法工作的属性包括:

属性名

说明

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); } }



wKioL1PIcivSZFrkAAARrAqeod8864.gif

 本文来自:

        高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17839983

                zapldy,原文地址:http://blog.csdn.net/zapldy/article/details/3971579