当前位置: 首页 > 工具软件 > MailSpring > 使用案例 >

发送邮件的JavaMail和Spring提供的MailSender比较分析

蓬思博
2023-12-01

发邮件,项目的必备功能之一,如果一个稍微模块化一点的公司,一般会单独出来一个项目专用来做公司的发送信息的功能,当然这个发送信息中不止包含发邮件,还会有短信、APP push等。这篇聊聊推送邮件。

在以前的开发中,公司用Java mail的比较多,由自己来写邮件的组装和发送功能,但是Java mail使用操作比较繁杂,后来渐渐的都开始使用spring提供的JavaMailSender工具来实现,用过的都知道,这叫一个爽,执行几个set、add操作,一个复杂的邮件就可以发送出去,但是在业务代码中发送邮件的位置很多,不能在每个业务逻辑位置都写一遍同样的set操作,这是基本常识,因此都会基于JavaMailSender再做一个工具类,这个工具类让调用者只用一句话实现就OK。引出主题工具类,接下来就开干吧。

JavaMail邮件推送

这边的主要内容是写JavaMailSender,但是开始还是来看看JavaMail的基本实现,因为JavaMailSender也是基于JavaMail做的再次封装。

邮件主要组成部分

发送邮件的三个重点:

  • Session:是用来设置邮件参数以及和邮件服务器建立会话用的。
  • MimeMessage:邮件本体,所有的邮件内容、邮件的收件人、发送人、附件、内嵌图片等都是在这里面封装。
  • Transport:提供发送邮件的API。

Session中实际也提供了获取transport的方法,但是这个transport不好用,还是习惯用Transport提供的静态调用。

/*Transport静态方法直接调用,将message传入即可*/
Transport.send(message);
/*Session获取transport方式调用*/
Transport transport = session.getTransport();
//address需要手动添加,表示发送人
transport.sendMessage(message,new Address[1]);

上面Session的transport只能指定发送人,抄送人、密送人都不能添加,如果在MimeMessage中添加不知道能不能起作用,这个没有尝试过,如果你喜欢用这种方式可以分享一下。

引入依赖

Java mail有两个依赖,分别是mail和activation。

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
简单邮件

简单的发送邮件没有复杂的逻辑,代码也很清晰,直接上代码:

private static void send() throws MessagingException {
    //构建Session
    Properties properties = new Properties();
    properties.setProperty("mail.host","smtp.163.com");
    properties.setProperty("mail.transport.protocol","smtp");
    properties.setProperty("mail.smtp.auth","true");
    properties.setProperty("mail.host.port","25");
    Session session = Session.getDefaultInstance(properties, new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("xxx@163.com","xxxxxx");
        }
    });
    
    session.setDebug(true);
    //构建邮件MimeMessage
    MimeMessage message = new MimeMessage(session);
    message.setSubject("这是一封测试邮件!");
    message.setText("测试邮件内容随意");
    message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com"));
    message.setFrom(new InternetAddress("xxx@163.com"));

    //发送邮件
    Transport.send(message);
}

需要注意的点:

  • 发件人要和进行密码验证的邮箱保持一致,否则会报错:com.sun.mail.smtp.SMTPSendFailedException: 553 Mail from must equal authorized user
  • 如果要发抄送人和密送人,需要使用setRecipient方法,在第一参数中指定抄送类型即可。
复杂邮件

复杂邮件使用的相对会比较多一点,在发送邮件的时候多少会带上内嵌图片或者附件。MimeMessage是整个邮件本体,构建复杂邮件的过程就是向这个本体中加不同的内容,Java mail提供了一个类MimeBodyPart,表示邮件内容的组件。将需要发送的内容用组件包装起来,再塞到邮件本地中即可。

示例代码:

private static void send() throws MessagingException {
    //构建Session
    Properties properties = new Properties();
    properties.setProperty("mail.host","smtp.163.com");
    properties.setProperty("mail.transport.protocol","smtp");
    properties.setProperty("mail.smtp.auth","true");
    properties.setProperty("mail.host.port","25");
    Session session = Session.getDefaultInstance(properties, new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("xxx@163.com","xxxxx");
        }
    });
    session.setDebug(true);//打印发送日志

    //构建基础信息
    MimeMessage message = new MimeMessage(session);
    message.setSubject("这是一封测试邮件!");
    //message.setText("测试邮件内容随意");
    message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com"));
    message.setFrom(new InternetAddress("xxx@163.com"));

    //构建内容
    MimeBodyPart text = new MimeBodyPart();
    text.setContent("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", "text/html;charset=UTF-8");
    MimeBodyPart bodyPart = new MimeBodyPart();

    //构建内嵌图片
    DataHandler dataHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png"));
    bodyPart.setDataHandler(dataHandler);
    bodyPart.setContentID("avatar");//如果没有这句,表示作为附件发送,加上表示显示在邮件内容的一部分

    //构建附件
    MimeBodyPart attachment = new MimeBodyPart();
    DataHandler attachmentHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png"));
    attachment.setDataHandler(attachmentHandler);
    attachment.addHeader("Content-Type","UTF-8");//防止中文名乱码
    attachment.setFileName("avatar.png");//这里名称记得带上后缀名,否则邮件附件下载下来需要手动添加后缀,可能会损坏

    //构建邮件体内容
    MimeMultipart mimeMultipart = new MimeMultipart();
    mimeMultipart.addBodyPart(text);
    mimeMultipart.addBodyPart(bodyPart);
    mimeMultipart.addBodyPart(attachment);
    mimeMultipart.setSubType("mixed");//设置为mixed,别忘了哦
    message.setContent(mimeMultipart);

    //发送邮件
    Transport.send(message);
}

复杂邮件构建的过程不是很难,就是代码量会多一点。

JavaMailSender

JavaMailSender是由spring提供的,功能很强大,对Java mail做了一层封装,在Spring boot中可以拿来用,是自动集成的。

引入依赖

和Spring boot整合后,引入依赖就很简单啦。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--是在使用邮件模板的时候使用,不使用模板可以忽略-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.6.4</version>
</dependency>
配置信息

使用JavaMailSender需要在配置文件中配置相关的信息,在Java mail中是通过Session来配置,在JavaMailSender上,在Spring项目中可以通过在配置文件中配置。(如果使用的是spring boot,在properties中配置,如果是Spring MVC项目就要用xml文件啦,或者自己手动的使用Configuration类来配置,本文以Spring boot为基础)

spring.mail.default-encoding=utf-8
spring.mail.host=smtp.163.com
spring.mail.protocol=smtp
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.transport.protocol=smtp
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.username=xxx@163.com
spring.mail.password=xxxx
简单邮件

简单邮件发送方法,到底简单到什么程度,看下面的代码:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ItcrudCommonsUtilApplication.class)
public class JavaMailSenderTest {

    @Autowired
    private JavaMailSender javaMailSender;

    @Autowired
    private VelocityEngine velocityEngine;//暂时用不上,在下面说到模板邮件使用

    @Test
    public void sendTest() throws MessagingException {
        //创建邮件本体
        MimeMessage message = javaMailSender.createMimeMessage();
        //创建本体编辑的协助者(YY出来的名字)
        MimeMessageHelper helper = new MimeMessageHelper(message);
		//构建邮件本体
        helper.setSubject("邮件标题");
        helper.setText("发送的内容");
        helper.setTo("itcrud@aliyun.com");
        helper.setFrom("xxx@163.com");
		//发送邮件
        javaMailSender.send(helper.getMimeMessage());
    }
}

看了这段发送邮件的代码,然后和上面Java mail的代码对比,果断的舍弃Java mail的有木有。

复杂邮件

对于Java mail来说是复杂邮件,但是对于JavaMailSender来说,复杂也会变得简单。

示例代码:

@Test
public void sendComplexText() throws MessagingException {
    MimeMessage message = javaMailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
    //构建邮件本体内容
    helper.setSubject("复杂邮件");
    helper.setFrom("xxx@163.com");
    helper.setTo("itcrud@aliyun.com");
    helper.setText("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", true);
    helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png"));
    helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png"));
    //发送邮件
    javaMailSender.send(helper.getMimeMessage());
}

代码的减少量可以看得出来吧。那些复杂的封装逻辑就交给Spring来处理啦。

需要注意的两点:(刚开始用的时候被坑过)

  • 在创建MimeMessageHelper的时候要指定是否为multipart(构造方法的第二个参数),可以理解为复杂邮件的意思;
  • 在setText方法的第二个参数也是要指定为true,如果这里没有设置true,这个内容会直接显示在邮件中,不会解析html内容。
模板邮件

上面的两个内容都不是最主要的,在将Java mail的时候最后一个功能很难说,代码量太多。下面我们用JavaMailSender来实现。

场景:在公司对外发送邮件的时候,除了附件、内嵌图片和普通文本,还有两个很重的东西,那就是签名和占位填充。个人发邮件的时候都会带上自己的签名,何况是公司向外发邮件啦。另外一个邮件的基本格式相同,只是部分填充内容不同,总不能每次发送文件都要重新写一个html文件吧。

但是想想,邮件签名就直接加在内容里面不就可以啦,然后使用html格式来进行邮件的排版。对的没有错,但是如果整个邮件的排版比较复杂,html内容很长怎么办。那也没问题啊,可以放在文件里面,通过流来读取文件不就可以了吗。对的的确是这样的,但是如果使用Java mail就要去手动的读取问题,不觉得会很麻烦吗。还有就是占位符的填充。

这个时候JavaMailSender帮我们解决了这一系列的问题。准确的说是和另一个插件Velocity结合来解决。这就是在引入依赖的时候,我加上velocity的依赖的重点所在。

只需要两步就可以搞定:

  • 在resources目录下建立一个目录用来存在模板文件。(这里指定mail目录)
<!--模板文件的内容-->
<html>
    <body>
        <h1>这是邮件的标题</h1>
        <div>
            <p>很长很长的邮件内容上半部分</p>
            <p>很长很长的邮件内容上半部分</p>
        </div>
        <img src="cid:avatar">
        <div>
            <p>很长很长的邮件内容下半部分</p>
            <p>很长很长的邮件内容下半部分</p>
            <p>还有一部分内容看附件</p>
        </div>
        <div>
            <p>
                这里是签名的内容
                公司名称:xxxx有限责任公司
                联系电话:xxxxxxxx
            </p>
        </div>
    </body>
</html>
  • 在代码中使用VelocityEngine来读取文件,并发送邮件
@Test
public void sendSuperTest() throws MessagingException {
    MimeMessage message = javaMailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
    //构建邮件本体内容
    helper.setSubject("复杂邮件");
    helper.setFrom("xxx@163.com");
    helper.setTo("itcrud@aliyun.com");
    //使用
    VelocityContext context = new VelocityContext();
    context.put("content","占位符需要填充的内容");
    try (StringWriter writer = new StringWriter()) {
        velocityEngine.mergeTemplate("/mail/temp.vm", "UTF-8", context, writer);
        helper.setText(writer.toString(), true);
    } catch (Exception e) {
        e.printStackTrace();
    }
    helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png"));
    helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png"));
    //发送邮件
    javaMailSender.send(helper.getMimeMessage());
}

完美解决上面的两个问题,并且整个实现过程的代码量也是很少的,比上面Java mail发送一个复杂邮件还要少。

总结

这里讲到了Java mail发送邮件和Spring封装后的JavaMailSender,两者对比来说,JavaMailSender要比Java mail直接实现要简单的多。如果你有时间其实可以自己实现一套Spring的这种封装,但是为何要重复造轮子呢。

Code

码云(gitee):https://gitee.com/itcrud/itcrud-commons(mail)

 类似资料: