web-dev-qa-db-fra.com

Envoyer des e-mails avec Spring en utilisant Java annotations

Comment pourrais-je envoyer un e-mail avec Spring 4 (et Spring Boot) en utilisant une approche basée uniquement sur les annotations (selon le Java Configurations règles)?

17
vdenotaris

Une solution simple (où vous utiliserez un serveur SMTP sans authentification) pour configurer le service de messagerie serait

@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 doit pouvoir résoudre les propriétés email.Host et email.port de la manière habituelle (dans le cas de Spring Boot, le plus simple est de le mettre ensuite dans application.properties)

Dans n'importe quelle classe qui a besoin des services de JavaMailSender, injectez simplement l'une des méthodes habituelles (telles que @Autowired private JavaMailSender javaMailSender)


[~ # ~] mise à jour [~ # ~]

Notez que depuis la version 1.2.0.RC1 Spring Boot peut configurer automatiquement JavaMailSender pour vous. Consultez ceci une partie de la documentation. Comme vous pouvez le voir dans la documentation, presque aucune configuration n'est requise pour être opérationnel!

33
geoand

Dans pom.xml:

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

Dans application.properties:

spring.mail.Host=...
spring.mail.port=...

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

Personnellement, je recommande d'exécuter un MTA localhost et de l'utiliser pour relayer vers votre vrai MTA (comme Gmail ou SES, ou le vôtre). Cela vous donne une file d'attente asynchrone "gratuite" et centralise la configuration. J'aime OpenSMTP.

10
Neil McGuigan

Avec Spring-Boot, c'était presque trivial, avec un ajustement nécessaire pour le serveur de messagerie smtp.office365.com - c'est ce que cette société utilise.

Avant de procéder à cet ajustement, l'authentification auprès du serveur SMTP Office365 continuait d'échouer et nous obtenions une erreur du type:

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)

C'était même si nous définissions un nom d'utilisateur et un mot de passe qui étaient correctement récupérés par la classe MailProperties de Spring.

Il s'avère qu'Office365 a besoin de l'authentification TLS activée et l'implémentation JavaMail actuelle de Spring n'a pas un moyen simple de le faire avec les propriétés. La solution consiste à créer notre propre instance javax.mail.Session et à l'enregistrer auprès de Spring.

L'image entière suit.

Dans le fichier pom.xml, ajoutez:

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

Où spring-boot.version est défini ailleurs (dans un pom parent dans ce cas) et dans ce cas a une valeur de 1.3.1.

Assurez-vous d'inclure le package et/ou la classe de configuration automatique si vous les détaillez dans votre application principale - si vous utilisez la @SpringBootApplication par défaut, cela n'est pas nécessaire, mais dans mon cas, j'ai ajouté MailSenderAutoConfiguration.class aux annotations @Import liste des classes.

J'ai créé une classe EmailSender simple:

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

}

Où @ Slf4j est une annotation lombok, et @Subscribe est pour EventBus de Guava, c'est ainsi que cette application permet à l'EmailSender de savoir qu'il y a un message à envoyer. J'utilise un POJO EmailMessageEvent trivial qui a un sujet et un texte de message - assez bon pour les fins de cette application.

La classe MessageConfig facilite simplement la configuration des valeurs par défaut des messages avec le reste de la configuration de l'application, et elle contient le morceau de sauce spécial qui était nécessaire pour que cela fonctionne avec un serveur SMTP Office365, une session javax.mail.Session personnalisée. instance enregistrée en tant que 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 est à nouveau une annotation Lombok - ajoute automatiquement des méthodes de mutateur et d'accesseur, toString (), equals () & hashCode (), etc.

La limitation de JavaMailSender de Spring (JavaMailSenderImpl) est que sa session n'est pas directement configurable, en particulier il n'y a aucun moyen d'activer l'authentification TLS via une propriété. Cependant, si un bean javax.mail.Session est enregistré dans le contexte, il sera injecté (câblé de manière conditionnelle) dans MailSenderAutoConfiguration, puis utilisé pour construire l'instance JavaMailSenderImpl.

Nous enregistrons donc un tel Bean via la méthode getSession (). Pour faire bonne mesure, nous faisons de la session que nous construisons ici celle par défaut pour la JVM - changez-la pour appeler getInstance () si vous ne voulez pas ce comportement.

Après avoir ajouté cette session @Bean, tout a fonctionné.

7
sofend

À partir de Spring Boot 1.2, un JavaMailSender peut être configuré automatiquement pour vous. J'ai cette vidéo expliquant comment envoyer des mails avec Spring Boot 1.2. Spring Lemon le code source peut être consulté pour les détails exacts.

2
Sanjay

En plus de la réponse de geoand: si vous ne voulez pas coder en dur les propriétés du courrier ou écrire du XML, vous pouvez ajouter vos propriétés à un fichier (mail.properties par exemple) dans vos ressources, et ajouter ce type de code dans le Classe 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;
}

Et la gamme de propriétés que vous pouvez utiliser est tout définie sur ces pages

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

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

Mais vous devrez toujours définir l'hôte, le port, le nom d'utilisateur et le mot de passe à partir des méthodes de JavaMailSenderImpl car il n'utilisera pas directement ceux définis dans vos propriétés.

1
Geoffrey