一、背景

  最近朋友的公司有用到这个功能,之前对这一块也不是很熟悉,就和他一起解决出现的异常的同时,也初窥一下使用Apache Common Email组件进行邮件发送。

二、Java发送邮件的注意事项

  1.不同的邮箱有不同的支持协议,比如有些只支持SSL协议,有些只支持TLS协议,还有些同时支持SSL和TLS协议。

  2.支持不同协议的邮箱,在使用Java发送邮件时要使用不同的方式发送,下面我会介绍基于SSL和TLS的两种实现方式。

  附:常用邮箱的服务器(Smtp/POP3)地址和端口总结

三、代码实现

说明:本例采用Apache Common Email组件进行开发。

1.发送邮件实体类

 package com.hafiz.zhang.mail.entity;

 import java.io.Serializable;
import java.util.List; import org.apache.commons.mail.EmailAttachment; /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午2:40:36
* @Description 邮件实体类
*/
public class MailEntity implements Serializable{
private static final long serialVersionUID = 1589570366044890462L;
public static final String ENCODING = "UTF-8"; //邮件编码
private String host;  //服务器地址
private String port; //发送端口
private String sender; //发件人邮箱
private List<String> receiver; //收件人邮箱
private List<String> copier; //抄送人
private String senderName; //发件人昵称
private String userName; //发件邮箱账号
private String password; //发件邮箱密码
private String subject; //邮件主题
private String content; //邮件内容(支持HTML)
private List<EmailAttachment> attachments; //邮件附件
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public List<String> getReceiver() {
return receiver;
}
public void setReceiver(List<String> receiver) {
this.receiver = receiver;
}
public List<String> getCopier() {
return copier;
}
public void setCopier(List<String> copier) {
this.copier = copier;
}
public String getSenderName() {
return senderName;
}
public void setSenderName(String senderName) {
this.senderName = senderName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public List<EmailAttachment> getAttachments() {
return attachments;
}
public void setAttachments(List<EmailAttachment> attachments) {
this.attachments = attachments;
}
@Override
public String toString() {
return "MailEntity [host=" + host + ", port=" + port + ", sender=" + sender + ", receiver=" + receiver
+ ", copier=" + copier + ", senderName=" + senderName + ", userName=" + userName + ", password="
+ password + ", subject=" + subject + ", content=" + content + ", attachments=" + attachments + "]";
}
}

注:实体类代码比较臃肿,建议使用lombok进行简化,lombok的使用方式见我的另一篇博客:Java简化代码神器-Lombok

2.发送邮件支持协议列举类

 package com.hafiz.zhang.mail.tools.factory;

 /**
* @author hafiz.Zhang
* @Date 2016年5月25日 下午12:08:59
* @Description 列举发送邮件的可用协议
*/
public class Protocols {
//SSL协议
public static String SSL_MAIL_UTIL = "com.hafiz.zhang.mail.tools.SSLMailUtil";
//TLS协议
public static String TLS_MAIL_UTIL = "com.hafiz.zhang.mail.tools.TLSMailUtil";
}

3.发送邮件工具接口类

 package com.hafiz.zhang.mail.tools;

 import com.hafiz.zhang.mail.entity.MailEntity;

 /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午3:26:15
* @Description 邮件发送接口类
*/
public interface IMailUtil {
/**
* @Author hafiz.Zhang
* @Description: 发送邮件
* @param mail要发送邮件的实体类
* @return Boolean 发送结果
*/
public abstract Boolean send(MailEntity mail);
}

4.基于SSL协议发送邮件工具实现类

 package com.hafiz.zhang.mail.tools;

 import java.util.List;

 import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.hafiz.zhang.mail.entity.MailEntity; /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午3:09:13
* @Description 通过SSL方式发送邮件工具类
*/
public class SSLMailUtil implements IMailUtil{
private static Logger log = LoggerFactory.getLogger(SSLMailUtil.class); public Boolean send(MailEntity mail) {
// 创建待发送的emil对象
HtmlEmail email = new HtmlEmail();
try { // 设置SMTP发送服务器的名字:163的如下:"smtp.163.com"
email.setHostName(mail.getHost());
//设置端口号
if(null != mail.getPort()) {
email.setSmtpPort(Integer.parseInt(mail.getPort()));
}
// 设置邮件字符编码集
email.setCharset(MailEntity.ENCODING);
// 设置抄送人
email.addCc(mail.getCopier().toArray(new String[mail.getCopier().size()]));
// 设置发送人的邮箱
email.setFrom(mail.getSender(), mail.getSenderName());
// 收件人的邮箱
email.addTo(mail.getReceiver().toArray(new String[mail.getReceiver().size()]));
// 如果需要认证信息的话,设置认证:用户名-密码。分别为发件人在邮件服务器上的注册名称和密码
email.setAuthentication(mail.getUserName(), mail.getPassword());
// 设置要发送的邮件主题
email.setSubject(mail.getSubject());
// 设置要发送的信息,由于使用了HtmlEmail,可以在邮件内容中使用HTML标签
email.setMsg(mail.getContent());
// 设置附件
List<EmailAttachment> attachments = mail.getAttachments();
if(null != attachments && attachments.size() > 0) {
for(EmailAttachment attachment : attachments) {
email.attach(attachment);
}
}
// 发送
email.send(); log.info(mail.getSender() + " 发送邮箱到 " + mail.getReceiver()); return true;
} catch (EmailException e) {
log.error(mail.getSender() + " 发送邮箱到 " + mail.getReceiver() + " 失败,错误原因:" + e.getMessage());
e.printStackTrace();
}
return false;
}
}

5.基于TLS协议的发送邮件实现类

 package com.hafiz.zhang.mail.tools;

 import java.util.List;
import java.util.Properties; import javax.mail.Session; import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.hafiz.zhang.mail.entity.MailEntity; /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午3:23:15
* @Description 通过TLS协议发送邮件工具类
*/
public class TLSMailUtil implements IMailUtil{
private static Logger log = LoggerFactory.getLogger(TLSMailUtil.class); public Boolean send(MailEntity mail){
HtmlEmail email = new HtmlEmail();
try {
Properties prop = new Properties();
// 设置SMTP发送服务器的名字:163的如下:"smtp.163.com"
prop.setProperty("mail.smtp.host", mail.getHost());
// 设置SMTP发送服务器的端口
prop.setProperty("mail.smtp.port", mail.getPort());
// 设置是否需要认证
prop.setProperty("mail.smtp.auth", "true");
// 开启TLS加密方式
prop.setProperty("mail.smtp.starttls.enable", "true");
// 添加信任的服务器
prop.setProperty("mail.smtp.ssl.trust", mail.getHost());
// 进行认证并获取需要的session
DefaultAuthenticator defaultAuthenticator =
new DefaultAuthenticator(mail.getUserName(), mail.getPassword());
Session session = Session.getInstance(prop,defaultAuthenticator);
email.setMailSession(session);
// 设置字符编码集
email.setCharset(MailEntity.ENCODING);
// 设置发送人的邮箱
email.setFrom(mail.getSender(), mail.getSenderName());
// 设置收件人的邮箱
email.addTo(mail.getReceiver().toArray(new String[mail.getReceiver().size()]));
// 设置抄送人
email.addCc(mail.getCopier().toArray(new String[mail.getCopier().size()]));
// 设置要发送的邮件主题
email.setSubject(mail.getSubject());
// 设置要发送的信息,由于使用了HtmlEmail,可以在邮件内容中使用HTML标签
email.setMsg(mail.getContent());
// 设置附件
List<EmailAttachment> attachments = mail.getAttachments();
if(null != attachments && attachments.size() > 0) {
for(EmailAttachment attachment : attachments) {
email.attach(attachment);
}
}
// 发送
email.send();
log.info(mail.getSender() + " 发送邮箱到 " + mail.getReceiver());
return true;
} catch (EmailException e) {
log.error(mail.getSender() + " 发送邮箱到 " + mail.getReceiver() + " 失败,错误原因:" + e.getMessage());
e.printStackTrace();
}
return false;
}
}

注意:

  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.获取邮件发送类的工厂类

 package com.hafiz.zhang.mail.tools.factory;

 import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.hafiz.zhang.mail.tools.IMailUtil; /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午3:43:15
* @Description 获取邮件发送类的工厂类
*/
public class MailUtilsFactory {
private static Logger logger = LoggerFactory.getLogger(MailUtilsFactory.class); private MailUtilsFactory() {
super();
} public static IMailUtil getMailUtil(String name) {
IMailUtil mailUtil = null;
try {
mailUtil = (IMailUtil)Class.forName(name).newInstance();
} catch (Exception e) {
logger.error("获取邮件发送类失败,失败原因:" + e.getMessage());
e.printStackTrace();
}
return mailUtil;
}
}

7.测试类

 package com.hafiz.zhang.mail.test;

 import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List; import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailAttachment; import com.hafiz.zhang.mail.entity.MailEntity;
import com.hafiz.zhang.mail.tools.IMailUtil;
import com.hafiz.zhang.mail.tools.factory.MailUtilsFactory;
import com.hafiz.zhang.mail.tools.factory.Protocols; /**
* @author hafiz.Zhang
* @Date 2016年5月23日 下午3:39:52
* @Description 两种方式发送邮件的测试类
*/
public class TestMailSender {
public static void main(String[] args) throws MalformedURLException {
MailEntity mail = new MailEntity();
mail.setHost("smtp.163.com");
mail.setSender("xxx@163.com");
mail.setPort("25");
mail.setUserName("xxx@163.com");
mail.setPassword("xxxxx");
//设置收件人
List<String> receiver = new ArrayList<String>();
receiver.add("xxx@qq.com");
     //设置抄送人
List<String> copier = new ArrayList<String>();
copier.add("xxx@163.com"); List<EmailAttachment> attachments = new ArrayList<EmailAttachment>();
// 添加本地附件
EmailAttachment att = new EmailAttachment();
att.setDescription("这是附件图片");
att.setDisposition(EmailAttachment.ATTACHMENT);
att.setName("好看的图片.png");
att.setPath("C:\\Users\\ZHF\\Desktop\\333.png");
attachments.add(att);
// 添加网络附件
EmailAttachment att2 = new EmailAttachment();
att2.setDescription("这是网络附件");
att2.setDisposition(EmailAttachment.ATTACHMENT);
att2.setName("网络附件.jpg");
att2.setURL(new URL("http://7xpsw5.com1.z0.glb.clouddn.com/41a9ccf2-2a22-44bd-aa3b-8e8779db1caf.jpg"));
attachments.add(att2); mail.setAttachments(attachments);
mail.setCopier(copier);
mail.setReceiver(receiver);
mail.setSubject("测试邮件发送主题");
     // String string = "<html><head></head><body><div>测试Java发送邮件内容</div></body></html>";
String string = "测试Java发送邮件内容";
mail.setContent(string);
// IMailUtil mailUtil = MailUtilsFactory.getMailUtil(Protocols.SSL_MAIL_UTIL);
IMailUtil mailUtil = MailUtilsFactory.getMailUtil(Protocols.TLS_MAIL_UTIL);
mailUtil.send(mail);
System.out.println("邮件发送成功");
}
}

8.工程pom.xml文件(由于本例使用maven工程)

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.hafiz.zhang</groupId>
<artifactId>sendEmail</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>sendEmail</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>

至于,本文为何采用slf4j而不是直接采用log4j进行日志控制输出,详见另一篇博客:slf4j介绍以及实现原理窥探

9.日志输出格式文件

 ### set log levels ###
log4j.rootLogger = DEBUG , stdout , D , E , F ### \u8F93\u51FA\u5230\u63A7\u5236\u53F0 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n ## \u8F93\u51FA\u5230\u65E5\u5FD7\u6587\u4EF6 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.DatePattern = '.'yyyyMMdd
log4j.appender.D.File = logs/api/warn.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = WARN
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.encoding=UTF-8
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n ## \u4FDD\u5B58\u5F02\u5E38\u4FE1\u606F\u5230\u5355\u72EC\u6587\u4EF6 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = logs/api/error.log
log4j.appender.E.DatePattern = '.'yyyyMMdd
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.encoding=UTF-8
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n ## \u4FDD\u5B58\u5F02\u5E38\u4FE1\u606F\u5230\u5355\u72EC\u6587\u4EF6 ###
log4j.appender.F = org.apache.log4j.DailyRollingFileAppender
log4j.appender.F.File = logs/api/debug.log
log4j.appender.F.DatePattern = '.'yyyyMMdd
log4j.appender.F.Append = true
log4j.appender.F.Threshold = DEBUG
log4j.appender.F.layout = org.apache.log4j.PatternLayout
log4j.appender.F.encoding=UTF-8
log4j.appender.F.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %p ] [ %l-%t:%r ] %m%n

测试结果:

邮件实际收到的内容:

为了保证以后的兼容性,本例采用了工厂设计模式,有什么不正确的或需要改进的,请各位批评指导,谢谢!

附:项目结构图

Java发送邮件初窥的更多相关文章

  1. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

  2. 初窥ElasticSearch

    初窥ElasticSearch 官网上面的,不知道讲的是什么.. youtube上面有一个start with,内容是在windows以下跑这个elastic search,然后用一个fidler工具 ...

  3. 李洪强iOS开发之函数式 编程初窥

    函数式 编程初窥   最近在学习Erlang和Python.Erlang是完全的函数式编程语言,Python语言是面向对象的语言,但是它的语法引入了大量的函数式编程思想.越研究越觉得函数式的编程思路可 ...

  4. 初窥软件工程 2020BUAA软件工程$\cdot$个人博客作业

    初窥软件工程 2020BUAA软件工程\(\cdot\)个人博客作业 目录 初窥软件工程 2020BUAA软件工程$\cdot$个人博客作业 一.作业要求简介 二.正文 (一) 快速看完整部教材,列出 ...

  5. Scrapy001-框架初窥

    Scrapy001-框架初窥 @(Spider)[POSTS] 1.Scrapy简介 Scrapy是一个应用于抓取.提取.处理.存储等网站数据的框架(类似Django). 应用: 数据挖掘 信息处理 ...

  6. 初窥Kaggle竞赛

    初窥Kaggle竞赛 原文地址: https://www.dataquest.io/mission/74/getting-started-with-kaggle 1: Kaggle竞赛 我们接下来将要 ...

  7. scrapy2_初窥Scrapy

    递归知识:oop,xpath,jsp,items,pipline等专业网络知识,初级水平并不是很scrapy,可以从简单模块自己写. 初窥Scrapy Scrapy是一个为了爬取网站数据,提取结构性数 ...

  8. WWDC15 Session笔记 - Xcode 7 UI 测试初窥

    https://onevcat.com/2015/09/ui-testing/ WWDC15 Session笔记 - Xcode 7 UI 测试初窥 Unit Test 在 iOS 开发中已经有足够多 ...

  9. 【软件工程】week5-个人作业-敏捷开发方法初窥

    敏捷开发方法初窥 引言:本周的软件工程个人博客作业是阅读关于敏捷开发方法的文章(http://martinfowler.com/agile.html),并撰写自己的读后感.文章内容非常丰富,对敏捷开发 ...

随机推荐

  1. SpringMVC 表单标签

    引入标签库 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" ...

  2. 利用session防止用户未经登录而直接访问

    在编写项目的时候,突然想如果按常理出牌,不首先进入登录界面而直接访问网页内容,可不可以呢?如此一来便尝试了一下,整的可以直接进入管理员页面,获取完全的管理权限.于是在网上查看了一下解决方案,学习了一下 ...

  3. UI第二节——UIButton详解

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launc ...

  4. 如何居中一个div?

    CSS 实现垂直居中的几种方案   说到居中,很多人第一反应应该是水平居中,说到水平居中,肯定道友们有一万种方法做到,CSS3 的FlexBox更是强大到没朋友.但是微笑今天想聊的是 CSS 垂直居中 ...

  5. Js控制iFrame切换加载网址

    <html> <head> <title>Js控制 iFrame 切换加载网址</title> </head> <body> & ...

  6. C#GDI+基础(三)画刷详解

    SolidBrush:一般的画刷,通常只用一种颜色去填充GDI+图形 创建一般画刷: SolidBrush sbBrush1 = new SolidBrush(Color.Green); HatchB ...

  7. wp手机 htc x310e

    入手htc x310e 手机不错,用着流畅 不习惯,已升到wp7.8,系统限制还是有些需要的功能没有,比如说短信拦截什么的 我需要的常用软件少 转手了 1 注销windows live? 设置--应用 ...

  8. 使用Ant部署应用程序系统

    1. 首先下载Ant http://ant.apache.org/ 配置环境变量 2. 编写build.xml部署文件如下: <?xml version="1.0" enco ...

  9. css弹性布局

    1.弹性布局是什么 在移动端一种方便的布局方式,打破了之前用浮动,定位的布局,更加灵活. 2.弹性布局的格式 包含父元素和子元素,有对应的属性应用在父元素和子元素达到布局的目的 3.父元素的属性 要开 ...

  10. Pythhon 字典 key in dict 比 dict.has_key (key)效率高 为什么?

    has_key是去取key对应的值,时间复杂度在最优情况下为O(1); in 是直接去dict.__contains__这个保存这key的list中去获取,相当与是去数组中获取. 所以in 比has_ ...