Springboot之邮件发送(内附源码)

目录

    • 概述
    • 邮件协议简介
    • 常用SMTP服务器地址
    • 准备工作
    • 授权密码申请
      • 1.进入邮箱设置
      • 2.开启SMTP服务
    • coding
      • 1.Maven依赖添加
      • 2.yml配置
      • 3.创建参数实体
      • 4.创建业务调用类
      • 5.测试方法
      • 6.邮件发送结果
    • 异常
      • 1.535 Error: authentication failed
      • 2.java.net.ConnectException: Operation timed out (Connection timed out)
    • 尾声

概述

   SpringBoot用过吧?邮件发过吧?那你知道如何用SpringBoot实现邮件发送功能么?

邮件协议简介

我们简单了解一下邮件协议
   POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。
   SMTP是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。
   IMAP全称是Internet Mail Access Protocol,即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。
我们通过一个图片来看一下邮件发送的过程。
简单来说SMTP是发送端使用的协议,我们的邮件通过客户端先行发送至SMTP服务器,最后由POP3服务器进行接收后由收件人客户端进行读取。

Springboot之邮件发送(内附源码)_第1张图片

常用SMTP服务器地址

邮箱类型 服务器地址 端口号
QQ邮箱 smtp.qq.com 465或587
sina邮箱 smtp.sina.cn 465或587
126邮箱 smtp.126.com 465或994
aliyun邮箱 smtp.aliyun.com 465或994
163邮箱 smtp.163.com 465或994
yeah邮箱 smtp.yeah.net 465或994

准备工作

我们以yml为例,看看需要哪些主要配置进行发送邮件

spring:
  #邮箱基本配置
  mail:
    #配置smtp服务主机地址
    host: smtp.163.com
    #发送者邮箱
    username: [email protected]
    #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
    password: xxxxxxxxxxx

配置做个简单解释,我们作为邮件的发送端,需要对以下几个配置比较清晰:
1.host是填写发送邮箱对应的SMTP服务器地址,例如狗哥使用的是163邮箱,则填写smtp.163.com
2.username邮箱账号,这里的邮箱是指发件箱
3.password 授权密码,这里不是邮箱的密码,是需要在邮箱中申请的密码
4.如果没有指定协议,默认协议是SMTP

授权密码申请

我们还以163邮箱为例,看看如何申请授权密码。

1.进入邮箱设置

Springboot之邮件发送(内附源码)_第2张图片

2.开启SMTP服务

Springboot之邮件发送(内附源码)_第3张图片

一般在开启的时候,就会让你通过短信验证的方式去生成授权码。还可以通过下面的添加设备进行新增授权码,当然授权码记得保存好,因为它只显示一次,要是忘了的话只能重新生成。
Springboot之邮件发送(内附源码)_第4张图片

coding

1.Maven依赖添加

		<dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-mailartifactId>
        dependency>
        
        <dependency>
            <groupId>com.sun.mailgroupId>
            <artifactId>jakarta.mailartifactId>
            <version>1.6.7version>
        dependency>

2.yml配置

spring:
  #邮箱基本配置
  mail:
    #配置smtp服务主机地址
    host: smtp.163.com
    #发送者邮箱
    username: [email protected]
    #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
    password: xxxxxxxxxxx
    #端口号465或994
    port: 465
    #默认的邮件编码为UTF-8
    default-encoding: UTF-8
    #其他参数
    properties:
     mail:
        #配置SSL 加密工厂
      smtp:
        ssl:
          #本地测试,先放开ssl
          enable: true
          required: true
        #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
      debug: true

3.创建参数实体

@Data
public class EmailRequest implements Serializable {

    /**
     * 接收人
     */
    private String sendTo;

    /**
     *  邮件主题
     */
    private String subject;

    /**
     *  邮件内容
     */
    private String text;

    /**
     *  附件路径
     */
    private String filePath;
}

4.创建业务调用类

@Service
public class EmailService {

        private static final Logger logger = LoggerFactory.getLogger(EmailService.class);

        @Resource
        private JavaMailSender javaMailSender;

        @Value("${spring.mail.username}")
        private String sendMailer;

        public void checkMail(EmailRequest mailRequest) {
            Assert.notNull(mailRequest,"邮件请求不能为空");
            Assert.notNull(mailRequest.getSendTo(), "邮件收件人不能为空");
            Assert.notNull(mailRequest.getSubject(), "邮件主题不能为空");
            Assert.notNull(mailRequest.getText(), "邮件收件人不能为空");
        }

        public void sendMail(EmailRequest mailRequest) {
            MimeMessage message = javaMailSender.createMimeMessage();
            checkMail(mailRequest);
            try {
                MimeMessageHelper helper = new MimeMessageHelper(message,true);
                //邮件发件人
                helper.setFrom(sendMailer);
                //邮件收件人 1或多个
                helper.setTo(mailRequest.getSendTo().split(","));
                //邮件主题
                helper.setSubject(mailRequest.getSubject());
                //邮件内容
                helper.setText(mailRequest.getText(),true);
                //邮件发送时间
                helper.setSentDate(new Date());

                String filePath = mailRequest.getFilePath();
                if (StringUtils.hasText(filePath)) {
                    FileSystemResource file = new FileSystemResource(new File(filePath));
                    String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
                    helper.addAttachment(fileName,file);
                }
                javaMailSender.send(message);
                logger.info("发送邮件成功:{}->{}",sendMailer,mailRequest.getSendTo());
            } catch (MessagingException e) {
                logger.error("发送邮件时发生异常!",e);
            }
        }
}

5.测试方法

@RestController
public class EmailController {

    @Resource
    private EmailService service;

    @GetMapping("/send")
    public void send() {
             EmailRequest request = new EmailRequest();
        request.setSendTo("[email protected]");
        request.setSubject("邮件主题");
        request.setText("祝大家中秋节快乐,天天无bug");
        request.setFilePath("/Users/scott/tmp/logs/moon.jpeg");
        service.sendMail(request);
    }
}

6.邮件发送结果

从控制台我们可以看到邮件发送结果,我们再来看看邮箱是否收到?
在这里插入图片描述
Springboot之邮件发送(内附源码)_第5张图片
点开附件一探究竟

异常

有一说一,本次coding中异常还是出现了,失败乃成功之母,我们来一探究竟。

1.535 Error: authentication failed

javax.mail.AuthenticationFailedException: 535 Error: authentication failed

	at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:947) ~[jakarta.mail-1.6.7.jar:1.6.7]
	at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:858) ~[jakarta.mail-1.6.7.jar:1.6.7]
	at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:762) ~[jakarta.mail-1.6.7.jar:1.6.7]
	at javax.mail.Service.connect(Service.java:342) ~[jakarta.mail-1.6.7.jar:1.6.7]
	at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518) ~[spring-context-support-5.3.22.jar:5.3.22]
	at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437) ~[spring-context-support-5.3.22.jar:5.3.22]
	at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361) ~[spring-context-support-5.3.22.jar:5.3.22]
	at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356) ~[spring-context-support-5.3.22.jar:5.3.22]
	at com.scott.springbootemail.service.EmailService.sendMail(EmailService.java:61) ~[classes/:na]
	at com.scott.springbootemail.controller.EmailController.send(EmailController.java:23) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_333]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_333]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_333]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_333]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.22.jar:5.3.22]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1070) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.22.jar:5.3.22]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.22.jar:5.3.22]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.22.jar:5.3.22]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.22.jar:5.3.22]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.65.jar:9.0.65]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_333]

这个535 Error: authentication failed异常产生的原因是我的发件人邮箱配置和SMTP服务器不匹配造成的,当你使用163邮箱但是配置的SMTP服务器并不是对应的地址时会出现这个异常。

2.java.net.ConnectException: Operation timed out (Connection timed out)

nested exception is:
	java.net.ConnectException: Operation timed out (Connection timed out). Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 33; timeout -1;
  nested exception is:
	java.net.ConnectException: Operation timed out (Connection timed out); message exceptions (1) are:
Failed message 1: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.163.com, 33; timeout -1;
  nested exception is:
	java.net.ConnectException: Operation timed out (Connection timed out)] with root cause

这个最后检查是自己邮件服务器端口配置错误。

尾声

写到这里,有没有想法去做一个节日邮件发送服务呢,给自己在意的人,每逢佳节自动送上一封节日的祝福。在这里狗哥也提前祝大家中秋快乐。

源码传送门

你可能感兴趣的:(java,spring,boot,服务器,后端)