Springboot 集成发送邮件服务

发送邮件算是项目里常用的功能,大体上可以分为四类:
1. 只有文本的简单有邮件
2. 带附件的邮件
3. 带静态资源的邮件
4. 以及带附件的模板邮件。
Springboot中集成邮件功能还是非常简单的,主要依赖spring-boot-starter-mail就够了。
Springboot-mail

在pom.xml文件中添加依赖的包

发送一些固定模板的邮件,就会用到模板引擎,我们可以选择thymeleaf或者freemarker,这里我使用了前者。

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

在application.yml中增加配置

这里的配置我们定义了SMTP服务地址,发件人的邮箱账号信息。

spring:
  mail:
    host: smtp.***.net
    mailFrom: ***@***.com
    username: ******
    password: ******
    default-encoding: UTF-8
    properties: 
      mail: 
        smtp: 
          auth: true
          timeout: 25000

再来定义一个配置类获取配置文件中的发送人:

@Data
@Component
public class EmailConfig {
    /** 
     * 发件邮箱 
     */  
    @Value("${spring.mail.mailFrom}")  
    private String emailFrom;  
}

这里我项目依赖了lombok包,使用@Data注解省略了get及set方法。

上面的配置中,mailFrom是我定义的发件人邮箱,有企业的公共邮箱登陆是不带邮箱后缀的,这样mailFrom和username的值就是不同的。
倘若邮箱的登陆是带后缀的,那么mailFrom和username的值实际上是相同的,定义配置的时候mailFrom我们可以省略,发件人配置类注入的时候改成${spring.mail.username}即可。

定义发送邮件接口

import org.thymeleaf.context.Context;
public interface MailService {
    /**
     * 发送简单邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     */
    public void sendSimpleMail(String sendTo, String titel, String content);  
    /**
     * 发送简单带附件邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     * @param attachments 附件路径
     */
    public void sendAttachmentsMail(String sendTo, String titel, String content, List<String> attachments);
    /**
     * 发送内嵌静态资源邮件
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     * @param attachments 资源id及路径
     */
    public void sendInlineMail(String sendTo, String titel, String content, Map<String,String> attachments);
    /**
     * 发送模板邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param context 内容参数
     * @param attachments 附件
     * @param templatePath 模板路径
     */
    public void sendTemplateMail(String sendTo, String titel,  Context context, List<String> attachments,String templatePath);  
}

实现接口

由于业务上我们并不需要关注邮件的发送结果,因此设计上我们直接使用异步方式发送邮件。这里我采用最简单的方式,使用@Async注解来使得实现类中的方法异步执行。当然如果你不需要,直接去掉注解就可以了。

import org.thymeleaf.context.Context;

@Async
@Service
public class MailServiceImpl implements MailService {
    
    private static final Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);
    @Autowired
    private EmailConfig emailConfig;
    @Autowired
    private JavaMailSender mailSender;
    @Autowired
    private TemplateEngine templateEngine;
    /**
     * 发送简单邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     */
    @Override
    public void sendSimpleMail(String sendTo, String titel, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(emailConfig.getEmailFrom());
        message.setTo(sendTo);
        message.setSubject(titel);
        message.setText(content);
        try{
            mailSender.send(message);
        } catch (MailException e) {
            logger.error("sendSimpleMail error.", e);
        }
    }
    /**
     * 发送简单带附件邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     * @param attachments 附件路径
     */
    @Override
    public void sendAttachmentsMail(String sendTo, String titel, String content, List<String> attachments) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper;
        try {
            helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom(emailConfig.getEmailFrom());
            helper.setTo(sendTo);
            helper.setSubject(titel);
            helper.setText(content);
            for (String filePath : attachments) {
                FileSystemResource file = new FileSystemResource(new File(filePath));
                String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
                helper.addAttachment(fileName, file);
            }
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            logger.error("sendAttachmentsMail error.", e);
        } catch (MailException e) {
            logger.error("sendAttachmentsMail error.", e);
        }
        
    }
    /**
     * 发送内嵌静态资源邮件
     * @param sendTo 收件人
     * @param titel 标题
     * @param content 内容
     * @param attachments 资源id及路径
     */
    @Override
    public void sendInlineMail(String sendTo, String titel, String content, Map<String,String> attachments) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper;
        try {
            helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom(emailConfig.getEmailFrom());
            helper.setTo(sendTo);
            helper.setSubject(titel);
            helper.setText(content, true);
            
            for (Map.Entry<String, String> entry : attachments.entrySet()) {
                FileSystemResource file = new FileSystemResource(new File(entry.getValue()));
                helper.addInline(entry.getKey(), file);
            }
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            logger.error("sendInlineMail error.", e);
        } catch (MailException e) {
            logger.error("sendInlineMail error.", e);
        }
        
    }
    /**
     * 发送模板邮件 
     * @param sendTo 收件人
     * @param titel 标题
     * @param context 内容参数
     * @param attachments 附件
     * @param templatePath 模板路径
     */
    @Override
    public void sendTemplateMail(String sendTo, String titel, Context context, List<String> attachments,String templatePath) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper;
        
        if(context==null){
            context = new Context();
        }
        
        try {
            helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom(emailConfig.getEmailFrom());
            helper.setTo(sendTo);
            helper.setSubject(titel);
            String html = templateEngine.process(templatePath,context);
            helper.setText(html, true);
            for (String filePath : attachments) {
                FileSystemResource file = new FileSystemResource(new File(filePath));
                String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
                helper.addAttachment(fileName, file);
            }
            
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            logger.error("sendTemplateMail error.", e);
        } catch (MailException e) {
            logger.error("sendTemplateMail error.", e);
        }
    }
}

再来看下我们事先定义好的邮件模板 test.html,放在 resources/templates/mail 目录下:

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>Title</title>
    </head>
    <body>
        您好[[${username}]], 欢迎来到sundayfine.com! <br/>
        <a href="#" th:href="@{https://www.sundayfine.com/?{id}(id=${id})}">点击进入</a>
    </body>
</html>

测试

public class MailServiceTest extends SpiritApplicationTests {
    @Autowired
    private MailService mailService;
    
    //收件人邮箱
    private String sendTo = "****@163.com";
    /**
     * 测试简单邮件
     */
    @Test
    public void sendSimpleMail() {
        String title = "This is a simple mail.";
        String content = "This is the content of the mail.";
        mailService.sendSimpleMail(sendTo, title, content);
    }
    /**
     * 测试带附件邮件
     */
    @Test
    public void sendAttachmentsMail() {
        String title = "This is an email with attachments.";
        String content ="This is the content of the mail.";
        
        List<String> attachments = new ArrayList<String>();
        attachments.add("E:\\web_pic\\006tNc79ly1fj8sf9847xj31kw11zhdw.jpg");
        attachments.add("E:\\web_pic\\laptop-2590647_1280.jpg");
        
        mailService.sendAttachmentsMail(sendTo,title,content, attachments);
    }
    /**
     * 测试带静态资源邮件
     */
    @Test
    public void sendInlineMail() {
        
        String title = "This is an email with some static resources.";
        StringBuilder content = new StringBuilder();
        HashMap<String, String> resMap = new HashMap<String, String>();
        
        String rsc1Id = "resource1";
        String rsc2Id = "resource2";
        
        resMap.put(rsc1Id, "E:\\web_pic\\006tNc79ly1fj8sf9847xj31kw11zhdw.jpg");
        resMap.put(rsc2Id, "E:\\web_pic\\laptop-2590647_1280.jpg");
        
        content.append("<html><head><title>Title</title></head>");
        content.append("<body>");
        content.append("<image src=\'cid:" + rsc1Id + "\' /><br/>");
        content.append("<image src=\'cid:" + rsc2Id + "\' />");
        content.append("</body></html>");
        mailService.sendInlineMail(sendTo, title, content.toString(), resMap);
    }
    /**
     * 测试带附件的模板邮件
     */
    @Test
    public void sendTemplateMail() {
        String title = "This is a template mail with attachments.";
        Context context = new Context();
        context.setVariable("id", "2091");
        context.setVariable("username", "nj.sun");
        
        List<String> attachments = new ArrayList<String>();
        attachments.add("E:\\web_pic\\006tNc79ly1fj8sf9847xj31kw11zhdw.jpg");
        attachments.add("E:\\web_pic\\laptop-2590647_1280.jpg");
        
        mailService.sendTemplateMail(sendTo,title,context, attachments,"mail/test");
    }
}

当然在系统中我们发送邮件时还需要记录发送的日志,包括发送的时间,邮件内容,状态,以及失败原因等等,这些可以自己加。上面的代码还算清晰就不多解释了,现在看看集成一个邮件服务是不是简单很多?

参考资料

  1. springboot(十):邮件服务
  2. Spring Boot(十二)集成spring-boot-starter-mail发送邮件

发表评论

电子邮件地址不会被公开。 必填项已用*标注