一、背景
最近朋友的公司有用到这个功能,之前对这一块也不是很熟悉,就和他一起解决出现的异常的同时,也初窥一下使用Apache Common Email组件进行邮件发送。
二、Java发送邮件的注意事项
1.不同的邮箱有不同的支持协议,比如有些只支持SSL协议,有些只支持TLS协议,还有些同时支持SSL和TLS协议。
2.支持不同协议的邮箱,在使用Java发送邮件时要使用不同的方式发送,下面我会介绍基于SSL和TLS的两种实现方式。
三、代码实现
说明:本例采用Apache Common Email组件进行开发。
1.发送邮件实体类
1 package com.hafiz.zhang.mail.entity;
2
3 import java.io.Serializable;
4 import java.util.List;
5
6 import org.apache.commons.mail.EmailAttachment;
7
8 /**
9 * @author hafiz.Zhang
10 * @Date 2016年5月23日 下午2:40:36
11 * @Description 邮件实体类
12 */
13 public class MailEntity implements Serializable{
14 private static final long serialVersionUID = 1589570366044890462L;
15 public static final String ENCODING = "UTF-8"; //邮件编码
16 private String host; //服务器地址
17 private String port; //发送端口
18 private String sender; //发件人邮箱
19 private List<String> receiver; //收件人邮箱
20 private List<String> copier; //抄送人
21 private String senderName; //发件人昵称
22 private String userName; //发件邮箱账号
23 private String password; //发件邮箱密码
24 private String subject; //邮件主题
25 private String content; //邮件内容(支持HTML)
26 private List<EmailAttachment> attachments; //邮件附件
27 public String getHost() {
28 return host;
29 }
30 public void setHost(String host) {
31 this.host = host;
32 }
33 public String getPort() {
34 return port;
35 }
36 public void setPort(String port) {
37 this.port = port;
38 }
39 public String getSender() {
40 return sender;
41 }
42 public void setSender(String sender) {
43 this.sender = sender;
44 }
45 public List<String> getReceiver() {
46 return receiver;
47 }
48 public void setReceiver(List<String> receiver) {
49 this.receiver = receiver;
50 }
51 public List<String> getCopier() {
52 return copier;
53 }
54 public void setCopier(List<String> copier) {
55 this.copier = copier;
56 }
57 public String getSenderName() {
58 return senderName;
59 }
60 public void setSenderName(String senderName) {
61 this.senderName = senderName;
62 }
63 public String getUserName() {
64 return userName;
65 }
66 public void setUserName(String userName) {
67 this.userName = userName;
68 }
69 public String getPassword() {
70 return password;
71 }
72 public void setPassword(String password) {
73 this.password = password;
74 }
75 public String getSubject() {
76 return subject;
77 }
78 public void setSubject(String subject) {
79 this.subject = subject;
80 }
81 public String getContent() {
82 return content;
83 }
84 public void setContent(String content) {
85 this.content = content;
86 }
87 public List<EmailAttachment> getAttachments() {
88 return attachments;
89 }
90 public void setAttachments(List<EmailAttachment> attachments) {
91 this.attachments = attachments;
92 }
93 @Override
94 public String toString() {
95 return "MailEntity [host=" + host + ", port=" + port + ", sender=" + sender + ", receiver=" + receiver
96 + ", copier=" + copier + ", senderName=" + senderName + ", userName=" + userName + ", password="
97 + password + ", subject=" + subject + ", content=" + content + ", attachments=" + attachments + "]";
98 }
99 }
注:实体类代码比较臃肿,建议使用lombok进行简化,lombok的使用方式见我的另一篇博客:Java简化代码神器-Lombok
2.发送邮件支持协议列举类
1 package com.hafiz.zhang.mail.tools.factory;
2
3 /**
4 * @author hafiz.Zhang
5 * @Date 2016年5月25日 下午12:08:59
6 * @Description 列举发送邮件的可用协议
7 */
8 public class Protocols {
9 //SSL协议
10 public static String SSL_MAIL_UTIL = "com.hafiz.zhang.mail.tools.SSLMailUtil";
11 //TLS协议
12 public static String TLS_MAIL_UTIL = "com.hafiz.zhang.mail.tools.TLSMailUtil";
13 }
3.发送邮件工具接口类
1 package com.hafiz.zhang.mail.tools;
2
3 import com.hafiz.zhang.mail.entity.MailEntity;
4
5 /**
6 * @author hafiz.Zhang
7 * @Date 2016年5月23日 下午3:26:15
8 * @Description 邮件发送接口类
9 */
10 public interface IMailUtil {
11 /**
12 * @Author hafiz.Zhang
13 * @Description: 发送邮件
14 * @param mail要发送邮件的实体类
15 * @return Boolean 发送结果
16 */
17 public abstract Boolean send(MailEntity mail);
18 }
4.基于SSL协议发送邮件工具实现类
1 package com.hafiz.zhang.mail.tools;
2
3 import java.util.List;
4
5 import org.apache.commons.mail.EmailAttachment;
6 import org.apache.commons.mail.EmailException;
7 import org.apache.commons.mail.HtmlEmail;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import com.hafiz.zhang.mail.entity.MailEntity;
12
13 /**
14 * @author hafiz.Zhang
15 * @Date 2016年5月23日 下午3:09:13
16 * @Description 通过SSL方式发送邮件工具类
17 */
18 public class SSLMailUtil implements IMailUtil{
19 private static Logger log = LoggerFactory.getLogger(SSLMailUtil.class);
20
21 public Boolean send(MailEntity mail) {
22 // 创建待发送的emil对象
23 HtmlEmail email = new HtmlEmail();
24 try {
25
26 // 设置SMTP发送服务器的名字:163的如下:"smtp.163.com"
27 email.setHostName(mail.getHost());
28 //设置端口号
29 if(null != mail.getPort()) {
30 email.setSmtpPort(Integer.parseInt(mail.getPort()));
31 }
32 // 设置邮件字符编码集
33 email.setCharset(MailEntity.ENCODING);
34 // 设置抄送人
35 email.addCc(mail.getCopier().toArray(new String[mail.getCopier().size()]));
36 // 设置发送人的邮箱
37 email.setFrom(mail.getSender(), mail.getSenderName());
38 // 收件人的邮箱
39 email.addTo(mail.getReceiver().toArray(new String[mail.getReceiver().size()]));
40 // 如果需要认证信息的话,设置认证:用户名-密码。分别为发件人在邮件服务器上的注册名称和密码
41 email.setAuthentication(mail.getUserName(), mail.getPassword());
42 // 设置要发送的邮件主题
43 email.setSubject(mail.getSubject());
44 // 设置要发送的信息,由于使用了HtmlEmail,可以在邮件内容中使用HTML标签
45 email.setMsg(mail.getContent());
46 // 设置附件
47 List<EmailAttachment> attachments = mail.getAttachments();
48 if(null != attachments && attachments.size() > 0) {
49 for(EmailAttachment attachment : attachments) {
50 email.attach(attachment);
51 }
52 }
53 // 发送
54 email.send();
55
56 log.info(mail.getSender() + " 发送邮箱到 " + mail.getReceiver());
57
58 return true;
59 } catch (EmailException e) {
60 log.error(mail.getSender() + " 发送邮箱到 " + mail.getReceiver() + " 失败,错误原因:" + e.getMessage());
61 e.printStackTrace();
62 }
63 return false;
64 }
65 }
5.基于TLS协议的发送邮件实现类
1 package com.hafiz.zhang.mail.tools;
2
3 import java.util.List;
4 import java.util.Properties;
5
6 import javax.mail.Session;
7
8 import org.apache.commons.mail.DefaultAuthenticator;
9 import org.apache.commons.mail.EmailAttachment;
10 import org.apache.commons.mail.EmailException;
11 import org.apache.commons.mail.HtmlEmail;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 import com.hafiz.zhang.mail.entity.MailEntity;
16
17 /**
18 * @author hafiz.Zhang
19 * @Date 2016年5月23日 下午3:23:15
20 * @Description 通过TLS协议发送邮件工具类
21 */
22 public class TLSMailUtil implements IMailUtil{
23 private static Logger log = LoggerFactory.getLogger(TLSMailUtil.class);
24
25 public Boolean send(MailEntity mail){
26 HtmlEmail email = new HtmlEmail();
27 try {
28 Properties prop = new Properties();
29 // 设置SMTP发送服务器的名字:163的如下:"smtp.163.com"
30 prop.setProperty("mail.smtp.host", mail.getHost());
31 // 设置SMTP发送服务器的端口
32 prop.setProperty("mail.smtp.port", mail.getPort());
33 // 设置是否需要认证
34 prop.setProperty("mail.smtp.auth", "true");
35 // 开启TLS加密方式
36 prop.setProperty("mail.smtp.starttls.enable", "true");
37 // 添加信任的服务器
38 prop.setProperty("mail.smtp.ssl.trust", mail.getHost());
39 // 进行认证并获取需要的session
40 DefaultAuthenticator defaultAuthenticator =
41 new DefaultAuthenticator(mail.getUserName(), mail.getPassword());
42 Session session = Session.getInstance(prop,defaultAuthenticator);
43 email.setMailSession(session);
44 // 设置字符编码集
45 email.setCharset(MailEntity.ENCODING);
46 // 设置发送人的邮箱
47 email.setFrom(mail.getSender(), mail.getSenderName());
48 // 设置收件人的邮箱
49 email.addTo(mail.getReceiver().toArray(new String[mail.getReceiver().size()]));
50 // 设置抄送人
51 email.addCc(mail.getCopier().toArray(new String[mail.getCopier().size()]));
52 // 设置要发送的邮件主题
53 email.setSubject(mail.getSubject());
54 // 设置要发送的信息,由于使用了HtmlEmail,可以在邮件内容中使用HTML标签
55 email.setMsg(mail.getContent());
56 // 设置附件
57 List<EmailAttachment> attachments = mail.getAttachments();
58 if(null != attachments && attachments.size() > 0) {
59 for(EmailAttachment attachment : attachments) {
60 email.attach(attachment);
61 }
62 }
63 // 发送
64 email.send();
65 log.info(mail.getSender() + " 发送邮箱到 " + mail.getReceiver());
66 return true;
67 } catch (EmailException e) {
68 log.error(mail.getSender() + " 发送邮箱到 " + mail.getReceiver() + " 失败,错误原因:" + e.getMessage());
69 e.printStackTrace();
70 }
71 return false;
72 }
73 }
注意:
1.如果没有设置开启TLS加密方式的代码(上面代码中标红处),则会出现Caused by: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.57 SMTP; Client was not authenticated to send anonymous mail during MAIL FROM异常,异常图片如下:
2.如果没有设置添加信任的主机服务器的代码(上面代码中标红处),则会出现:Caused by: javax.mail.MessagingException: Could not convert socket to TLS;异常,异常图片如下:
6.获取邮件发送类的工厂类
1 package com.hafiz.zhang.mail.tools.factory;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5
6 import com.hafiz.zhang.mail.tools.IMailUtil;
7
8 /**
9 * @author hafiz.Zhang
10 * @Date 2016年5月23日 下午3:43:15
11 * @Description 获取邮件发送类的工厂类
12 */
13 public class MailUtilsFactory {
14 private static Logger logger = LoggerFactory.getLogger(MailUtilsFactory.class);
15
16 private MailUtilsFactory() {
17 super();
18 }
19
20 public static IMailUtil getMailUtil(String name) {
21 IMailUtil mailUtil = null;
22 try {
23 mailUtil = (IMailUtil)Class.forName(name).newInstance();
24 } catch (Exception e) {
25 logger.error("获取邮件发送类失败,失败原因:" + e.getMessage());
26 e.printStackTrace();
27 }
28 return mailUtil;
29 }
30 }
7.测试类
1 package com.hafiz.zhang.mail.test;
2
3 import java.net.MalformedURLException;
4 import java.net.URL;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import org.apache.commons.mail.Email;
9 import org.apache.commons.mail.EmailAttachment;
10
11 import com.hafiz.zhang.mail.entity.MailEntity;
12 import com.hafiz.zhang.mail.tools.IMailUtil;
13 import com.hafiz.zhang.mail.tools.factory.MailUtilsFactory;
14 import com.hafiz.zhang.mail.tools.factory.Protocols;
15
16 /**
17 * @author hafiz.Zhang
18 * @Date 2016年5月23日 下午3:39:52
19 * @Description 两种方式发送邮件的测试类
20 */
21 public class TestMailSender {
22 public static void main(String[] args) throws MalformedURLException {
23 MailEntity mail = new MailEntity();
24 mail.setHost("smtp.163.com");
25 mail.setSender("xxx@163.com");
26 mail.setPort("25");
27 mail.setUserName("xxx@163.com");
28 mail.setPassword("xxxxx");
29 //设置收件人
30 List<String> receiver = new ArrayList<String>();
31 receiver.add("xxx@qq.com");
32 //设置抄送人
33 List<String> copier = new ArrayList<String>();
34 copier.add("xxx@163.com");
35
36 List<EmailAttachment> attachments = new ArrayList<EmailAttachment>();
37 // 添加本地附件
38 EmailAttachment att = new EmailAttachment();
39 att.setDescription("这是附件图片");
40 att.setDisposition(EmailAttachment.ATTACHMENT);
41 att.setName("好看的图片.png");
42 att.setPath("C:\\Users\\ZHF\\Desktop\\333.png");
43 attachments.add(att);
44 // 添加网络附件
45 EmailAttachment att2 = new EmailAttachment();
46 att2.setDescription("这是网络附件");
47 att2.setDisposition(EmailAttachment.ATTACHMENT);
48 att2.setName("网络附件.jpg");
49 att2.setURL(new URL("http://7xpsw5.com1.z0.glb.clouddn.com/41a9ccf2-2a22-44bd-aa3b-8e8779db1caf.jpg"));
50 attachments.add(att2);
51
52 mail.setAttachments(attachments);
53 mail.setCopier(copier);
54 mail.setReceiver(receiver);
55 mail.setSubject("测试邮件发送主题");
56 // String string = "<html><head></head><body><div>测试Java发送邮件内容</div></body></html>";
57 String string = "测试Java发送邮件内容";
58 mail.setContent(string);
59 // IMailUtil mailUtil = MailUtilsFactory.getMailUtil(Protocols.SSL_MAIL_UTIL);
60 IMailUtil mailUtil = MailUtilsFactory.getMailUtil(Protocols.TLS_MAIL_UTIL);
61 mailUtil.send(mail);
62 System.out.println("邮件发送成功");
63 }
64 }
8.工程pom.xml文件(由于本例使用maven工程)
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3 <modelVersion>4.0.0</modelVersion>
4
5 <groupId>com.hafiz.zhang</groupId>
6 <artifactId>sendEmail</artifactId>
7 <version>0.0.1-SNAPSHOT</version>
8 <packaging>jar</packaging>
9
10 <name>sendEmail</name>
11 <url>http://maven.apache.org</url>
12
13 <properties>
14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15 </properties>
16
17 <dependencies>
18 <dependency>
19 <groupId>org.apache.commons</groupId>
20 <artifactId>commons-email</artifactId>
21 <version>1.4</version>
22 </dependency>
23 <dependency>
24 <groupId>org.slf4j</groupId>
25 <artifactId>slf4j-log4j12</artifactId>
26 <version>1.6.4</version>
27 </dependency>
28 <dependency>
29 <groupId>org.slf4j</groupId>
30 <artifactId>slf4j-api</artifactId>
31 <version>1.6.4</version>
32 </dependency>
33 <dependency>
34 <groupId>log4j</groupId>
35 <artifactId>log4j</artifactId>
36 <version>1.2.16</version>
37 </dependency>
38 <dependency>
39 <groupId>junit</groupId>
40 <artifactId>junit</artifactId>
41 <version>4.12</version>
42 </dependency>
43 </dependencies>
44 </project>
至于,本文为何采用slf4j而不是直接采用log4j进行日志控制输出,详见另一篇博客:slf4j介绍以及实现原理窥探
9.日志输出格式文件
1 ### set log levels ###
2 log4j.rootLogger = DEBUG , stdout , D , E , F
3
4 ### \u8F93\u51FA\u5230\u63A7\u5236\u53F0 ###
5 log4j.appender.stdout = org.apache.log4j.ConsoleAppender
6 log4j.appender.stdout.Target = System.out
7 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
8 log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n
9
10 ## \u8F93\u51FA\u5230\u65E5\u5FD7\u6587\u4EF6 ###
11 log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
12 log4j.appender.D.DatePattern = '.'yyyyMMdd
13 log4j.appender.D.File = logs/api/warn.log
14 log4j.appender.D.Append = true
15 log4j.appender.D.Threshold = WARN
16 log4j.appender.D.layout = org.apache.log4j.PatternLayout
17 log4j.appender.D.encoding=UTF-8
18 log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n
19
20 ## \u4FDD\u5B58\u5F02\u5E38\u4FE1\u606F\u5230\u5355\u72EC\u6587\u4EF6 ###
21 log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
22 log4j.appender.E.File = logs/api/error.log
23 log4j.appender.E.DatePattern = '.'yyyyMMdd
24 log4j.appender.E.Append = true
25 log4j.appender.E.Threshold = ERROR
26 log4j.appender.E.layout = org.apache.log4j.PatternLayout
27 log4j.appender.E.encoding=UTF-8
28 log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n
29
30 ## \u4FDD\u5B58\u5F02\u5E38\u4FE1\u606F\u5230\u5355\u72EC\u6587\u4EF6 ###
31 log4j.appender.F = org.apache.log4j.DailyRollingFileAppender
32 log4j.appender.F.File = logs/api/debug.log
33 log4j.appender.F.DatePattern = '.'yyyyMMdd
34 log4j.appender.F.Append = true
35 log4j.appender.F.Threshold = DEBUG
36 log4j.appender.F.layout = org.apache.log4j.PatternLayout
37 log4j.appender.F.encoding=UTF-8
38 log4j.appender.F.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n
测试结果:
邮件实际收到的内容:
为了保证以后的兼容性,本例采用了工厂设计模式,有什么不正确的或需要改进的,请各位批评指导,谢谢!
附:项目结构图