Java:使用Spring Mail发送邮件

发送邮件

本项目源码在:https://coding.net/u/pruidong/p/SpringEmailDemo/git

之前简单使用过Java Email发送邮件,但最近接触了Spring Mail组件之后,发现在之前的基础上更加方便了.本文的内容参照了:Spring Mail官方文档,以及Thymeleaf官方示例.但对部分内容进行了修改和优化,也使得发送邮件变得更加简单.

有两点需要说明的是:

  1. 本文不会贴完所有的资源,可以前往[项目地址]获取所有源码;
  2. 项目基于Maven构建,并且不需要Spring Web MVC(也就是不需要在Tomcat中运行),配置相关邮箱信息之后,直接在控制台运行即可
Spring Mail发送邮件项目结构图
Spring Mail发送邮件项目结构图

部分源码

直接使用下面的源码无法构建出完整项目,因为有部分图片、模板文件(HTML,txt)未贴出,贴出下面的代码,仅作为参考依据.完整源码可以访问项目地址

Maven依赖文件(pom.xml):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>xyz.suancaiyu</groupId>
    <artifactId>SpringMail19Demo</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.9.0</version>
        </dependency>


        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>


    </dependencies>


</project>

java标志

配置邮箱地址、邮箱服务器信息(base.properties):

xyz.suancaiyu.smtpServer=SMTP服务器地址(smtp.qq.com)
xyz.suancaiyu.smtpServerPort=SMTP服务器端口
xyz.suancaiyu.smtpUserName=邮箱登录名
xyz.suancaiyu.smtpUserPassword=邮箱登录密码(QQ邮箱要使用授权码)
xyz.suancaiyu.smtpFromEmail=发件人邮箱地址
xyz.suancaiyu.smtpToEmail=收件人邮箱地址

Spring的配置文件(AppInitial.java):

package xyz.suancaiyu.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import xyz.suancaiyu.Constat;

import java.util.Collections;
import java.util.Properties;

/**
 * Spring 配置文件.
 *
 * Created by puruidong on 2017/9/15.
 */
@Configuration
@ComponentScan(basePackages = "xyz.suancaiyu.*")
public class AppInitial {


    @Bean
    public SimpleMailMessage templateMessage() {
        SimpleMailMessage msg = new SimpleMailMessage();
        msg.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
        msg.setTo(Constat.getValue(Constat.SMTPTOEMAIL));
        return msg;
    }

    @Bean
    public JavaMailSenderImpl mailSender() {
        JavaMailSenderImpl jmsl = new JavaMailSenderImpl();
        jmsl.setHost(Constat.getValue(Constat.SMTPSERVER));
        jmsl.setPort(Integer.valueOf(Constat.getValue(Constat.SMTPSERVERPORT)));
        jmsl.setUsername(Constat.getValue(Constat.SMTPUSERNAME));
        jmsl.setPassword(Constat.getValue(Constat.SMTPPASSWORD));
        Properties properties = new Properties();
        properties.setProperty("mail.smtp.auth", "true");
        properties.setProperty("mail.smtp.ssl.enable", "true");
        properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        jmsl.setJavaMailProperties(properties);
        return jmsl;
    }

    /********* Thymeleaf Email Template **************/

    @Bean
    public ResourceBundleMessageSource emailMessageSource() {
        final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("mail/MailMessages");
        return messageSource;
    }


    @Bean
    public TemplateEngine emailTemplateEngine() {
        final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        // Resolver for TEXT emails
        templateEngine.addTemplateResolver(textTemplateResolver());
        // Resolver for HTML emails (except the editable one)
        templateEngine.addTemplateResolver(htmlTemplateResolver());
        // Resolver for HTML editable emails (which will be treated as a String)
        templateEngine.addTemplateResolver(stringTemplateResolver());
        // Message source, internationalization specific to emails
        templateEngine.setTemplateEngineMessageSource(emailMessageSource());
        return templateEngine;
    }

    private ITemplateResolver textTemplateResolver() {
        final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setOrder(Integer.valueOf(1));
        templateResolver.setResolvablePatterns(Collections.singleton("text/*"));
        templateResolver.setPrefix("/mail/");
        templateResolver.setSuffix(".txt");
        templateResolver.setTemplateMode(TemplateMode.TEXT);
        templateResolver.setCharacterEncoding(Constat.EMAIL_TEMPLATE_ENCODING);
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    private ITemplateResolver htmlTemplateResolver() {
        final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setOrder(Integer.valueOf(2));
        templateResolver.setResolvablePatterns(Collections.singleton("html/*"));
        templateResolver.setPrefix("/mail/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCharacterEncoding(Constat.EMAIL_TEMPLATE_ENCODING);
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    private ITemplateResolver stringTemplateResolver() {
        final StringTemplateResolver templateResolver = new StringTemplateResolver();
        templateResolver.setOrder(Integer.valueOf(3));
        // No resolvable pattern, will simply process as a String template everything not previously matched
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    /********* Thymeleaf Email Template END **************/


}

发送纯文本邮件(未使用模板,SendTextEmail.java):

package xyz.suancaiyu.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Component;

import java.time.LocalDate;

/**
 * 发送纯文本邮件.
 * <p>
 * <p>
 * Created by puruidong on 2017/9/15.
 */
@Component
public class SendTextEmail {

    @Autowired
    private SimpleMailMessage templateMessage;

    @Autowired
    private MailSender mailSender;

    public void sendEmail() {
        System.out.println("发送纯文本邮件:");
        templateMessage.setSubject("订单信息-" + LocalDate.now().toString());
        templateMessage.setText("你好,email测试. ");
        mailSender.send(templateMessage);
        System.out.println("发送成功..");
    }

}

发送带附件的邮件(SendFileEmail.java):

package xyz.suancaiyu.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import xyz.suancaiyu.Constat;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import static xyz.suancaiyu.Constat.PNG_MIME;

/**
 * 发送邮件-带附件.
 * <p>
 * Created by puruidong on 2017/9/15.
 */
@Component
public class SendFileEmail {


    @Autowired
    private JavaMailSenderImpl mailSender;

    public void sendEmail() {
        System.out.println("发送邮件,携带附件:");
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setTo(Constat.getValue(Constat.SMTPTOEMAIL));
            helper.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            helper.setText("订单消息-详情请查看附件");
            helper.setSubject("订单消息");
            helper.addAttachment("detail.jpg", new ClassPathResource(Constat.THYMELEAF_LOGO_IMAGE), PNG_MIME);
            mailSender.send(message);
            System.out.println("发送成功!");
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    public void sendHTMLEmail() {
        System.out.println("发送HTML邮件:");
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true, "GBK");
            helper.setTo(Constat.getValue(Constat.SMTPTOEMAIL));
            helper.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            helper.setText("<mail.html><head><META http-equiv=Content-Type content='text/mail.html; charset=GBK'> <meta charset='utf-8'/><head><body><img src='cid:identifier1234'><b>测试一个文本.</b><p style='color:red'>重要提示.</p></body></mail.html>", true);
            helper.setSubject("订单消息");
            helper.addInline("identifier1234", new ClassPathResource(Constat.THYMELEAF_LOGO_IMAGE), PNG_MIME);
            mailSender.send(message);
            System.out.println("发送成功!");
        } catch (MessagingException e) {
            e.printStackTrace();
        }


    }


}

使用Thymeleaf发送模板邮件(参照了Thymeleaf官方示例,有部分改动):

package xyz.suancaiyu.service.thymeleaf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import xyz.suancaiyu.Constat;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Arrays;
import java.util.Date;


/**
 * 使用Thymeleaf发送模板邮件.
 * 
 * Created by puruidong on 2017/9/15.
 */
@Component
public class EmailService {

    @Autowired
    private JavaMailSenderImpl mailSender;

    @Autowired
    private TemplateEngine emailTemplateEngine;

    private static final String EMAIL_TEXT_TEMPLATE_NAME = "text/email-text";
    private static final String EMAIL_SIMPLE_TEMPLATE_NAME = "html/email-simple";
    private static final String EMAIL_WITHATTACHMENT_TEMPLATE_NAME = "html/email-withattachment";
    private static final String EMAIL_INLINEIMAGE_TEMPLATE_NAME = "html/email-inlineimage";
    private static final String EMAIL_EDITABLE_TEMPLATE_CLASSPATH_RES = "html/email-editable";

    public void sendTextMail() {
        // Prepare the evaluation context
        final Context ctx = new Context();
        ctx.setVariable("name", "test123");
        ctx.setVariable("subscriptionDate", new Date());
        ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));

        // Prepare message using a Spring helper
        final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
        final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
        try {
            message.setSubject("Example plain TEXT email");
            message.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            message.setTo(Constat.getValue(Constat.SMTPTOEMAIL));

            // Create the plain TEXT body using Thymeleaf
            final String textContent = emailTemplateEngine.process(EMAIL_TEXT_TEMPLATE_NAME, ctx);
            message.setText(textContent);

            // Send email
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    /*
     * Send HTML mail (simple)
     */
    public void sendSimpleMail() {

        // Prepare the evaluation context
        final Context ctx = new Context();
        ctx.setVariable("name", "13343sdsxvbn");
        ctx.setVariable("subscriptionDate", new Date());
        ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));

        // Prepare message using a Spring helper
        final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
        final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
        try {
            message.setSubject("Example HTML email (simple)");
            message.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            message.setTo(Constat.getValue(Constat.SMTPTOEMAIL));

            // Create the HTML body using Thymeleaf
            final String htmlContent = emailTemplateEngine.process(EMAIL_SIMPLE_TEMPLATE_NAME, ctx);
            message.setText(htmlContent, true /* isHtml */);

            // Send email
            this.mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }

    }


    public void sendMailWithInline() {
        final Context ctx = new Context();
        ctx.setVariable("name", "testlocal");
        ctx.setVariable("subscriptionDate", new Date());
        ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));
        ctx.setVariable("imageResourceName", "identifier1234"); // so that we can reference it from HTML

        final MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
            helper.setSubject("Thymeleaf模板邮件");
            helper.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            helper.setTo(Constat.getValue(Constat.SMTPTOEMAIL));
            final String htmlContent = emailTemplateEngine.process(EMAIL_INLINEIMAGE_TEMPLATE_NAME, ctx);
            helper.setText(htmlContent, true);

            helper.addInline("identifier1234", new ClassPathResource(Constat.THYMELEAF_LOGO_IMAGE), Constat.PNG_MIME);
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }


    /*
     * Send HTML mail with inline image
     */
    public void sendEditableMail() {
        try {
            // Prepare message using a Spring helper
            final MimeMessage mimeMessage = mailSender.createMimeMessage();
            final MimeMessageHelper message
                    = new MimeMessageHelper(mimeMessage, true /* multipart */, "UTF-8");
            message.setSubject("Example editable HTML email");
            message.setFrom(Constat.getValue(Constat.SMTPFROMEMAIL));
            message.setTo(Constat.getValue(Constat.SMTPTOEMAIL));

            // Prepare the evaluation context
            final Context ctx = new Context();
            ctx.setVariable("name", "fdsafdsadsaork11233");
            ctx.setVariable("subscriptionDate", new Date());
            ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));

            // Create the HTML body using Thymeleaf
            final String output = emailTemplateEngine.process(EMAIL_EDITABLE_TEMPLATE_CLASSPATH_RES, ctx);
            message.setText(output, true /* isHtml */);

            // Add the inline images, referenced from the HTML code as "cid:image-name"
            message.addInline("background", new ClassPathResource(Constat.BACKGROUND_IMAGE), Constat.PNG_MIME);
            message.addInline("logo-background", new ClassPathResource(Constat.LOGO_BACKGROUND_IMAGE), Constat.PNG_MIME);
            message.addInline("thymeleaf-banner", new ClassPathResource(Constat.THYMELEAF_BANNER_IMAGE), Constat.PNG_MIME);
            message.addInline("thymeleaf-logo", new ClassPathResource(Constat.THYMELEAF_LOGO_IMAGE), Constat.PNG_MIME);

            // Send mail
            mailSender.send(mimeMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行测试类(Main.java):

package xyz.suancaiyu.main;


import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xyz.suancaiyu.config.AppInitial;
import xyz.suancaiyu.service.SendFileEmail;
import xyz.suancaiyu.service.SendTextEmail;
import xyz.suancaiyu.service.thymeleaf.EmailService;

/**
 * 使用Spring mail发送邮件.
 * <p>
 * <p>
 * <p>
 * 可能出现的问题:
 * <p>
 * <p>
 * 1.增加验证,否则会报:553 authentication is required 的错误信息
 * Properties prop = new Properties();
 * prop.setProperty("mail.smtp.auth", "true");
 * JavaMailSenderImpl.setJavaMailProperties(prop);
 * <p>
 * 2.当邮件内容是HTML语言时的中文问题:
 * 初始化MimeMessageHelper辅助类时,设置"GBK" encoding!如:
 * MimeMessageHelper messageHelp = new MimeMessageHelper(message,true,"GBK");
 * 同时在设置:<META http-equiv=Content-Type content='text/mail.html; charset=GBK'>
 * 如果都设置为"UTF-8",在某些邮件客户端标题不能正常显示!
 * <p>
 * 3.邮件附件的中文问题!
 * spring的文档里面说MimeMessageHelper设置了encoding,同时对title,text,attach产生作用,但还是会出问题:
 * 解决方法:MimeUtility.encodeWord(file.getName());就OK了!
 * <p>
 * Created by puruidong on 2017/9/15.
 */
public class Main {

    private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(AppInitial.class);

    /**
     * 发送文本邮件.
     */
    private static void sendTextEmail() {
        SendTextEmail send = ctx.getBean(SendTextEmail.class);
        send.sendEmail();
    }

    /**
     * 发送文件邮件.
     */
    private static void sendFileEmail() {
        SendFileEmail send = ctx.getBean(SendFileEmail.class);
        send.sendEmail();
        send.sendHTMLEmail();
    }

    /**
     * 使用Thymeleaf模板发送模板邮件.
     * <p>
     * 参考:https://github.com/thymeleaf/thymeleafexamples-springmail
     */
    private static void sendThymeleafEmail() {
        EmailService emailService = ctx.getBean(EmailService.class);
        emailService.sendMailWithInline();
        emailService.sendTextMail();
        emailService.sendSimpleMail();
        emailService.sendEditableMail();
    }

    public static void main(String[] args) {
        System.out.println("先配置base.properties");
        System.exit(1);
        sendThymeleafEmail();
    }
}

到此.