18

如何使用纯基于注释的方法(根据Java 配置规则)发送带有Spring 4(和Spring Boot )的电子邮件?

4

5 回答 5

35

用于配置电子邮件服务的简单解决方案(您将使用没有身份验证的 SMTP 服务器)将通过

@Configuration 
public class MailConfig {

    @Value("${email.host}")
    private String host;

    @Value("${email.port}")
    private Integer port;

    @Bean
    public JavaMailSender javaMailService() {
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();

        javaMailSender.setHost(host);
        javaMailSender.setPort(port);

        javaMailSender.setJavaMailProperties(getMailProperties());

        return javaMailSender;
    }

    private Properties getMailProperties() {
        Properties properties = new Properties();
        properties.setProperty("mail.transport.protocol", "smtp");
        properties.setProperty("mail.smtp.auth", "false");
        properties.setProperty("mail.smtp.starttls.enable", "false");
        properties.setProperty("mail.debug", "false");
        return properties;
    }
}

Spring 必须能够以通常的方式解析属性email.hostemail.port在 Spring Boot 的情况下,最简单的是将 then 放入 application.properties)

在任何需要 JavaMailSender 服务的类中,只需使用其中一种常用方式注入即可(例如@Autowired private JavaMailSender javaMailSender


更新

请注意,从 1.2.0.RC1 版本开始,Spring Boot 可以JavaMailSender为您自动配置。查看文档的这一部分。从文档中可以看出,启动和运行几乎不需要任何配置!

于 2014-03-18T15:40:38.750 回答
10

pom.xml

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

application.properties

spring.mail.host=...
spring.mail.port=...

Foo.java

@Component
public class Foo {

    @Autowired
    private JavaMailSender mailSender;

    public void send() {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("foo@example.com");
        message.setTo("bar@example.com");
        message.setSubject("hello");
        mailSender.send(message);
    }
}

就个人而言,我建议运行 localhost MTA,并使用它来中继到您的真实 MTA(如 Gmail 或 SES,或您自己的)。这为您提供了一个“免费”的异步队列,并集中了配置。我喜欢 OpenSMTP。

于 2015-12-30T00:44:28.287 回答
7

使用 Spring-Boot,这几乎是微不足道的,只需对 smtp.office365.com 邮件服务器进行一次调整——这就是这家公司正在使用的。

在进行此调整之前,对 Office365 SMTP 服务器的身份验证一直失败,我们会收到如下错误:

org.springframework.mail.MailSendException: Failed messages: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.57 SMTP; Client was not authenticated to send anonymous mail during MAIL FROM
    at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:474)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:307)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:296)

即使我们设置了 Spring 的 MailProperties 类正确获取的用户名和密码,也是如此。

事实证明 Office365 需要启用 TLS 身份验证,而 Spring 当前的 JavaMail 实现没有简单的方法来使用属性来做到这一点。解决方案是创建我们自己的 javax.mail.Session 实例并将其注册到 Spring。

整个画面如下。

在 pom.xml 文件中,添加:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
        <version>${spring-boot.version}</version>
    </dependency>

spring-boot.version 在其他地方定义(在本例中为父 pom),在本例中值为 1.3.1.RELEASE

如果您在主应用程序中逐项列出包和/或自动配置类,请确保包括包和/或自动配置类 - 如果您使用默认的 @SpringBootApplication 这不是必需的,但在我的情况下,我将 MailSenderAutoConfiguration.class 添加到 @Import 注释的类列表。

我创建了一个简单的 EmailSender 类:

@SuppressWarnings("SpringJavaAutowiringInspection")
@Slf4j
@Service
@ConditionalOnClass(JavaMailSender.class)
public class EmailSender {

    @Autowired
    private EventBus mmEventBus;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private MessageConfig messageConfig;

    @Subscribe
    public void sendEmail(EmailMessageEvent eme) {
        log.info("{}", eme);

        SimpleMailMessage msg = new SimpleMailMessage(messageConfig.getMessageTemplate());
        msg.setSubject(eme.getSubject());
        msg.setText(eme.getMessage());

        try {
            mailSender.send(msg);
        } catch (MailException ex) {
            log.error("Error sending mail message: " + eme.toString(), ex);
        }
    }

    @PostConstruct
    public void start() throws MessagingException {
        mmEventBus.register(this);
    }

}

@Slf4j 是一个 lombok 注释,@Subscribe 是用于 Guava 的 EventBus,这就是这个应用程序让 EmailSender 知道有消息要发送的方式。我使用了一个简单的 EmailMessageEvent POJO,它有一个主题和消息文本——对于这个应用程序的目的来说已经足够好了。

MessageConfig 类只是使设置消息默认值以及应用程序的其余配置变得容易,并且它具有使此工作与 Office365 SMTP 服务器一起使用所需的一种特殊调味料,即自定义 javax.mail.Session注册为 Spring Bean 的实例:

@Data
@Component
@ConfigurationProperties(prefix = "spring.message", ignoreUnknownFields = false)
@Slf4j
public class MessageConfig {

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired
    private MailProperties mailProperties;

    private String from;
    private String subject;
    private String[] recipients;

    private SimpleMailMessage messageTemplate;

    public void setRecipients(String... r) {
        this.recipients = r;
    }

    @PostConstruct
    public void createTemplate() {
        messageTemplate = new SimpleMailMessage();
        messageTemplate.setFrom(from);
        messageTemplate.setSubject(subject);
        messageTemplate.setTo(recipients);

        log.debug("Email Message Template defaults: {}", messageTemplate);
    }

    @Bean
    public SimpleMailMessage getMessageTemplate() {
        return messageTemplate;
    }

    @Bean
    public Session getSession() {
        log.debug("Creating javax.mail.Session with TLS enabled.");
        // We could be more flexible and have auth based on whether there's a username and starttls based on a property.
        Properties p = new Properties();
        p.setProperty("mail.smtp.auth", "true");
        p.setProperty("mail.smtp.starttls.enable", "true");
        p.setProperty("mail.smtp.host", mailProperties.getHost());
        p.setProperty("mail.smtp.port", mailProperties.getPort().toString());
        return Session.getDefaultInstance(p, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(mailProperties.getUsername(), mailProperties.getPassword());
            }
        });
    }

}

@Data 又是一个 Lombok 注释 - 自动添加 mutator 和 accessor 方法,toString()、equals() 和 hashCode() 等。

Spring 的 JavaMailSender (JavaMailSenderImpl) 的限制是它的 Session 不能直接配置,特别是无法通过属性启用 TLS 身份验证。但是,如果在上下文中注册了一个 javax.mail.Session Bean,它将被注入(有条件地自动装配)到 MailSenderAutoConfiguration 中,然后用于构造 JavaMailSenderImpl 实例。

所以我们通过 getSession() 方法注册了这样一个 Bean。为了更好地衡量,我们将我们在此处构造的 Session 设置为 JVM 的默认值 - 如果您不想要这种行为,请将其更改为调用 getInstance()。

添加此 Session @Bean 后,一切正常。

于 2016-01-07T22:24:27.760 回答
2

从 Spring Boot 1.2 开始,可以为您自动配置 JavaMailSender。我有这段视频,解释了如何使用 Spring Boot 1.2 及更高版本发送邮件。确切的细节可以参考Spring Lemon的源代码。

于 2015-08-06T07:47:42.063 回答
1

除了 geoand 的回答:如果您不想对邮件属性进行硬编码或编写 XML,您可以将属性添加到资源中的文件(例如 mail.properties)中,并将此类代码添加到邮件配置类:

@Resource(name = "mailProperties")
private Properties mailProperties;

@Bean(name = "mailProperties")
public PropertiesFactoryBean mapper() {
    PropertiesFactoryBean bean = new PropertiesFactoryBean();
    bean.setLocation(new ClassPathResource("mail.properties"));
    return bean;
}

您可以使用的属性范围都在这些页面上定义

https://javamail.java.net/nonav/docs/api/

https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html

但是您仍然必须从 JavaMailSenderImpl 的方法中设置主机、端口、用户名和密码,因为它不会直接使用您在属性中设置的方法。

于 2015-05-23T00:32:29.713 回答