A, in actual combat

1 Email preparation

Here is an example of qq mailbox, the same as other mailboxes

1.1 Login to the Mailbox Find the Settings

1.2 Find the POP3/IMAP/SMTP service Settings in the mailbox and enable the POP3/SMTP,IMAP/SMTP service

At this point, the mailbox setup is complete.

1.3 Finally check the QQ mailbox POP3 and SMTP server

email POP3 server (port 995) SMTP server (port 465 or 587)
qq.com pop.qq.com smtp.qq.com

Other mailboxes can be found in the official server address.

Note: For more information about POP3, SMTP, and IMAP protocols, see Part 3.

2 Code Practice

2.1 Setting up the Spring Boot Project

Introduce the spring-boot-starter-mail dependency

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

2.2 Email Configuration

Configure application. Properties

# email server address
spring.mail.host=smtp.qq.com
# username
spring.mail.username=[email protected]
# your password
spring.mail.password=XXXXXXX
# hudmmejunplchdea
spring.mail.default-encoding=UTF-8
spring.mail.port=587
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
# Who will send the email
mail.fromMail.addr=[email protected]
Copy the code

2.3 Write mail sending service and its implementation class

Implemented through the JavaMailSender provided by Spring Boot Mail

package com.ganhuojun.demo.service;
import java.io.ByteArrayOutputStream;
public interface MailService {
    /** ** Send email *@paramTo destination *@paramThe subject theme *@paramThe content content *@paramOS attachment *@paramAttachmentFilename Attachment name *@throws Exception
     */
    public void sendMail(String to, String subject, String content, ByteArrayOutputStream os, String attachmentFilename, Boolean isHtml) throws Exception;

    public void sendMail(String[] to, String subject, String content, ByteArrayOutputStream os, String attachmentFilename, Boolean isHtml) throws Exception;
}
Copy the code

The implementation class

package com.ganhuojun.demo.service.impl;
import com.ganhuojun.demo.service.MailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.mail.internet.MimeMessage;
import java.io.ByteArrayOutputStream;
@Component
public class MailServiceImpl implements MailService {
    private static Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);

    @Autowired
    private JavaMailSender mailSender;

    @Value("${mail.fromMail.addr}")
    private String from;

    @Override
    public void sendMail(String to, String subject, String content, ByteArrayOutputStream os, String attachmentFilename, Boolean isHtml) throws Exception {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, isHtml);
            if (null! = os) {/ / accessories
                InputStreamSource inputStream = new ByteArrayResource(os.toByteArray());
                helper.addAttachment(attachmentFilename, inputStream);
            }

            mailSender.send(message);
            logger.info("Simple mail has been sent.");
        } catch (Exception e) {
            logger.error("An exception occurred while sending simple mail!", e);
            throw new Exception("An exception occurred while sending simple mail!",e); }}@Override
    public void sendMail(String[] to, String subject, String content, ByteArrayOutputStream os, String attachmentFilename, Boolean isHtml) throws Exception {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, isHtml);
            if (null! = os) {/ / accessories
                InputStreamSource inputStream = new ByteArrayResource(os.toByteArray());
                helper.addAttachment(attachmentFilename, inputStream);
            }

            mailSender.send(message);
            logger.info("Simple mail has been sent.");
        } catch (Exception e) {
            logger.error("An exception occurred while sending simple mail!", e);
            throw new Exception("An exception occurred while sending simple mail!",e); }}}Copy the code

2.4 Writing test cases

package com.ganhuojun.demo.service;

import com.ganhuojun.demo.DemoApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.web.WebAppConfiguration;

@SpringBootTest(classes = DemoApplication.class)
@WebAppConfiguration
public class MailServiceTest {
    @Autowired
    MailService mailService;

    @Test
    public void sendMail(a){
        try {
            mailService.sendMail("[email protected]"."Title"."Contents of email".null.null.false);
        }catch (Exception e){
            System.out.println("fail");
        }
        System.out.println("success"); }}Copy the code

2.5 Running Test Cases

Check the mailbox and find that the email has been received

Second, source code interpretation

Many people will have a question, we know spring-boot-starter-mail, but I do not know where to start with the source code, we from the principle of analysis for you.

1 Learn about the starter mechanism of Spring Boot

If you know the starter of Spring Boot, you will immediately know how it works. This article does not describe the starter mechanism in detail. If you need to know more about it, you can query it by yourself. Let’s briefly describe it:

SpringBoot projects have the @SpringBootApplication annotation on the Application class, which automatically introduces the @enableAutoConfiguration annotation to enable the spring Boot default configuration. @enableAutoConfiguration In the spring-boot-Autoconfigure package.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<? >[] exclude()default {};

    String[] excludeName() default {};
}
Copy the code

Because @ EnableAutoConfiguration annotations using the @ Import ({AutoConfigurationImportSelector. Class}), if you look at the source code, You can finally see that it’s reading data from meta-INF/Spring.Factories

The source code is as follows:

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if(result ! =null) {
            return result;
        } else {
            try{ Enumeration<URL> urls = classLoader ! =null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) { Entry<? ,? > entry = (Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); }}}Copy the code

Let’s look at the spring.factories file in the spring-boot-autoconfigure package

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\...
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
...
Copy the code

At this point be suddenly enlightened, originally we spring the boot to the entrance of the mail is the class MailSenderAutoConfiguration, we analyzed below.

2 class MailSenderAutoConfiguration email configuration

@Configuration( proxyBeanMethods = false )
@ConditionalOnClass({MimeMessage.class, MimeType.class, MailSender.class})
@ConditionalOnMissingBean({MailSender.class})
@Conditional({MailSenderAutoConfiguration.MailSenderCondition.class})
@EnableConfigurationProperties({MailProperties.class})
@Import({MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class})
public class MailSenderAutoConfiguration {
    public MailSenderAutoConfiguration(a) {}static class MailSenderCondition extends AnyNestedCondition {
        MailSenderCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty( prefix = "spring.mail", name = {"jndi-name"} )
        static class JndiNameProperty {
            JndiNameProperty() {
            }
        }

        @ConditionalOnProperty( prefix = "spring.mail", name = {"host"} )
        static class HostProperty {
            HostProperty() {
            }
        }
    }
}
Copy the code

@ ConditionalOnClass @ ConditionalOnMissingBean, @ Conditional MailSenderAutoConfiguration created these configuration said conditions (introducing spring – the boot – starter E-mail will meet its conditions)

@ EnableConfigurationProperties ({MailProperties. Class}) that import mail configuration here, we see MailProperties. Class

@ConfigurationProperties( prefix = "spring.mail" )
public class MailProperties {
    
    private static final Charset DEFAULT_CHARSET;
    
    /** * SMTP server host. For instance, `smtp.example.com`. */
	private String host;

	/** * SMTP server port. */
	private Integer port;

	/** * Login user of the SMTP server. */
	private String username;

	/** * Login password of the SMTP server. */
	private String password;

	/** * Protocol used by the SMTP server. */
	private String protocol = "smtp";

	/** * Default MimeMessage encoding. */
	private Charset defaultEncoding = DEFAULT_CHARSET;

	/** * Additional JavaMail Session properties. */
	private Map<String, String> properties = new HashMap<>();

	/** * Session JNDI name. When set, takes precedence over other Session settings. */
	private String jndiName;
    
    // The getter and setter are omitted here
}
Copy the code

Here explains why our actual configuration files are spring. Mail, which attributes, what the meaning of each field, the source code can be seen.

So where does spring manage the JavaMailSender interface that we use at the bottom of sending emails?

Of course is @ Import ({MailSenderJndiConfiguration. Class, MailSenderPropertiesConfiguration. Class}) for us to do the core is as follows:

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
class MailSenderPropertiesConfiguration {

	@Bean
	@ConditionalOnMissingBean(JavaMailSender.class)
	JavaMailSenderImpl mailSender(MailProperties properties) {
		JavaMailSenderImpl sender = new JavaMailSenderImpl();
		applyProperties(properties, sender);
		return sender;
	}
    // Ignore other code here... }Copy the code

That builds the JavaMailSender for us

3 Learn about the Send method of JavaMailSenderImpl

 public void send(MimeMessage mimeMessage) throws MailException {
     this.send(mimeMessage);
 }

public void send(MimeMessage... mimeMessages) throws MailException {
    this.doSend(mimeMessages, (Object[])null);
}
Copy the code

The send method is overloaded.

And the answer is yes, so what do we make of this?

After the debugging, the send method is redirected to the send method, causing confusion.

Thinking there might be a comment in the source code to explain it, I clicked Download Sources in the upper right corner

And then, when we download the source code this code is going to be, all of a sudden, it’s going to change

@Override
public void send(MimeMessage mimeMessage) throws MailException {
    send(new MimeMessage[] {mimeMessage});
}

@Override
public void send(MimeMessage... mimeMessages) throws MailException {
    doSend(mimeMessages, null);
}
Copy the code

Therefore, we conclude that the default view of IDEA class file and source code is still different, recommend to view the source code when download sources

The final sending is through doSend method. Finally, the bottom layer is transmitted through the SMTPTransport of Sun Company. There is no analysis, and those who are interested can consult and study.

POP3, SMTP, IMAP differences

POP3 is short for Post Office Protocol 3, version 3 of the Post Office Protocol, which specifies how to connect a personal computer to a mail server on the Internet and an electronic Protocol for downloading E-mail. It was the first offline protocol standard for Internet E-mail. POP3 allowed users to store mail from a server to a local host (that is, their own computer) while deleting the mail stored on the mail server, which was a POP3 protocol receiving mail server used to receive E-mail.

SMTP is the Simple Mail Transfer Protocol. It is a set of specifications used to transport messages from source addresses to destination addresses to control how messages are forwarded. SMTP belongs to the TCP/IP protocol family, which helps each computer find the next destination when sending or forwarding a letter. The SMTP server is a mail sending server that complies with THE SMTP protocol.

IMAP stands for Internet Mail Access Protocol, or INTERACTIVE Mail Access Protocol. It is a standard Mail Access Protocol similar to POP3. However, after IMAP is enabled, the emails you receive from the email client are still stored on the server, and all the operations on the client are reported to the server, such as deleting emails or marking the emails as read. The emails on the server will also take corresponding actions. Therefore, whether you log in to the mailbox from the browser or the client software, the mail and status are the same.

Simply speaking, POP3 is to receive mail, SMTP is to send mail, so in the second part, we realize the need to configure SMTP server to send mail.