Java 发送邮件的多种方式

Spring Boot发送邮件

邮件发送其实是一个非常常见的需求,用户注册,找回密码等地方,都会用到,使用 JavaSE 代码发送邮件,步骤还是挺繁琐的,Spring Boot 中对于邮件发送,提供了相关的自动化配置类,使得邮件发送变得非常容易,本文我们就来一探究竟!看看使用 Spring Boot 发送邮件的 5 中姿势

邮件基础

我们经常会听到各种各样的邮件协议,比如 SMTP、POP3、IMAP ,那么这些协议有什么作用,有什么区别?我们先来讨论一下这个问题。

SMTP 是一个基于 TCP/IP 的应用层协议,江湖地位有点类似于 HTTP,SMTP 服务器默认监听的端口号为 25 。看到这里,小伙伴们可能会想到既然 SMTP 协议是基于 TCP/IP 的应用层协议,那么我是不是也可以通过 Socket 发送一封邮件呢?回答是肯定的。

邮件的发送过程分三个步骤,假设 qq@qq.com 发送邮件 到 163@163.com

  1. qq@qq.com 先将邮件投递到腾讯服务器
  2. 腾讯服务器将我们的邮件投递到网易的邮件服务器
  3. 163@163.com 登录网易的邮箱服务器查看邮件

邮件投递大致就是这个过程,这个过程就涉及到了多个协议,我们来分别看一下。

SMTP 协议全称为 Simple Mail Transfer Protocol,译作简单邮件传输协议,它定义了邮件客户端软件与 SMTP 服务器之间,以及 SMTP 服务器与 SMTP 服务器之间的通信规则。

也就是说 qq@qq.com 用户先将邮件投递到腾讯的 SMTP 服务器这个过程就使用了 SMTP 协议,然后腾讯的 SMTP 服务器将邮件投递到网易的 SMTP 服务器这个过程也依然使用了 SMTP 协议,SMTP 服务器就是用来收邮件。

而 POP3 协议全称为 Post Office Protocol ,译作邮局协议,它定义了邮件客户端与 POP3 服务器之间的通信规则,那么该协议在什么场景下会用到呢?当邮件到达网易的 SMTP 服务器之后, 163@163.com 用户需要登录服务器查看邮件,这个时候就该协议就用上了:邮件服务商都会为每一个用户提供专门的邮件存储空间,SMTP 服务器收到邮件之后,就将邮件保存到相应用户的邮件存储空间中,如果用户要读取邮件,就需要通过邮件服务商的 POP3 邮件服务器来完成。

准备工作

目前国内大部分的邮件服务商都不允许直接使用用户名/密码的方式来在代码中发送邮件,都是要先申请授权码,这里以 QQ 邮箱为例,向大家演示授权码的申请流程:首先我们需要先登录 QQ 邮箱网页版,点击上方的设置按钮:

[外链图片转存失败(img-iPTAGjt7-1563437309715)(..\typora-user-images\1563420394330.png)]

在账户选项卡中找到开启POP3/SMTP选项,如下:

[外链图片转存失败(img-tSw5p3Cx-1563437309716)(..\typora-user-images\1563420442370.png)]

点击开启,开启相关功能,开启过程需要手机号码验证,按照步骤操作即可,不赘述。开启成功之后,即可获取一个授权码,将该号码保存好,一会使用。

项目创建

Spring Boot 中,对于邮件发送提供了自动配置类,开发者只需要加入相关依赖,然后配置一下邮箱的基本信息,就可以发送邮件了

创建

首先创建一个 Spring Boot 项目,引入邮件发送依赖:

在这里插入图片描述

创建完成后,项目依赖如下:

 		<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>

配置邮箱基本信息

项目创建成功后,接下来在 application.properties 中配置邮箱的基本信息:

spring.mail.host=smtp.qq.com  ## 配置 SMTP 服务器地址
spring.mail.port=587  ##SMTP 服务器的端口
spring.mail.username=2289359823@qq.com ##配置邮箱用户名
spring.mail.password=开启后的密钥 ##配置密码,注意,不是真正的密码,而是刚刚申请到的授权码
spring.mail.default-encoding=UTF-8 ##默认的邮件编码
spring.mail.properties.mail.smtp.socketFactoryClass=javax.net.ssl.SSLSocketFactory ##配饰 SSL 加密工厂
spring.mail.properties.mail.debug=true ## 表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误

如果不知道 smtp 服务器的端口或者地址的的话,可以参考 腾讯的邮箱文档

做完这些之后,Spring Boot 就会自动帮我们配置好邮件发送类,相关的配置在 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration 类中,部分源码如下

@Configuration
@ConditionalOnClass({MimeMessage.class, MimeType.class, MailSender.class})
@ConditionalOnMissingBean({MailSender.class})
@Conditional({MailSenderAutoConfiguration.MailSenderCondition.class})
@EnableConfigurationProperties({MailProperties.class})
@Import({MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class})
public class MailSenderAutoConfiguration {
    public MailSenderAutoConfiguration() {
    }

    static class MailSenderCondition extends AnyNestedCondition {
        MailSenderCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty(
            prefix = "spring.mail",
            name = {"jndi-name"}
        )
        static class JndiNameProperty {
            JndiNameProperty() {
            }
        }

        @ConditionalOnProperty(
            prefix = "spring.mail",
            name = {"host"}
        )
        static class HostProperty {
            HostProperty() {
            }
        }
    }
}

从这段代码中,可以看到,导入了另外一个配置 MailSenderPropertiesConfiguration 类,这个类中,提供了邮件发送相关的工具类:

@Configuration
@ConditionalOnProperty(
    prefix = "spring.mail",
    name = {"host"}
)
class MailSenderPropertiesConfiguration {
    private final MailProperties properties;

    MailSenderPropertiesConfiguration(MailProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    public JavaMailSenderImpl mailSender() {
        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        this.applyProperties(sender);
        return sender;
    }
   }
    

可以看到,这里创建了一个 JavaMailSenderImpl 的实例, JavaMailSenderImpl 是 JavaMailSender 的一个实现,我们将使用 JavaMailSenderImpl 来完成邮件的发送工作。

做完如上两步,邮件发送的准备工作就算是完成了,接下来就可以直接发送邮件了。

具体的发送,有 5 种不同的方式.

发送简单邮件

简单邮件就是指邮件内容是一个普通的文本文档:

   @Autowired
    JavaMailSender javaMailSender;
    
    @Test
    public void sendSimpleMail() {
        SimpleMailMessage message = new SimpleMailMessage(); //构建一个邮件对象
        message.setSubject("这是一封测试邮件"); //设置邮件主题
        message.setFrom("2289359823@qq.com"); //设置邮件发送者
        message.setTo(new String[]{"572906901@qq.com","lic@sunmnet.com"}); //设置邮件接收者,可以有多个接收者
        message.setCc("mengdy@sunmnet.com"); //设置邮件抄送人,可以有多个抄送人
        message.setBcc("1791926069@qq.com"); //设置隐秘抄送人,可以有多个
        message.setSentDate(new Date()); //设置邮件发送日期
        message.setText("这是测试邮件的正文"); //设置邮件的正文
        javaMailSender.send(message); //发送邮件
    }

最后执行该方法,就可以实现邮件的发送,发送效果图如下:

[外链图片转存失败(img-orXpzQIu-1563437309732)(..\typora-user-images\1563430031073.png)]

发送带附件的邮件

邮件的附件可以是图片,也可以是普通文件,都是支持的。

  @Test
    public void sendAttachFileMail() throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
        helper.setSubject("这是一封测试邮件");
        helper.setFrom("2289359823@qq.com"); //设置邮件发送者
        helper.setTo(new String[]{"572906901@qq.com","lic@sunmnet.com"}); //设置邮件接收者,可以有多个接收者
        helper.setCc("mengdy@sunmnet.com"); //设置邮件抄送人,可以有多个抄送人
        helper.setBcc("1791926069@qq.com"); //设置隐秘抄送人,可以有多个
        helper.setSentDate(new Date()); //设置邮件发送日期
        helper.setText("这是测试邮件的正文"); //设置邮件的正文
        helper.addAttachment("javaboy.jpg",new File("E:\\image\\hltx.jpg"));
        javaMailSender.send(mimeMessage);
    }

注意这里在构建邮件对象上和前文有所差异,这里是通过 javaMailSender 来获取一个复杂邮件对象,然后再利用 MimeMessageHelper 对邮件进行配置,MimeMessageHelper 是一个邮件配置的辅助工具类,创建时候的 true 表示构建一个 multipart message 类型的邮件,有了 MimeMessageHelper 之后,我们针对邮件的配置都是由 MimeMessageHelper 来代劳。

最后通过 addAttachment 方法来添加一个附件。

执行该方法,邮件发送效果图如下:

[外链图片转存失败(img-KvqD8Hud-1563437309734)(..\typora-user-images\1563430428401.png)]

发送带图片资源的邮件

图片资源和附件有什么区别呢?图片资源是放在邮件正文中的,即一打开邮件,就能看到图片。但是一般来说,不建议使用这种方式,一些公司会对邮件内容的大小有限制(因为这种方式是将图片一起发送的)。


    @Test
    public void sendImgResMail() throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setSubject("这是一封测试邮件");
        helper.setFrom("2289359823@qq.com"); //设置邮件发送者
        helper.setTo(new String[]{"572906901@qq.com","lic@sunmnet.com"}); //设置邮件接收者,可以有多个接收者
        helper.setCc("mengdy@sunmnet.com"); //设置邮件抄送人,可以有多个抄送人
        helper.setBcc("1791926069@qq.com"); //设置隐秘抄送人,可以有多个
        helper.setSentDate(new Date()); //设置邮件发送日期
        helper.setText("<p>hello 大家好,这是一封测试邮件,这封邮件包含两种图片,分别如下</p><p>第一张图片:</p><img src='cid:hltx'/><p>第二张图片:</p><img src='cid:bz'/>",true);
        helper.addInline("hltx",new FileSystemResource(new File("E:\\image\\hltx.jpg")));
        helper.addInline("bz",new FileSystemResource(new File("E:\\image\\bz.jpg")));
        javaMailSender.send(mimeMessage);
    }

这里的邮件 text 是一个 HTML 文本,里边涉及到的图片资源先用一个占位符占着,setText 方法的第二个参数 true 表示第一个参数是一个 HTML 文本。

setText 之后,再通过 addInline 方法来添加图片资源。

最后执行该方法,发送邮件,效果如下

[外链图片转存失败(img-pnVCpZvL-1563437309741)(..\typora-user-images\1563430766809.png)]

在公司实际开发中,第一种和第三种都不是使用最多的邮件发送方案。因为正常来说,邮件的内容都是比较的丰富的,所以大部分邮件都是通过 HTML 来呈现的,如果直接拼接 HTML 字符串,这样以后不好维护,为了解决这个问题,一般邮件发送,都会有相应的邮件模板。最具代表性的两个模板就是 Freemarker 模板和 Thyemeleaf 模板了。

使用 Freemarker 作邮件模板

首先需要引入 Freemarker 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

然后在 resources/templates 目录下创建一个 test.ftl 作为邮件发送模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>邮箱测试</title>
    <style type="text/css">
        table{border-collapse: collapse;margin: 0 auto;ext-align: center;}
        table td, table th{border: 1px solid #cad9ea;color: #666; height: 30px; }
        table thead th{background-color: #CCE8EB;width: 100px;}
        table tr:nth-child(odd){background: #fff;}
        table tr:nth-child(even) {background: #F5FAFA;}
    </style>
</head>
<body>
<p>这是邮箱测试   时间: ${date}</p>
<table width="90%" class="table">
    <#list userList as user>
        <tr>
            <td>姓名</td>
            <td>${user.name}</td>
        </tr>
        <tr>
            <td>年龄</td>
            <td>${user.age}</td>
        </tr>
        <tr>
            <td>性别</td>
            <td>${user.sex}</td>
        </tr>
    </#list>
</table>
<div style="color: #FF2785">666666666666</div>
</body>
</html>

接下来,将邮件模板渲染成 HTML ,然后发送即可

 @Test
    public void sendFreemarkerMail() throws MessagingException, IOException, TemplateException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setSubject("这是一封测试邮件");
        helper.setFrom("2289359823@qq.com"); //设置邮件发送者
        helper.setTo(new String[]{"57**01@qq.com","lic**net.com"}); //设置邮件接收者,可以有多个接收者
        helper.setCc("meng**mnet.com"); //设置邮件抄送人,可以有多个抄送人
        helper.setBcc("179***69@qq.com"); //设置隐秘抄送人,可以有多个
        helper.setSentDate(new Date()); //设置邮件发送日期
        helper.setSentDate(new Date());
        //构建 Freemarker 的基本配置
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
        // 配置模板位置
        ClassLoader loader = SpringBootMailApplication.class.getClassLoader();
        configuration.setClassLoaderForTemplateLoading(loader, "templates");
        //加载模板
        Template template = configuration.getTemplate("mail.ftl");
        MailTemplate mailTemplate = new MailTemplate();
        mailTemplate.setDate("2019-07-18 15:00:00");
        List<User> userList = new ArrayList<>();
        for (int i = 0; i <5 ; i++) {
            User user = new User();
            user.setName("姓名"+i);
            user.setAge(i);
            user.setSex(i%1 == 0?"男":"女");
            userList.add(user);
        }
        mailTemplate.setUserList(userList);
     
        StringWriter out = new StringWriter();
        //模板渲染,渲染的结果将被保存到 out 中 ,将out 中的 html 字符串发送即可
        template.process(mailTemplate, out);
        helper.setText(out.toString(),true);
        javaMailSender.send(mimeMessage);
    }

效果如下:

在这里插入图片描述

使用 Thymeleaf 作邮件模板

推荐在 Spring Boot 中使用 Thymeleaf 来构建邮件模板。因为 Thymeleaf 的自动化配置提供了一个 TemplateEngine,通过 TemplateEngine 可以方便的将 Thymeleaf 模板渲染为 HTML ,同时,Thymeleaf 的自动化配置在这里是继续有效的 。

首先,引入 Thymeleaf 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

然后,创建 Thymeleaf 邮件模板:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>邮箱测试</title>
    <style type="text/css">
        table{border-collapse: collapse;margin: 0 auto;ext-align: center;}
        table td, table th{border: 1px solid #cad9ea;color: #666; height: 30px; }
        table thead th{background-color: #CCE8EB;width: 100px;}
        table tr:nth-child(odd){background: #fff;}
        table tr:nth-child(even) {background: #F5FAFA;}
    </style>
</head>
<body>
<p>这是邮箱测试   时间:<p th:text = "${date}"></p></p>
<table width="90%" class="table">

    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>
    <tr th:each = "user : ${userList}">
        <td th:text="${user.name}"></td>
        <td th:text="${user.age}"></td>
        <td th:text="${user.sex}"></td>
    </tr>
</table>
<div style="color: #FF2785">666666666666</div>
</body>
</html>

接下来发送邮件:

@Autowired
    TemplateEngine templateEngine;

    @Test
    public void sendThymeleafMail() throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setSubject("这是一封测试邮件");
        helper.setFrom("2289359823@qq.com"); //设置邮件发送者
        helper.setTo("lic@sunmnet.com"); //设置邮件接收者,可以有多个接收者
        //helper.setCc("mengdy@sunmnet.com"); //设置邮件抄送人,可以有多个抄送人
       // helper.setBcc("1791926069@qq.com"); //设置隐秘抄送人,可以有多个
        helper.setSentDate(new Date());
        List<User> userList = new ArrayList<>();
        for (int i = 0; i <5 ; i++) {
            User user = new User();
            user.setName("姓名"+i);
            user.setAge(i);
            user.setSex(i%2 == 0?"男":"女");
            userList.add(user);
        }
        Context context = new Context();
        context.setVariable("date", "2019-07-18 15:00:00");
        context.setVariable("userList",userList);
        String process = templateEngine.process("mail.html", context);
        helper.setText(process,true);
        javaMailSender.send(mimeMessage);
    }

调用该方法,发送邮件,效果图如下

[外链图片转存失败(img-q7UN7MjA-1563437309746)(..\typora-user-images\1563436569785.png)]

GitHub地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值