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


    概述

       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服务器进行接收后由收件人客户端进行读取。

    在这里插入图片描述

    常用SMTP服务器地址

    邮箱类型服务器地址端口号
    QQ邮箱smtp.qq.com465或587
    sina邮箱smtp.sina.cn465或587
    126邮箱smtp.126.com465或994
    aliyun邮箱smtp.aliyun.com465或994
    163邮箱smtp.163.com465或994
    yeah邮箱smtp.yeah.net465或994

    准备工作

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

    spring:
      #邮箱基本配置
      mail:
        #配置smtp服务主机地址
        host: smtp.163.com
        #发送者邮箱
        username: xxxxxxx@163.com
        #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
        password: xxxxxxxxxxx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    授权密码申请

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

    1.进入邮箱设置

    在这里插入图片描述

    2.开启SMTP服务

    在这里插入图片描述

    一般在开启的时候,就会让你通过短信验证的方式去生成授权码。还可以通过下面的添加设备进行新增授权码,当然授权码记得保存好,因为它只显示一次,要是忘了的话只能重新生成。
    在这里插入图片描述

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.yml配置

    spring:
      #邮箱基本配置
      mail:
        #配置smtp服务主机地址
        host: smtp.163.com
        #发送者邮箱
        username: xxxxxxx@163.com
        #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
        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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.创建参数实体

    @Data
    public class EmailRequest implements Serializable {
    
        /**
         * 接收人
         */
        private String sendTo;
    
        /**
         *  邮件主题
         */
        private String subject;
    
        /**
         *  邮件内容
         */
        private String text;
    
        /**
         *  附件路径
         */
        private String filePath;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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);
                }
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    5.测试方法

    @RestController
    public class EmailController {
    
        @Resource
        private EmailService service;
    
        @GetMapping("/send")
        public void send() {
                 EmailRequest request = new EmailRequest();
            request.setSendTo("xxx@163.com");
            request.setSubject("邮件主题");
            request.setText("祝大家中秋节快乐,天天无bug");
            request.setFilePath("/Users/scott/tmp/logs/moon.jpeg");
            service.sendMail(request);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6.邮件发送结果

    从控制台我们可以看到邮件发送结果,我们再来看看邮箱是否收到?
    在这里插入图片描述
    在这里插入图片描述
    点开附件一探究竟
    在这里插入图片描述

    异常

    有一说一,本次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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    这个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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    尾声

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

    源码传送门😬😬😬

  • 相关阅读:
    来一套完整的面试题
    OFTP虚拟文件名设置
    数据仓库任务调度器-Azkaban | 案例测试<自动邮件报警通知机制>
    vue3 antd 表单校验与重置
    LabVIEW挖坑指南
    C++标准库(第二版,作者_NicolaiMJosuttis)_第5章通用工具5.2.1shared_ptr
    【毕业设计】 python小游戏设计 -吃豆人小游戏
    nodejs+vue国产动漫网站论坛数据智能分析系统python django
    ROS小车1_ssh远程登陆小车
    测试人生 | 97年双非学历的小哥哥,2线城市涨薪100%,我酸了......
  • 原文地址:https://blog.csdn.net/u010786653/article/details/126681056