que - Envía correos electrónicos con Spring usando las anotaciones de Java
que hace autowired spring (5)
Además de la respuesta de Geoand: si no desea codificar las propiedades de correo o escribir XML, puede agregar sus propiedades a un archivo (mail.properties, por ejemplo) en sus recursos, y agregar este tipo de código en el Clase MailConfig:
@Resource(name = "mailProperties")
private Properties mailProperties;
@Bean(name = "mailProperties")
public PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("mail.properties"));
return bean;
}
Y el rango de propiedades que puede usar está definido en estas páginas
https://javamail.java.net/nonav/docs/api/
https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html
Pero aún tendrá que configurar Host, Puerto, Nombre de usuario y Contraseña desde los métodos de JavaMailSenderImpl, ya que no usará directamente los que están establecidos en sus propiedades.
¿Cómo podría enviar un correo electrónico con Spring 4 (y Spring Boot ) utilizando un enfoque basado en anotaciones puras (de acuerdo con las reglas de Configuraciones de Java )?
Con Spring-Boot fue casi trivial, con un ajuste necesario para el servidor de correo smtp.office365.com, que es lo que esta compañía está usando.
Antes de realizar este ajuste, la autenticación en el servidor SMTP de Office365 seguía fallando y recibíamos un error en las siguientes líneas:
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)
Esto fue a pesar de que estábamos configurando un nombre de usuario y una contraseña que fueron correctamente recogidos por la clase MailProperties de Spring.
Resulta que Office365 necesita la autenticación TLS habilitada y la implementación actual de JavaMail de Spring no tiene una forma sencilla de hacerlo con las propiedades. La solución es crear nuestra propia instancia javax.mail.Session y registrarla con Spring.
La imagen completa sigue.
En el archivo pom.xml, agregue:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>${spring-boot.version}</version>
</dependency>
Donde spring-boot.version se define en otra parte (en un pom principal en este caso) y en este caso tiene un valor de 1.3.1.RELEASE
Asegúrese de incluir el paquete y / o la clase de configuración automática si los detalla en su aplicación principal; si usa la aplicación @SpringBootAplation predeterminada, esto no es necesario, pero en mi caso agregué MailSenderAutoConfiguration.class a las anotaciones de @Import lista de clases
Creé una clase simple de 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);
}
}
Donde @ Slf4j es una anotación lombok, y @Subscribe es para EventBus de Guava, que es la forma en que esta aplicación le permite al EmailSender saber que hay un mensaje para enviar. Uso un correo electrónico trivial EmailMessageEvent POJO que tiene un asunto y un texto de mensaje, lo suficientemente bueno para los propósitos de esta aplicación.
La clase MessageConfig solo facilita la configuración de los valores predeterminados de los mensajes junto con el resto de la configuración de la aplicación, y tiene la única pieza de salsa especial que se necesitaba para hacer que esto funcionara con un servidor Office365 SMTP, un javax.mail.Session personalizado instancia registrada como 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());
}
});
}
}
La @Data es de nuevo una anotación de Lombok: agrega automáticamente métodos mutadores y de acceso, toString (), equals () y hashCode (), etc.
La limitación de JavaMailSender (JavaMailSenderImpl) de Spring es que su sesión no se puede configurar directamente, en particular, no hay manera de habilitar la autenticación TLS a través de una propiedad. Sin embargo, si hay un javax.mail.Session Bean registrado en el contexto, se inyectará (condicionará automáticamente) en MailSenderAutoConfiguration y luego se usará para construir la instancia de JavaMailSenderImpl.
Así que registramos tal Bean a través del método getSession (). Como medida de valor, hacemos que la sesión que construimos sea la predeterminada para la JVM; cámbiela para llamar a getInstance () si no desea ese comportamiento.
Después de agregar esta sesión, @Bean todo funcionó.
En pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
En application.properties
:
spring.mail.host=...
spring.mail.port=...
En Foo.java
:
@Component
public class Foo {
@Autowired
private JavaMailSender mailSender;
public void send() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("[email protected]");
message.setTo("[email protected]");
message.setSubject("hello");
mailSender.send(message);
}
}
Personalmente, recomiendo ejecutar un MTA localhost y usarlo para retransmitir a su MTA real (como Gmail o SES, o el suyo propio). Esto le da una cola asíncrona "libre" y centraliza la configuración. Me gusta el OpenSMTP.
Una solución simple (donde utilizará un servidor SMTP sin autenticación) para configurar el servicio de correo electrónico sería
@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 debe poder resolver las propiedades email.host
y email.port
de las formas habituales (en el caso de Spring Boot, la más sencilla es colocarla en application.properties).
En cualquier clase que necesite los servicios de JavaMailSender, simplemente inyecte con una de las formas habituales (como @Autowired private JavaMailSender javaMailSender
)
ACTUALIZAR
Tenga en cuenta que desde la versión 1.2.0.RC1 Spring Boot puede configurar automáticamente JavaMailSender
por usted. Echa un vistazo a this parte de la documentación. Como puede ver en la documentación, ¡casi no se requiere configuración para ponerse en marcha!
Usando Spring Boot 1.2 en adelante, un JavaMailSender podría configurarse automáticamente para usted. Tengo este video que explica exactamente cómo enviar correos electrónicos a partir de Spring Boot 1.2. El código fuente de Spring Lemon se puede consultar para obtener los detalles exactos.