SpringBoot:邮件发送

        官网文档:39. Sending Email (spring.io)。

Sending Email

Spring框架提供了JavaMailSender实例,用于发送邮件。

如果SpringBoot项目中包含了相关的启动器,那么就会自动装配一个Bean实例到项目中。

        在SpringBoot项目中引入如下Email启动器,



    org.springframework.boot
    spring-boot-starter-mail

核心接口和类

核心接口:MailSender

        org.springframework.mail是邮件发送依赖的顶级包,其中提供了邮件发送的核心接口MailSender,该接口提供了两个方法,分别用于指定逐个邮件发送、批量邮件发送。

package org.springframework.mail;

/**
 * This interface defines a strategy for sending simple mails. Can be
 * implemented for a variety of mailing systems due to the simple requirements.
 * For richer functionality like MIME messages, consider JavaMailSender.
 *
 * 

Allows for easy testing of clients, as it does not depend on JavaMail's * infrastructure classes: no mocking of JavaMail Session or Transport necessary. */ public interface MailSender { /** * Send the given simple mail message.-发送简单邮件 * @param simpleMessage the message to send * @throws MailParseException in case of failure when parsing the message * @throws MailAuthenticationException in case of authentication failure * @throws MailSendException in case of failure when sending the message */ void send(SimpleMailMessage simpleMessage) throws MailException; /** * Send the given array of simple mail messages in batch.-批量发送一组简单邮件 * @param simpleMessages the messages to send * @throws MailParseException in case of failure when parsing a message * @throws MailAuthenticationException in case of authentication failure * @throws MailSendException in case of failure when sending a message */ void send(SimpleMailMessage... simpleMessages) throws MailException; }

        可以看到,其实JavaMailSender接口也作为它的子接口存在,并且内置了一个实现类JavaMailSenderImpl可以供我们直接使用。

SpringBoot:邮件发送_第1张图片

邮件消息接口:MailMessage

        邮件消息接口:规定一封电子邮件对应的组成元素,内置了简单邮件消息和媒体邮件消息两个实现子类。

package org.springframework.mail;

import java.util.Date;

/**
 * This is a common interface for mail messages, allowing a user to set key
 * values required in assembling a mail message, without needing to know if
 * the underlying message is a simple text message or a more sophisticated
 * MIME message.
 *
 * 

Implemented by both SimpleMailMessage and MimeMessageHelper, * to let message population code interact with a simple message or a * MIME message through a common interface. */ public interface MailMessage { void setFrom(String from) throws MailParseException; void setReplyTo(String replyTo) throws MailParseException; void setTo(String to) throws MailParseException; void setTo(String... to) throws MailParseException; void setCc(String cc) throws MailParseException; void setCc(String... cc) throws MailParseException; void setBcc(String bcc) throws MailParseException; void setBcc(String... bcc) throws MailParseException; void setSentDate(Date sentDate) throws MailParseException; void setSubject(String subject) throws MailParseException; void setText(String text) throws MailParseException; }

简单邮件对象:SimpleMailMessage 

        可以用来囊括简单邮件信息的类是SimpleMailMessage类,邮件主体内容以简单文本形式为主。

A simple value object that encapsulates the properties of a simple mail such as from and to (plus many others) is the SimpleMailMessage class. 

        该类的源码如下, 

package org.springframework.mail;

import java.io.Serializable;
import java.util.Date;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * Models a simple mail message, including data such as the from, to, cc, subject,
 * and text fields.
 *
 * 

Consider {@code JavaMailSender} and JavaMail {@code MimeMessages} for creating * more sophisticated messages, for example messages with attachments, special * character encodings, or personal names that accompany mail addresses. * * @author Dmitriy Kopylenko * @author Juergen Hoeller * @since 10.09.2003 * @see MailSender * @see org.springframework.mail.javamail.JavaMailSender * @see org.springframework.mail.javamail.MimeMessagePreparator * @see org.springframework.mail.javamail.MimeMessageHelper * @see org.springframework.mail.javamail.MimeMailMessage */ @SuppressWarnings("serial") public class SimpleMailMessage implements MailMessage, Serializable { @Nullable private String from; @Nullable private String replyTo; @Nullable private String[] to; @Nullable private String[] cc; @Nullable private String[] bcc; @Nullable private Date sentDate; @Nullable private String subject; @Nullable private String text; /** * Create a new {@code SimpleMailMessage}. */ public SimpleMailMessage() { } /** * Copy constructor for creating a new {@code SimpleMailMessage} from the state * of an existing {@code SimpleMailMessage} instance. */ public SimpleMailMessage(SimpleMailMessage original) { Assert.notNull(original, "'original' message argument must not be null"); this.from = original.getFrom(); this.replyTo = original.getReplyTo(); this.to = copyOrNull(original.getTo()); this.cc = copyOrNull(original.getCc()); this.bcc = copyOrNull(original.getBcc()); this.sentDate = original.getSentDate(); this.subject = original.getSubject(); this.text = original.getText(); } @Override public void setFrom(String from) { this.from = from; } @Nullable public String getFrom() { return this.from; } @Override public void setReplyTo(String replyTo) { this.replyTo = replyTo; } @Nullable public String getReplyTo() { return this.replyTo; } @Override public void setTo(String to) { this.to = new String[] {to}; } @Override public void setTo(String... to) { this.to = to; } @Nullable public String[] getTo() { return this.to; } @Override public void setCc(String cc) { this.cc = new String[] {cc}; } @Override public void setCc(String... cc) { this.cc = cc; } @Nullable public String[] getCc() { return this.cc; } @Override public void setBcc(String bcc) { this.bcc = new String[] {bcc}; } @Override public void setBcc(String... bcc) { this.bcc = bcc; } @Nullable public String[] getBcc() { return this.bcc; } @Override public void setSentDate(Date sentDate) { this.sentDate = sentDate; } @Nullable public Date getSentDate() { return this.sentDate; } @Override public void setSubject(String subject) { this.subject = subject; } @Nullable public String getSubject() { return this.subject; } @Override public void setText(String text) { this.text = text; } @Nullable public String getText() { return this.text; } /** * Copy the contents of this message to the given target message. * @param target the {@code MailMessage} to copy to */ public void copyTo(MailMessage target) { Assert.notNull(target, "'target' MailMessage must not be null"); if (getFrom() != null) { target.setFrom(getFrom()); } if (getReplyTo() != null) { target.setReplyTo(getReplyTo()); } if (getTo() != null) { target.setTo(copy(getTo())); } if (getCc() != null) { target.setCc(copy(getCc())); } if (getBcc() != null) { target.setBcc(copy(getBcc())); } if (getSentDate() != null) { target.setSentDate(getSentDate()); } if (getSubject() != null) { target.setSubject(getSubject()); } if (getText() != null) { target.setText(getText()); } } @Override public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (!(other instanceof SimpleMailMessage)) { return false; } SimpleMailMessage otherMessage = (SimpleMailMessage) other; return (ObjectUtils.nullSafeEquals(this.from, otherMessage.from) && ObjectUtils.nullSafeEquals(this.replyTo, otherMessage.replyTo) && ObjectUtils.nullSafeEquals(this.to, otherMessage.to) && ObjectUtils.nullSafeEquals(this.cc, otherMessage.cc) && ObjectUtils.nullSafeEquals(this.bcc, otherMessage.bcc) && ObjectUtils.nullSafeEquals(this.sentDate, otherMessage.sentDate) && ObjectUtils.nullSafeEquals(this.subject, otherMessage.subject) && ObjectUtils.nullSafeEquals(this.text, otherMessage.text)); } @Override public int hashCode() { int hashCode = ObjectUtils.nullSafeHashCode(this.from); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.replyTo); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.to); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.cc); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.bcc); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.sentDate); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.subject); return hashCode; } @Override public String toString() { StringBuilder sb = new StringBuilder("SimpleMailMessage: "); sb.append("from=").append(this.from).append("; "); sb.append("replyTo=").append(this.replyTo).append("; "); sb.append("to=").append(StringUtils.arrayToCommaDelimitedString(this.to)).append("; "); sb.append("cc=").append(StringUtils.arrayToCommaDelimitedString(this.cc)).append("; "); sb.append("bcc=").append(StringUtils.arrayToCommaDelimitedString(this.bcc)).append("; "); sb.append("sentDate=").append(this.sentDate).append("; "); sb.append("subject=").append(this.subject).append("; "); sb.append("text=").append(this.text); return sb.toString(); } @Nullable private static String[] copyOrNull(@Nullable String[] state) { if (state == null) { return null; } return copy(state); } private static String[] copy(String[] state) { return state.clone(); } }

媒体邮件对象:MimeMailMessage

        MemeMailMessage表示媒体邮件对象,可以实现自定义邮件模板的动态填充和邮件发送。

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.mail.javamail;

import java.util.Date;

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

import org.springframework.mail.MailMessage;
import org.springframework.mail.MailParseException;

/**
 * Implementation of the MailMessage interface for a JavaMail MIME message,
 * to let message population code interact with a simple message or a MIME
 * message through a common interface.
 *
 * 

Uses a MimeMessageHelper underneath. Can either be created with a * MimeMessageHelper instance or with a JavaMail MimeMessage instance. * * @author Juergen Hoeller * @since 1.1.5 * @see MimeMessageHelper * @see javax.mail.internet.MimeMessage */ public class MimeMailMessage implements MailMessage { private final MimeMessageHelper helper; /** * Create a new MimeMailMessage based on the given MimeMessageHelper. * @param mimeMessageHelper the MimeMessageHelper */ public MimeMailMessage(MimeMessageHelper mimeMessageHelper) { this.helper = mimeMessageHelper; } /** * Create a new MimeMailMessage based on the given JavaMail MimeMessage. * @param mimeMessage the JavaMail MimeMessage */ public MimeMailMessage(MimeMessage mimeMessage) { this.helper = new MimeMessageHelper(mimeMessage); } /** * Return the MimeMessageHelper that this MimeMailMessage is based on. */ public final MimeMessageHelper getMimeMessageHelper() { return this.helper; } /** * Return the JavaMail MimeMessage that this MimeMailMessage is based on. */ public final MimeMessage getMimeMessage() { return this.helper.getMimeMessage(); } @Override public void setFrom(String from) throws MailParseException { try { this.helper.setFrom(from); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setReplyTo(String replyTo) throws MailParseException { try { this.helper.setReplyTo(replyTo); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setTo(String to) throws MailParseException { try { this.helper.setTo(to); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setTo(String... to) throws MailParseException { try { this.helper.setTo(to); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setCc(String cc) throws MailParseException { try { this.helper.setCc(cc); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setCc(String... cc) throws MailParseException { try { this.helper.setCc(cc); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setBcc(String bcc) throws MailParseException { try { this.helper.setBcc(bcc); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setBcc(String... bcc) throws MailParseException { try { this.helper.setBcc(bcc); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setSentDate(Date sentDate) throws MailParseException { try { this.helper.setSentDate(sentDate); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setSubject(String subject) throws MailParseException { try { this.helper.setSubject(subject); } catch (MessagingException ex) { throw new MailParseException(ex); } } @Override public void setText(String text) throws MailParseException { try { this.helper.setText(text); } catch (MessagingException ex) { throw new MailParseException(ex); } } }

异常接口:MailException

 This package also contains a hierarchy of checked exceptions that provide a higher level of abstraction over the lower level mail system exceptions, with the root exception being MailException.

        Mail是邮件发送的异常处理根接口,源码如下,

@SuppressWarnings("serial")
public abstract class MailException extends NestedRuntimeException {

	/**
	 * Constructor for MailException.
	 * @param msg the detail message
	 */
	public MailException(String msg) {
		super(msg);
	}

	/**
	 * Constructor for MailException.
	 * @param msg the detail message
	 * @param cause the root cause from the mail API in use
	 */
	public MailException(@Nullable String msg, @Nullable Throwable cause) {
		super(msg, cause);
	}

}

        当然,它也内置了一系列子接口,

SpringBoot:邮件发送_第2张图片

        基本含义如下, 

MailSendException:邮件发送失败异常

MailParseException:非法的邮件配置属性

MailPreparationException:邮件模板渲染出错时,会抛出此类异常。

MailAuthenticationException:认证失败异常。

application.yml配置

        要使用JavaMailSender实现邮件发送,那么就需要先对其进行配置。通过上述内容的初步了解,基本上可以确定我们要配置的参数就和JavaMailSenderImpl实现子类相关了,其成员属性如下,和SpringBoot官网配置文档给出的配置参数也是一一对应的。

SpringBoot:邮件发送_第3张图片 JavaMailSenderImpl实现子类
SpringBoot:邮件发送_第4张图片 springBoot官网配置文档

        我所做的配置如下,

spring:
  mail:
    protocol: smtp
    host: smtp.qq.com
    default-encoding: UTF-8
    username: email-account
    password: key/password
    properties:
      mail:
        debug: true #开启日志打印
        smtp:
          auth: true
          starttls:
            enable: true
            required: true

邮件发送

        上面了解到两种邮件形式,所以,接下来我们尝试一下,如何发送简单文本邮件,以及自定义模板的邮件。

SimpleMailMessage发送

        简单邮件的发送示例代码如下,其中:JavaMailSender 实例即为文章开头部分提到的,由Spring框架自动注入的Bean实例。


@Component
public class EmailSendServiceImpl implements EmailSenderService {

    @Autowired
    private JavaMailSender mailSender;

    @Override
    public R sendSimpleEmail(String from, String to, String subject, String text) {
        try {
            //创建邮件对象
            SimpleMailMessage mailMessage = new SimpleMailMessage();
            //设置邮件属性
            mailMessage.setFrom(from);//发件人
            mailMessage.setTo(to);//收件人
            mailMessage.setSubject(subject);//主题
            mailMessage.setText(text);//文本内容
            mailMessage.setSentDate(new Date());//发送日期
            //发送邮件
            this.mailSender.send(mailMessage);
            //返回消息
            return R.ok("邮件发送成功!");
        } catch (MailException e) {
            e.printStackTrace();
            return R.fail("邮件发送失败!");
        }
    }
}

MimeMailMessage发送

        为了方便向邮件中写入自定义内容,所以自定义邮件模板需要借助后端的模板引擎来实现,此处我们选择thymeleaf模板引擎。以下为此时的依赖,

       
        
            org.springframework.boot
            spring-boot-starter-mail
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        

thymeleaf配置

SpringBoot:邮件发送_第5张图片

         如上图所示,thymeleaf模板引擎内置了一套配置规则,最简单的方式就是将其原模原样的写到application.yml配置文件中即可。

        以下为我的配置项,

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML

邮件模板定制

邮件模板定制,其实就是写一个前端页面,然后借助thymeleaf模板引擎的表达式语法,实现自定义内容的动态填充即可。

关于Thymeleaf的表达式语法,详情可参阅官网文档:Tutorial: Using Thymeleaf

        但是说实话,对于简单的文本替换,以下的简单表达式语法基本就能满足了。

SpringBoot:邮件发送_第6张图片

 关于邮件模板的定制,如果要求特别高的话,可以自己从零开始写;但是,如果要快速实现的话,也可以借助在线生成工具,这里比较推荐:拉易网提供的精美邮件定制服务,里面内置了一些可以直接使用的邮件模板,也可以在此基础上进行个性化定制,零代码自动生成。

最终将定制好的模板下载下来即可。

SpringBoot:邮件发送_第7张图片       

SpringBoot:邮件发送_第8张图片 如何使用邮件模板

        那么我们下载下来的邮件模板如何使用呢?

其实和我们的Thymeleaf模板引擎配置相关,毕竟是要将这个模板交给Thymeleaf模板引擎进行值的动态替换和渲染的。

SpringBoot:邮件发送_第9张图片

这里对应于thymeleaf的prefix配置项,我的email.html邮件模板就放在templates路径下。

        另外,为了实现动态替换文本,例如:我这里想要实现邮件验证码,那么,我就要通过表达式语法,提供一个文本字符串的变量verifyCode,以便于在后面发送邮件时,将这个变量替换为任何我们随机生成的验证码字符串。

示例代码

终于来到代码环节了,但是前面的环节也确实缺一不可。

@Component
public class EmailSendServiceImpl implements EmailSenderService {

    @Autowired
    private JavaMailSender mailSender;
    @Autowired
    private TemplateEngine templateEngine;


    @Override
    public R sendMimeEmail(String from, String to, String subject) {
        //create a new JavaMail MimeMessage
        MimeMessage mimeMailMessage = this.mailSender.createMimeMessage();
        MimeMessageHelper mimeMessageHelper = null;
        try {
            /**
             * mimeMessage – the mime message to work on
             * multipart – whether to create a multipart message that supports alternative texts, inline elements and attachments (corresponds to MULTIPART_MODE_MIXED_RELATED)
             */
            mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);
            //setting basic params
            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(to);
            mimeMessageHelper.setSubject(subject);
            //create html-text based on thymeleaf template
            Context context = new Context();
            context.setVariable("verifyCode","456935");
            String process = templateEngine.process("email", context);
            //设置邮件内容
            /**
             * process:the text for the message
             * html:whether to apply content type "text/html" for an HTML mail, using default content type ("text/plain") else
             */
            mimeMessageHelper.setText(process,true);
            //发送邮件
            this.mailSender.send(mimeMailMessage);
            return R.ok("邮件发送成功!");
        } catch (MessagingException e) {
            e.printStackTrace();
            return R.fail("邮件发送失败!");
        }
    }
}

* 模板变量替换:Context探究

        先看一下源码注释,

IContext接口的非web(场景)实现,适用于非web场景。

SpringBoot:邮件发送_第10张图片

        我们继续查看Context父类AbstractContext实现的IContext接口,可以看到,有一个我们刚才用于动态替换thymeleaf模板变量的方法,

SpringBoot:邮件发送_第11张图片

        注意到它还提供了一个重载方法,可以实现多个模板变量值的设置,都是以key-value键值对的形式出现。

SpringBoot:邮件发送_第12张图片

你可能感兴趣的:(Java,开源项目,GIS技术,spring,boot,java,后端)