如何使用纯基于注释的方法(根据Java 配置规则)发送带有Spring 4(和Spring Boot )的电子邮件?
5 回答
用于配置电子邮件服务的简单解决方案(您将使用没有身份验证的 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.host
(email.port
在 Spring Boot 的情况下,最简单的是将 then 放入 application.properties)
在任何需要 JavaMailSender 服务的类中,只需使用其中一种常用方式注入即可(例如@Autowired private JavaMailSender javaMailSender
)
更新
请注意,从 1.2.0.RC1 版本开始,Spring Boot 可以JavaMailSender
为您自动配置。查看文档的这一部分。从文档中可以看出,启动和运行几乎不需要任何配置!
在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。
使用 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 后,一切正常。
从 Spring Boot 1.2 开始,可以为您自动配置 JavaMailSender。我有这段视频,解释了如何使用 Spring Boot 1.2 及更高版本发送邮件。确切的细节可以参考Spring Lemon的源代码。
除了 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 的方法中设置主机、端口、用户名和密码,因为它不会直接使用您在属性中设置的方法。