文档章节

从spring boot发邮件聊到开发的友好性

wphmoon
 wphmoon
发布于 10/03 17:52
字数 1549
阅读 34
收藏 0

    前些天帮一个朋友做网站,全站都是静态页面,唯一需要用到后端开发的是他需要一个留言板。传统的留言板一般都是提交后保存到数据库,然后提供一个后台的留言列表给管理人员看,我嫌麻烦,就决定留言提交到后台直接发邮件出去,这样就不用开发后台页面了,他也不需要登录一个什么后台才能看留言,两全其美,岂不美哉。

1、最简版spring boot发邮件

spring boot发邮件还是挺简单的,首先把发邮件的start加到pom里面:

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

然后在application.properties里面配置好关于发邮件的参数

spring.mail.host=smtp.163.com
spring.mail.port=25
spring.mail.username=yourmail@163.com
spring.mail.password=yourpassword

其中spring.mail.host和spring.mail.username是一一对应的,哪里的邮箱就要用哪里的smtp服务器

然后我写了一个controller来接收留言板的内容:

    @Value("${spring.mail.username}")
	private String fromMail;
	@Autowired
    private JavaMailSender mailSender;

    @RequestMapping(value = "/getNote", method = RequestMethod.POST)
	public String getNote(Note note) {
		MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper;
		try {
			helper = new MimeMessageHelper(mimeMessage, true);
			//发件人
	        helper.setFrom(fromMail,note.yourName);
	        //收件人(留言内容最终发往的邮箱地址)
	        helper.setTo("recieve@mail.com");
	        //标题
	        helper.setSubject(note.yourSubject);
	        //文本
	        helper.setText("from email:"+note.yourEmail+"\n"+note.yourMessage);
	        mailSender.send(mimeMessage);
		} catch (MessagingException | UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
               
		return "redirect:return.htm";
	}
public class Note {
	String yourName;
	String yourEmail;
	String yourSubject;
	String yourMessage;

//getter,setter省略
}
  • note对象是留言板的内容
  • JavaMailSender和MimeMessageHelper是官方推荐的好基友,一般配套使用
  • fromMail就是在application.properties里面配置的spring.mail.username属性,也就是发邮件的一方,用helper.setTo(...)配置
  • 额外说一下,由于填留言板时一般也会留邮件地址,但那个邮件地址和这里的任何一个设置邮件的地方毫无关系,只需要在邮件的文本里面做记录就好了,我之前就弱智了一把,把留言板里面的邮件地址填到helper.setTo(...),结果浪费了我半个小时查这种bug,真是一言难尽...

2、授权码发邮件

     很多时候,按照上面的方法发邮件会出现下面的错误:

javax.mail.AuthenticationFailedException: 535 Error: authentication failed

其中的一个原因是邮件服务器使用了授权码登录方式,也就是第三方登录不能直接使用账号密码,而需要使用一种授权码的方式,比如上面的163.com邮箱,就可以设置授权码登录,设置界面如下:

QQ邮箱更是默认就需要用授权码登录,QQ邮箱的授权登录操作文档请看这里

使用授权码登录后,需要把之前application.properties里面spring.mail.password的内容从密码换成授权码,就能够正常发邮件了。

3、简单聊下JavaMailSender和MimeMessageHelper

我们查下JavaMailSender的代码就知道,它其实背景比较复杂。首先它继承了org.springframework.mail.MailSender接口。

public interface JavaMailSender extends MailSender {
...
}

而它本身也是一个接口,实现类只有一个,JavaMailSenderImpl

我们再来翻JavaMailSenderImpl的代码,发现spring并没有自己来做发送邮件的功能,而是直接用了java自身的邮件发送功能,核心是这一段

protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
		Map<Object, Exception> failedMessages = new LinkedHashMap<>();
		Transport transport = null;

		try {
			for (int i = 0; i < mimeMessages.length; i++) {

				// Check transport connection first...
				if (transport == null || !transport.isConnected()) {
					if (transport != null) {
						try {
							transport.close();
						}
						catch (Exception ex) {
							// Ignore - we're reconnecting anyway
						}
						transport = null;
					}
					try {
						transport = connectTransport();
					}
					catch (AuthenticationFailedException ex) {
						throw new MailAuthenticationException(ex);
					}
					catch (Exception ex) {
						// Effectively, all remaining messages failed...
						for (int j = i; j < mimeMessages.length; j++) {
							Object original = (originalMessages != null ? originalMessages[j] : mimeMessages[j]);
							failedMessages.put(original, ex);
						}
						throw new MailSendException("Mail server connection failed", ex, failedMessages);
					}
				}

				// Send message via current transport...
				MimeMessage mimeMessage = mimeMessages[i];
				try {
					if (mimeMessage.getSentDate() == null) {
						mimeMessage.setSentDate(new Date());
					}
					String messageId = mimeMessage.getMessageID();
					mimeMessage.saveChanges();
					if (messageId != null) {
						// Preserve explicitly specified message id...
						mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId);
					}
					Address[] addresses = mimeMessage.getAllRecipients();
					transport.sendMessage(mimeMessage, (addresses != null ? addresses : new Address[0]));
				}
				catch (Exception ex) {
					Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
					failedMessages.put(original, ex);
				}
			}
		}
		finally {
			try {
				if (transport != null) {
					transport.close();
				}
			}
			catch (Exception ex) {
				if (!failedMessages.isEmpty()) {
					throw new MailSendException("Failed to close server connection after message failures", ex,
							failedMessages);
				}
				else {
					throw new MailSendException("Failed to close server connection after message sending", ex);
				}
			}
		}

		if (!failedMessages.isEmpty()) {
			throw new MailSendException(failedMessages);
		}
	}

doSend方法中调用的核心类就是Transport类,这个类的包名是javax.mail。spring不愧是集成大师,java自带的mail功能经过spring的标准化包装就成了spring自身功能的一部分,再通过spring boot的包装,用starter的方式再次做简化,我们就能够直接通过极简的方式使用了。

当然,简化的方法多种多样,另外的一种形式的包装就是使用helper类的方法,spring使用的就是MimeMessageHelper。在javax.mail在处理邮件的方式上,使用的是分而治之的办法,不同的类处理不同的问题,所以看到很多的类在处理各种问题和情况。

这种做法在实现功能上是很好的,把一个复杂的问题分解成若干个小问题,分别实现。但对使用的开发人员就谈不上友好了,容易出现以下几个问题:

  • 不直观,调用者不知道从何下手
  • 查找麻烦,类太多而且功能分散,不容易找到对应的功能类
  • 关系复杂,经常对要引用哪个类会很没有把握,因为太多处理单一问题的类
  • 没有统一的入口,上手难,很难脱离文档直接使用

针对上述情况,spring通过MimeMessageHelper,把几乎所有邮件发送需要处理的问题就集中到了这个类里面,使用方便又好找。下面是这个类所有的方法。

这个类不可谓不复杂,基本上涵盖了所有发邮件方面的功能,但因为都集成在一个类里面,非常方便好用,可以说是一个对程序员友好的典范了,值得大家在开发时做借鉴。

 

 

© 著作权归作者所有

wphmoon
粉丝 12
博文 36
码字总数 64434
作品 0
广州
高级程序员
私信 提问
对照谈-官方spring-boot-starter和自定义starter异同分析

在前面我讲用spring-boot-starter-mail发邮件的时候,我侧重看的是spring boot发邮件的便利性,今天,我们聊下另外一个方面,spring-boot-starter自身的便利性。 1、看看官方starter的jar里面...

wphmoon
10/10
35
0
使用 Spring Boot 开发邮件系统

使用 Spring Boot 开发邮件系统 作者简介: 纯洁的微笑,一个多年在一线奋斗的程序员,从2016年开始研究 Spring Boot/Cloud ,先后写了很多关于 Spring Boot/Cloud 的文章,后来就成为了各大...

GitChat技术杂谈
2018/09/20
0
0
什么是Kotlin?Java的替代语言?

什么是Kotlin Kotlin是一个基于JVM的新的编程语言,2010年由IntelliJ IDEA所在的JetBrains公司开发,自2012年以来一直开源。 Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没...

java技术栈
2017/10/17
0
0
Spring Boot admin 2.0 详解

一、什么是Spring Boot Admin ? Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册(通过...

公众号:程序员果果
05/21
0
0
SpringBoot | 第二十八章:监控管理之Spring Boot Admin使用

前言 上一章节,我们介绍了的使用,知道了可通过访问不同的端点路径,获取相应的监控信息。但使用后也能发现,返回的监控数据都是以JSON串的形式进行返回的,对于实施或者其他人员来说,不是...

oKong
2018/09/14
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Win10如何修改入站规则

桌面->网络 右击 -> 属性 -> 防火墙 点击 高级设置 右击入站规则 -> 新建规则 根据端口设置入站规则

恒宝乐园
28分钟前
7
0
在PPT中将照片变裸眼3D效果怎样操作?分享操作技巧,帮你快速实现

PPT的使用相信大家都不陌生,使用最多的就是制作PPT对工作进行汇报,对新项目进行展开讨论。其实在PPT中还可以设计海报,制作高逼格封面以及将照片变为3D效果等偏设计类的操作。今天将以如何...

百因必有果
37分钟前
7
0
苹果的M4A音频格式怎么转换成FLAC

最新版的苹果手机iPhone 11其完美的系统和华丽的外观真是让人想“剁手”。不过使用过苹果手机或者iPad等移动设备的朋友可能知道,其音乐文件为M4A音频格式。而这种文件格式最初是在iTunes以及...

软件分享达人
38分钟前
10
0
提现设置

提现设置 用户如何提现? 1.打开后台-设置-交易设置-提现设置 最低提现金额:提现金额将不小于此值才能提现 最小提现单元:每次以最小提现单元金额的整数倍提现 提现提示信息:商家可填写提示...

Geek-Chic
今天
3
0
php的redis.so扩展安装【PHP】

一、资源准备,下载 不仅是redis.so,安装其他拓展的方式也类似 第一种方式: 下载:wget https://github.com/phpredis/phpredis/archive/develop.zip 解压:unzip develop.zip 进入目录:c...

一代码农码一代
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部