首先,附上官网文档,请自行查阅:
https://docs.oracle.com/javaee/7/api/javax/mail/package-summary.html
常用邮箱服务器以及对应端口:
sina.com: POP3服务器地址:pop3.sina.com.cn(端口:110) SMTP服务器地址:smtp.sina.com.cn(端口:25) sinaVIP: POP3服务器:pop3.vip.sina.com (端口:110) SMTP服务器:smtp.vip.sina.com (端口:25) sohu.com: POP3服务器地址:pop3.sohu.com(端口:110) SMTP服务器地址:smtp.sohu.com(端口:25) 126邮箱: POP3服务器地址:pop.126.com(端口:110) SMTP服务器地址:smtp.126.com(端口:25) 139邮箱: POP3服务器地址:POP.139.com(端口:110) SMTP服务器地址:SMTP.139.com(端口:25) 163.com: POP3服务器地址:pop.163.com(端口:110) SMTP服务器地址:smtp.163.com(端口:25) QQ邮箱 POP3服务器地址:pop.qq.com(端口:110) SMTP服务器地址:smtp.qq.com (端口:25) QQ企业邮箱 POP3服务器地址:pop.exmail.qq.com (SSL启用 端口:995) SMTP服务器地址:smtp.exmail.qq.com(SSL启用 端口:587/465) yahoo.com: POP3服务器地址:pop.mail.yahoo.com SMTP服务器地址:smtp.mail.yahoo.com yahoo.com.cn: POP3服务器地址:pop.mail.yahoo.com.cn(端口:995) SMTP服务器地址:smtp.mail.yahoo.com.cn(端口:587 HotMail POP3服务器地址:pop3.live.com (端口:995) SMTP服务器地址:smtp.live.com (端口:587) gmail(google.com) POP3服务器地址:pop.gmail.com(SSL启用 端口:995) SMTP服务器地址:smtp.gmail.com(SSL启用 端口:587) 263.net: POP3服务器地址:pop3.263.net(端口:110) SMTP服务器地址:smtp.263.net(端口:25) 263.net.cn: POP3服务器地址:pop.263.net.cn(端口:110) SMTP服务器地址:smtp.263.net.cn(端口:25) x263.net: POP3服务器地址:pop.x263.net(端口:110) SMTP服务器地址:smtp.x263.net(端口:25) 21cn.com: POP3服务器地址:pop.21cn.com(端口:110) SMTP服务器地址:smtp.21cn.com(端口:25) Foxmail: POP3服务器地址:POP.foxmail.com(端口:110) SMTP服务器地址:SMTP.foxmail.com(端口:25) china.com: POP3服务器地址:pop.china.com(端口:110) SMTP服务器地址:smtp.china.com(端口:25) tom.com: POP3服务器地址:pop.tom.com(端口:110) SMTP服务器地址:smtp.tom.com(端口:25) etang.com: POP3服务器地址:pop.etang.com SMTP服务器地址:smtp.etang.com
邮件的发送过程大致如下:
创建一个 Session 对象。 Session 对象创建一个 Transport 对象 /Store 对象,用来发送 / 保存邮件。 Transport 对象 /Store 对象连接邮件服务器。 Transport 对象 /Store 对象创建一个 Message 对象 ( 也就是邮件内容 ) 。 Transport 对象发送邮件; Store 对象获取邮箱的邮件。
发送文本邮件最简代码,官方文档给出的如下:
//配置文件 Properties props = new Properties(); //设置邮箱服务器,有对应的默认端口 props.put("mail.smtp.host", "my-mail-server"); //获取一个邮箱session实例 Session session = Session.getInstance(props, null); try { //邮件发送工具类参数对象 MimeMessage msg = new MimeMessage(session); //设置发件人信息 msg.setFrom("me@example.com"); //设置收件人信息 msg.setRecipients(Message.RecipientType.TO, "you@example.com"); //设置邮件主题 msg.setSubject("JavaMail hello world example"); //设置邮件时间 msg.setSentDate(new Date()); //设置邮件内容 msg.setText("Hello, world!\n"); //发送邮件,验证邮箱账号和密码 Transport.send(msg, "me@example.com", "my-password"); } catch (MessagingException mex) { System.out.println("send failed, exception: " + mex); }
以上,就可以完成一个简单的邮件发送。
接下来,我们做一下优化,使其能够成为易于维护的工具类。
先建一个maven工程,pom.xml如下
<?xml version="1.0" encoding="UTF-8"?> <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>spring-boot</groupId> <artifactId>spring-study</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-boot-starter-test 起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>LATEST</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.29</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
我们最主要是添加 spring-boot-starter-mail 的起步依赖来实现邮件发送的功能。
MailAuthenticator ,用于用户登录认证
package demo.mail; import javax.mail.Authenticator; import javax.mail.PasswordAuthentication; public class MailAuthenticator extends Authenticator { /** * 用户名 */ private String userName; /** * 密码 */ private String userPass; public MailAuthenticator() { } public MailAuthenticator(String userName, String userPass) { this.userName = userName; this.userPass = userPass; } @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(userName, userPass); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPass() { return userPass; } public void setUserPass(String userPass) { this.userPass = userPass; } @Override public String toString() { return "MailAuthenticator{" + "userName='" + userName + '\'' + ", userPass='" + userPass + '\'' + '}'; } }
MailBean,邮件发送bean
package demo.mail; import java.util.List; public class MailBean { /** * 发件人地址 */ private String fromAddress; /** * 收件人地址 */ private String toAddress; /** * 邮箱用户名 */ private String userName; /** * 邮箱用户名密码 */ private String userPassword; /** * 多个收件人 */ private List<String> recipients; /** * 邮件主题 */ private String subject; /** * 邮件内容 */ private MailContent content; public String getFromAddress() { return fromAddress; } public void setFromAddress(String fromAddress) { this.fromAddress = fromAddress; } public String getToAddress() { return toAddress; } public void setToAddress(String toAddress) { this.toAddress = toAddress; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public List<String> getRecipients() { return recipients; } public void setRecipients(List<String> recipients) { this.recipients = recipients; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public MailContent getContent() { return content; } public void setContent(MailContent content) { this.content = content; } @Override public String toString() { return "MailBean{" + "fromAddress='" + fromAddress + '\'' + ", toAddress='" + toAddress + '\'' + ", userName='" + userName + '\'' + ", userPassword='" + userPassword + '\'' + ", recipients=" + recipients + ", subject='" + subject + '\'' + ", content=" + content + '}'; } }
Mail配置常量
package demo.mail; public class MailConstant { /** * smtp 服务器 */ public static final String MAIL_SMTP_HOST = "mail.smtp.host"; /** * smtp服务器的端口 */ public static final String MAIL_SMTP_PORT = "mail.smtp.port"; /** * 如果为true,STARTTLS则在发出任何登录命令之前,启用命令(如果服务器支持)将连接切换到TLS保护的连接。 * 请注意,必须配置适当得信任存储,以便客户端信任服务器得证书。默认为false */ public static final String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable"; /** * 如果为true,则尝试使用AUTH命令认证用户。默认为false. */ public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; /** * 指定使用指定套接字工厂时要连接的端口。如果未设置,将使用默认端口。 */ public static final String MAIL_SMTP_SOCKETFACTORY_PORT = "mail.smtp.socketFactory.port"; /** * 如果设置为true,则无法使用指定的套接字工厂类创建套接字将导致使用java.net.Socket该类创建套接字。默认为true. */ public static final String MAIL_SMTP_SOCKETFACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; /** * Transport对象才会向SMTP服务器提交用户认证信息 */ public static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol"; /** * 发件人地址 */ public static final String MAIL_FROM = "mail.from"; /** * ssl 被smtp认证 */ public static final String MAIL_SMTP_SSL_TRUST = "mail.smtp.ssl.trust"; /** * 用户名 */ public static final String MAIL_USERNAME = "mail.username"; /** * 密码 */ public static final String MAIL_CRYPTOGRAM = "mail.password"; /** * 私有构造器,坏味道 */ private MailConstant() { //do nothing } }
MailContent,用于存储邮件内容:
package demo.mail; public class MailContent { /** * 邮件主题 */ private String subject; /** * 内容时间 */ private String date; /** * 内容 */ private String content; public MailContent() { } public MailContent(String subject, String date, String content) { this.subject = subject; this.date = date; this.content = content; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "MailContent{" + "subject='" + subject + '\'' + ", date='" + date + '\'' + ", content='" + content + '\'' + '}'; } }
MailHtml,将邮件内容转换为Html形式
package demo.mail; public class MailHtml { public static String getMailHtml(String date,String sub,String content){ StringBuilder sb = new StringBuilder(); sb.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">") .append("<html>") .append("<head>") .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">") .append("<title>邮件</title>") .append("<style type=\"text/css\">") .append("table{width:100%;height:100%;border-collapse:collapse;border:none;}") .append("table td{border:solid gray 1px;}") .append(".td_1{width:20%;text-align:center;background-color:#cce8cf;}") .append("</style>") .append("</head>") .append("<body>") .append("<table>") .append("<tr style='height:15%;'><td class=\"td_1\">时间</td><td>"+date+"</td></tr>") .append("<tr style='height:15%;'><td class=\"td_1\">标题</td><td>"+sub+"</td></tr>") .append("<tr><td class=\"td_1\">内容</td><td>"+content+"</td></tr>") .append("</table>") .append("</body>") .append("</html>"); return sb.toString(); } private MailHtml() { throw new IllegalAccessError("MailHtml class"); } }
MailProperties,用于获取邮件配置文件信息
package demo.mail; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ResourceBundle; public class MailProperties { private static Logger logger = LoggerFactory.getLogger(MailProperties.class); private static final String BONDEL_NAME = "mail"; private static final ResourceBundle bundel = ResourceBundle.getBundle(BONDEL_NAME); public static String getValue(String key) { //参数校验 if (StringUtils.isBlank(key)) { System.out.println("发生了未知异常!"); } String value = null; try { value = bundel.getString(StringUtils.trim(key)); } catch (Exception e) { logger.warn(String.format("value not existed: %s", key), e); } return StringUtils.isBlank(value) ? StringUtils.EMPTY : value; } }
MailSend,邮件发送类:
package demo.mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.mail.*; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Date; import java.util.List; import java.util.Properties; public class MailSend { private static final Logger logger = LoggerFactory.getLogger(MailSend.class); /** * 发送邮件的props 文件 */ private static Properties props; /** * 邮件登录 权限验证 */ private static MailAuthenticator authenticator; /** * 邮件服务器session */ private static Session session; /** * 发送邮件 工具 */ private static Transport transport; /** * 邮件附件(目前还不支持附件上传的功能) */ private static Multipart multipart; /** * 初始化 * */ static { // 初始化props props = new Properties(); props.put(MailConstant.MAIL_SMTP_HOST, MailProperties.getValue(MailConstant.MAIL_SMTP_HOST)); props.put(MailConstant.MAIL_SMTP_PORT, MailProperties.getValue(MailConstant.MAIL_SMTP_PORT)); props.put(MailConstant.MAIL_SMTP_STARTTLS_ENABLE, MailProperties.getValue(MailConstant.MAIL_SMTP_STARTTLS_ENABLE)); props.put(MailConstant.MAIL_SMTP_AUTH, MailProperties.getValue(MailConstant.MAIL_SMTP_AUTH)); props.put(MailConstant.MAIL_SMTP_SOCKETFACTORY_PORT, MailProperties.getValue(MailConstant.MAIL_SMTP_SOCKETFACTORY_PORT)); props.put(MailConstant.MAIL_SMTP_SOCKETFACTORY_FALLBACK, MailProperties.getValue(MailConstant.MAIL_SMTP_SOCKETFACTORY_FALLBACK)); props.put(MailConstant.MAIL_TRANSPORT_PROTOCOL, MailProperties.getValue(MailConstant.MAIL_TRANSPORT_PROTOCOL)); props.put(MailConstant.MAIL_FROM, MailProperties.getValue(MailConstant.MAIL_FROM)); props.put(MailConstant.MAIL_SMTP_SSL_TRUST, MailProperties.getValue(MailConstant.MAIL_SMTP_SSL_TRUST)); // 验证 authenticator = new MailAuthenticator(MailProperties.getValue(MailConstant.MAIL_USERNAME), MailProperties.getValue(MailConstant.MAIL_CRYPTOGRAM)); // 创建session session = Session.getDefaultInstance(props, authenticator); } /** * 群发邮件 * * @param recipients 收件人们 * @param subject 主题 * @param content 内容 * @throws AddressException * @throws MessagingException */ @SuppressWarnings("static-access") protected static void send(List<String> recipients, String subject, MailContent content) { try { // 创建mime类型邮件 final MimeMessage message = new MimeMessage(session); // 设置发信人 message.setFrom(new InternetAddress(authenticator.getUserName())); // 设置收件人们 final int num = recipients.size(); InternetAddress[] addresses = new InternetAddress[num]; for (int i = 0; i < num; i++) { addresses[i] = new InternetAddress(recipients.get(i)); } message.setRecipients(Message.RecipientType.TO, addresses); // 设置主题 message.setSubject(subject); // 设置邮件内容 message.setContent(MailHtml.getMailHtml(content.getDate(), content.getSubject(), content.getContent()), "text/html;charset=utf-8"); // 发送时间 message.setSentDate(new Date()); message.saveChanges(); // 发送 logger.info("===protocol is ============" + props.get(MailConstant.MAIL_TRANSPORT_PROTOCOL) ); transport = session.getTransport(((String) props.get(MailConstant.MAIL_TRANSPORT_PROTOCOL)).trim()); // 真正的连接邮件服务器并进行身份验证 transport.connect((String.valueOf(props.get(MailConstant.MAIL_SMTP_HOST)).trim()), Integer.parseInt(String.valueOf(props.get(MailConstant.MAIL_SMTP_PORT))), authenticator.getUserName(), authenticator.getUserPass()); transport.send(message); } catch (Exception e) { logger.error("mailsend send error : ", e); } finally { try { transport.close(); } catch (MessagingException e) { logger.error("mailsend send close error : ", e); } } } }
SendMailThread,用于控制最大并发
package demo.mail; public class SendMailThread implements Runnable{ private MailBean mailBean; public SendMailThread(MailBean mailBean) { this.mailBean = mailBean; } public MailBean getMailBean() { return mailBean; } public void setMailBean(MailBean mailBean) { this.mailBean = mailBean; } @Override public void run() { MailUtil.send(mailBean); } }
MailUtil,邮件发送工具类,将邮件发送类注入静态方法
package demo.mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Service public class MailUtil { private static final Logger logger = LoggerFactory.getLogger(MailUtil.class); //线程池核心线程数 private static final int COREPOOLSIZE = 1; //线程池最大线程数 private static final int MAXMUMPOOLSIZE = 10; //超过核心线程数,空闲线程等待任务的最长时间 单位 毫秒,0 的意思是 空闲线程直接销毁 private static final long KEEPALIVETIME = 0; //当线程池中线程数等于核心线程数时,新创建的线程任务放到阻塞队列中去 private static final BlockingQueue<Runnable> EMAIL_QUEUE = new LinkedBlockingDeque<>(10); ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(COREPOOLSIZE, MAXMUMPOOLSIZE, KEEPALIVETIME, TimeUnit.SECONDS, EMAIL_QUEUE, new ThreadPoolExecutor.AbortPolicy()); public static void send(MailBean mailBean) { try { MailSend.send(mailBean.getRecipients(), mailBean.getSubject(), mailBean.getContent()); } catch (Exception e) { logger.error("mailutil error : ", e); }finally { logger.info("邮件发送 成功了!"); } } }
.properties,配置文件
#smtp服务器 mail.smtp.host=smtp.163.com #smtp服务器的端口 mail.smtp.port=25 #如果为true,STARTTLS则在发出任何登录命令之前,启用使用命令(如果服务器支持)将连接切换到TLS保护的连接。请注意,必须配置适当的信任存储,以便客户端信任服务器的证书。默认为false。 mail.smtp.starttls.enable=true #如果为true,则尝试使用AUTH命令认证用户。默认为false。 mail.smtp.auth=true #指定使用指定套接字工厂时要连接的端口。如果未设置,将使用默认端口。 mail.smtp.socketFactory.port=25 #如果设置为true,则无法使用指定的套接字工厂类创建套接字将导致使用java.net.Socket该类创建套接字 。默认为true。 mail.smtp.socketFactory.fallback=false #Tranport对象才会向SMTP服务器提交用户认证信息 mail.transport.protocol=smtp mail.from=xxx@163.com #ssl被smtp认证 mail.smtp.ssl.trust=smtp.163.com #邮箱用户名 mail.username=xxx@163.com #邮箱密码 mail.password=password
单元测试,要将邮件服务器,和验证发件人和密码改成对应的
package demo.mail; import demo.DemoApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import java.util.ArrayList; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK) public class MailUtilTest { @Autowired private MailUtil mailUtil; @Test public void send() throws Exception { MailBean mailBean = new MailBean(); List<String> internetAddresses = new ArrayList<>(); internetAddresses.add("xiaomingtongxie@163.com"); MailContent mailContent = new MailContent(); mailContent.setContent("邮件内容为:23333333"); mailBean.setSubject("测试邮件发送"); mailBean.setRecipients(internetAddresses); mailBean.setContent(mailContent); mailUtil.send(mailBean); } }
感谢您的阅读,如果觉得对您有用,记得点赞哦。
先附上源码地址,博客有时间再更新
https://github.com/xiaomingtongxie/springboot-study.git
码云地址:
https://gitee.com/xiaomingnevermind/springboot-study.git
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句