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

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

一、最简版spring boot发邮件

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

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>复制代码

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

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,真是一言难尽...

二、受权码发邮件

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

javax.mail.AuthenticationFailedException: 535 Error: authentication failed复制代码

其中的一个缘由是邮件服务器使用了受权码登陆方式,也就是第三方登陆不能直接使用帐号密码,而须要使用一种受权码的方式,好比上面的163.com邮箱,就能够设置受权码登陆,设置界面以下:服务器

QQ邮箱更是默认就须要用受权码登陆,QQ邮箱的受权登陆操做文档请看这里app

使用受权码登陆后,须要把以前application.properties里面spring.mail.password的内容从密码换成受权码,就可以正常发邮件了。spring-boot

三、简单聊下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,把几乎全部邮件发送须要处理的问题就集中到了这个类里面,使用方便又好找。下面是这个类全部的方法。

这个类不可谓不复杂,基本上涵盖了全部发邮件方面的功能,但由于都集成在一个类里面,很是方便好用,能够说是一个对程序员友好的典范了,值得你们在开发时作借鉴。

相关文章
相关标签/搜索