前言:
发送邮件,肯定是每个公司都会有的基本业务。很多公司都会选择把发送邮件作为一个基础服务,对外提供接口。直接调用就可发邮件了。但是我们都知道发送邮件耗时都比较长。那么今天就介绍下使用Spring boot+eventbus来打造一个简单邮件服务
规划接口列表
发送邮件的类型准备的有三种
- 发送普通邮件
 - 发送html邮件
 - 发送图文邮件
 
还有一个细节,如果我们同步的取发送邮件会有两个问题。
- 接口响应时间比较长
 - 遇到并发的情况,容易导致服务器压力过大或者邮箱服务封ip
 
所以我们准备使用队列来执行发送邮件的操作。可以解决这个问题。队列我选用的是Google的eventbus。是一款很轻量的队列。直接走的内存
准备工作
首先要在pom.xml中引入 需要使用的包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
- spring-boot-starter-mail :spring-boot提供的发邮件的maven库
 - guava:google提供的开源库。里面包含来很多工具
 - lombok:可以帮你省去编写实体类的工具
 
引入之后,我们还需要配置发送邮件所需要的必要配置
在application.properties中配置邮箱
spring.mail.host=smtp.mail.me.com //邮箱发送服务器 spring.mail.port=587//服务器端口 spring.mail.username=xxx6666@icloud.com//发件人邮箱 spring.mail.password=password//客户端专用密码 //如果和我一样使用的icloud邮箱 还需要下列两个配置,别的有的邮箱不需要 spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true
做到这里其实就已经完成了,发邮件所需要的配置了。但是我们是要用队列来发送,所以还需要配置下队列
@Configuration
public class AsyncEventBusConfig {
 //实例化bean,采用单例形式注入容器
 @Bean
 @Scope("singleton")
 public AsyncEventBus asyncEventBus(){
  //创建线程池对象
  final ThreadPoolExecutor executor=executor();
  return new AsyncEventBus(executor);
 }
 //创建线程池方法
 private ThreadPoolExecutor executor(){
  return new
    ThreadPoolExecutor(2,
    2,0L,
    TimeUnit.MICROSECONDS,
    new LinkedBlockingQueue<>());
 }
}
封装EmailService
准备好了之后,就可以直接来封装发送邮件的业务了。之前有提到我们需要三个接口,同样的,我们也需要三个service方法
@Service
public class EmailService {
 @Autowired
 private JavaMailSender javaMailSender;
 /**
  * 发件人。这里发件人一般是同使用的发件邮箱一致
  */
 @Value("${spring.mail.username}")
 private String from;
 /**
  * 发送文本邮件
  * @param to 收件人邮箱地址
  * @param subject 主题
  * @param content 内容
  */
 public void sendTextMail(String to,
        String subject,
        String content) {
  SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
  simpleMailMessage.setTo(to);
  simpleMailMessage.setSubject(subject);
  simpleMailMessage.setText(content);
  simpleMailMessage.setFrom(from);
  javaMailSender.send(simpleMailMessage);
 }
 /**
  * 发送html内容的邮件
  * @param to 收件人
  * @param htmlContent html内容
  * @param subject 主题
  * @throws MessagingException
  */
 public void sendHtmlMail(String to,
        String htmlContent,
        String subject) throws MessagingException {
  MimeMessage message = javaMailSender.createMimeMessage();
  MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
  messageHelper.setTo(to);
  messageHelper.setSubject(subject);
  messageHelper.setFrom(from);
  messageHelper.setText(htmlContent, true);
  javaMailSender.send(message);
 }
 /**
  * 发送图文邮件
  * @param to 收件人
  * @param imgContent 图文内容
  * @param subject 主题
  * @param rscId 资源id
  * @param imgPath 资源路径
  * @throws MessagingException
  */
 public void sendImgMail(String to,
       String imgContent,
       String subject,
       String rscId,
       String imgPath) throws MessagingException {
  MimeMessage message = javaMailSender.createMimeMessage();
  MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
  messageHelper.setTo(to);
  messageHelper.setSubject(subject);
  messageHelper.setFrom(from);
  messageHelper.setText(imgContent, true);
  messageHelper.addInline(rscId, new File(imgPath));
  javaMailSender.send(message);
 }
}
队列监听
既然封装好了方法,那么就需要调用。调用的方式,其实就是将接口传来的数据传到队列里。队列的消费者接收到了消息就将消息拿来调用发送邮件的方法
我们首先创建一个消费类,用来接受消息,处理消息。
@Service
public class EventBusListener {
 /**
  * 引入bean
  */
 @Autowired
 private AsyncEventBus asyncEventBus;
 @Autowired
 private EmailService emailService;
 /**
  * 注册服务类
  */
 @PostConstruct
 public void init(){
  asyncEventBus.register(this);
 }
 /**
  * 线程安全,消费 文本消息
  * @param textEmailDTO
  */
 @AllowConcurrentEvents
 @Subscribe
 public void sendTextMail(TextEmailDTO textEmailDTO){
  emailService.sendTextMail(
    textEmailDTO.getTo(),
    textEmailDTO.getSubject(),
    textEmailDTO.getContent()
  );
 }
 /**
  * 线程安全 消费 html消息
  * @param htmlEmailDTO
  */
 @AllowConcurrentEvents
 @Subscribe
 public void sendHtmlMail(HtmlEmailDTO htmlEmailDTO){
  try {
   emailService.sendHtmlMail(
     htmlEmailDTO.getTo(),
     htmlEmailDTO.getHtmlContent(),
     htmlEmailDTO.getSubject()
   );
  } catch (MessagingException e) {
   // nothing to do
  }
 }
 /**
  * 线程安全 消费 图文消息
  * @param imgEmailDTO
  */
 @AllowConcurrentEvents
 @Subscribe
 public void sendImgMail(ImgEmailDTO imgEmailDTO){
  try {
   emailService.sendImgMail(
     imgEmailDTO.getTo(),
     imgEmailDTO.getImgContent(),
     imgEmailDTO.getSubject(),
     imgEmailDTO.getRscId(),
     imgEmailDTO.getImgPath()
   );
  } catch (MessagingException e) {
   // nothing to do
  }
 }
}
其实eventbus抛消息都是使用的post方法来抛消息。走到不同的方法里面是利用了类的多态,抛入不同的实体类就可以进行区分了。走进了不同的方法,就调用相应Service方法。
控制器与测试
控制器部分,没什么好说的,我就贴出图文的代码。其余代码可以在我的github上面看
先看眼实体类
@Data
public class ImgEmailDTO implements Serializable {
 public ImgEmailDTO() {
 }
 /**
  * 图片路径
  */
 private String imgPath;
 /**
  * 资源id
  */
 private String rscId;
 /**
  * 主题
  */
 private String subject;
 /**
  * 图片正文(同样可以使用html)
  */
 private String imgContent;
 /**
  * 收件人
  */
 private String to;
}
 /**
  * 发送图文邮件
  * @param request
  * @return
  */
 @RequestMapping(value = "/sendImgMail", method = RequestMethod.POST)
 public Result<Integer> sendImgMail(@RequestBody Request<ImgEmailDTO> request) {
  Result<Integer> result = Result.create();
  ImgEmailDTO imgEmailDTO=request.getData();
  StringBuilder sb=new StringBuilder();
  sb.append(imgEmailDTO.getImgContent());
  //cid:资源id。在spring中会自动绑定
  sb.append("<img src=\'cid:").append(imgEmailDTO.getRscId()).append("\'></img>");
  imgEmailDTO.setImgContent(sb.toString());
  asyncEventBus.post(imgEmailDTO);
  return result.success(1);
 }
图文要稍微特殊一点,需要拼接下正文内容。然后将实体类中的content替换。最后将实体类抛入队列。直接返回接口请求。队列那边就会排着队搞定所有的邮件
下面来做个测试

请求很迅速的返回了结果
然后去邮箱中查看结果

好了今天对邮件服务的介绍就写到这里。知识点并不深奥,主要介绍一个思路。如有不对的地方,请大神指出。谢谢
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对自由互联的支持。
