From spring boot email to development friendliness

Keywords: Programming Spring Java Database Attribute

A few days ago, I helped a friend to build a website. All the websites were static pages. The only thing that needed to be developed in the back end was that he needed a message board. The traditional message boards are usually saved after submission to the database, and then provide a backstage message list for managers to see. I find it troublesome to decide the message to submit to the background to send out the mail directly, so that there is no need to develop the backstage page. He does not need to log in for a background to see the message.

1. Minimalist Sprboot Email

Sprboot email is still quite simple. First, add the start of the email to the pom:

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

Then configure the parameters about sending mail in application.properties

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

Among them, spring.mail.host and spring.mail.username are one-to-one correspondence, where the mailbox will use where the smtp server.

Then I wrote a controller to receive the message board:

    @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);
			//Sender
	        helper.setFrom(fromMail,note.yourName);
	        //Recipient (e-mail address to which message content is ultimately sent)
	        helper.setTo("recieve@mail.com");
	        //Title
	        helper.setSubject(note.yourSubject);
	        //text
	        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 ellipsis
}
  • note object is the content of message board
  • JavaMailSender and MimeMessageHelper are recommended by the government.
  • fromMail is the spring.mail.username attribute configured in application.properties, that is, the emailing party, which is configured with helper.setTo(...).
  • To put it another way, I usually leave an email address when I fill in the message board, but the email address has nothing to do with any place where I set up the mail. I just need to record the text in the mail. I used to be mentally retarded. I filled in the mail address in the message board to helper.setTo(...), and it took me half an hour to check the bug. Hard to say...

2. Authorization Code Email

In many cases, the following errors occur when sending an email according to the above method:

javax.mail.AuthenticationFailedException: 535 Error: authentication failed

One of the reasons is that mail servers use authorization code login mode, that is, third party login can not use account password directly, but need to use an authorization code mode, such as the 163.com mailbox above, you can set authorization code login, the setting interface is as follows:

QQ mailbox is the default need to login with authorization code, QQ mailbox authorization login operation document see Here

After login with authorization code, you need to change the content of spring.mail.password in application.properties from password to authorization code to send mail normally.

3. Talk about JavaMailSender and MeimeMessageHelper

As you can see from the JavaMailSender code, its background is rather complicated. First, it inherits the org. spring framework. mail. MailSender interface.

public interface JavaMailSender extends MailSender {
...
}

It's also an interface, and there's only one implementation class, JavaMailSenderImpl.

Let's look at the code of JavaMailSenderImpl again and find that spring does not do its own email sending function, but directly uses java's own email sending function, the core of which is this paragraph.

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);
		}
	}

The core class called in the doSend method is the Transport class, whose package name is javax.mail. Spring is a master of integration. The mail function of java has become a part of spring's own function after spring's standardized packaging. Then, through spring boot's packaging, we can simplify it again in the way of starter, and we can use it directly in a minimalist way.

Of course, there are many ways to simplify. Another form of packaging is to use the helper class method. spring uses MimeMessageHelper. In the way javax.mail handles mail, it uses a divide and conquer method. Different classes deal with different problems, so we see many classes dealing with various problems and situations.

This method is very good in realizing functions. It decomposes a complex problem into several small problems and realizes them separately. However, it is not friendly to the developers who use it. The following problems easily arise:

  • Intuitively, the caller does not know where to start.
  • Finding trouble, too many classes and decentralized functions, it is not easy to find corresponding functional classes
  • Relationships are complex, and it is often uncertain which class to refer to, because there are too many classes dealing with a single problem.
  • There is no uniform entry. It is difficult to get started. It is difficult to use it directly from the document.

In response to the above situation, spring through MimeMessageHelper, almost all the issues that need to be addressed when sending mail are concentrated in this class, which is easy to use and easy to find. Here are all the methods of this class.

This class is not complicated, basically covers all the functions of sending e-mail, but because it is integrated into a class, it is very convenient and easy to use. It can be said to be a good example for programmers, and it is worth learning from in development.

Posted by mattcooper on Tue, 08 Oct 2019 00:43:37 -0700