Skip to content
Commencez avec 100 crédits de vérification gratuits
Qualisend
Tous les articles
Ingénierie / 14 juin 2026

Comment valider une adresse e-mail en Java

7 minutes read

Qualisend team
Une fenêtre de code Java validant un e-mail à travers les couches syntaxe, DNS et SMTP jusqu'à un verdict de délivrabilité

Valider une adresse e-mail en Java, c'est en réalité trois contrôles sous un seul nom. La plupart des tutoriels dégainent une regex, confirment que la chaîne a la forme d'une adresse, et s'arrêtent là — mais la forme est la moins utile des trois choses que vous voulez vraiment savoir. La vraie validation est en couches : un contrôle de format bon marché, une recherche DNS de la route de messagerie du domaine, et une sonde de boîte aux lettres par SMTP. La bibliothèque standard de Java et Jakarta Mail couvrent proprement les deux premières ; la troisième est un problème réseau qu'il vaut mieux déléguer. Ce guide construit chaque couche avec du code fonctionnel, et montre exactement où chacune s'arrête.

La réponse courte#

Utilisez jakarta.mail.internet.InternetAddress (ou Apache Commons Validator) pour la syntaxe, le fournisseur DNS JNDI intégré au JDK pour la recherche MX, et une API de vérification pour le contrôle de boîte aux lettres par SMTP — le moins cher d'abord, en court-circuitant dès que l'un est décisif. N'ouvrez pas de connexions SMTP depuis votre application pour sonder les boîtes aux lettres vous-même : le port 25 sortant est bloqué sur la plupart des hébergeurs, et la réponse dépend de la réputation de l'IP d'envoi et du greylisting que vous ne voulez pas réimplémenter. Chaque couche écarte des adresses à moindre coût que la précédente ; seule la dernière peut retenir une adresse comme valide.

Couche 1 : la syntaxe avec Jakarta Mail#

Jakarta Mail — la bibliothèque renommée depuis javax.mail lors de son passage dans l'espace de noms Jakarta EE — embarque un analyseur d'adresses sur lequel vous pouvez vous appuyer plutôt que de bricoler une regex. Construisez un InternetAddress et appelez validate() : le constructeur analyse l'adresse, et validate() fait respecter la syntaxe RFC 822, en levant AddressException sur tout ce qui est mal formé.

import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;

public static boolean isValidSyntax(String email) {
    if (email == null || email.length() > 320) {
        return false;
    }
    try {
        InternetAddress address = new InternetAddress(email);
        address.validate();
        return true;
    } catch (AddressException e) {
        return false;
    }
}

Une chose à savoir : validate() est permissif quant au domaine. Il accepte jane@localhost et jane@example — pas de point requis — parce que la RFC 822 autorise les domaines sans point. C'est très bien ici. La couche 1 est un filtre de format, pas un contrôle de délivrabilité, et c'est la recherche DNS de la couche 2 qui décide réellement si le domaine peut recevoir du courrier. Gardez ce contrôle permissif et laissez le DNS faire l'élimination ; courir après la perfection du format avec une regex plus grosse est de toute façon une partie perdue d'avance.

Si vous êtes sur un ancien jar javax.mail, le code est identique au nom du paquet près. Et si vous dépendez déjà d'Apache Commons Validator, son EmailValidator est une bonne alternative de couche 1 — il est un brin plus strict (il rejette les domaines sans point par défaut) :

import org.apache.commons.validator.routines.EmailValidator;

boolean valid = EmailValidator.getInstance().isValid(email);

Préférez Jakarta Mail si vous l'utilisez déjà pour envoyer du courrier — une dépendance de moins. Dans tous les cas, chaque bibliothèque ne répond jamais qu'à la question de la syntaxe.

Couche 2 : le domaine peut-il recevoir du courrier ?#

Un domaine sans enregistrement MX ne peut accepter de courrier pour personne, si bien qu'une seule recherche MX élimine les domaines morts, les noms d'entreprise mal orthographiés et les TLD inventés. Java le fait sans aucune dépendance externe : le JDK embarque un fournisseur de service DNS pour JNDI, vous résolvez donc l'attribut MX à travers un DirContext.

import java.util.Hashtable;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public static boolean hasMailRoute(String domain) {
    Hashtable<String, String> env = new Hashtable<>();
    env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
    env.put("com.sun.jndi.dns.timeout.initial", "2000"); // don't hang on a slow resolver
    env.put("com.sun.jndi.dns.timeout.retries", "1");

    DirContext ctx = null;
    try {
        ctx = new InitialDirContext(env);
        Attributes attrs = ctx.getAttributes(domain, new String[] { "MX" });
        Attribute mx = attrs.get("MX");
        return mx != null && mx.size() > 0;
    } catch (NamingException e) {
        // NameNotFoundException → domain doesn't exist; no MX attribute → no mail route
        return false;
    } finally {
        if (ctx != null) {
            try { ctx.close(); } catch (NamingException ignored) { }
        }
    }
}

Séparez le domaine de l'adresse au dernier @ pour que les parties locales entre guillemets ne vous fassent pas trébucher :

String domain = email.substring(email.lastIndexOf('@') + 1);

hasMailRoute("gmail.com");               // true
hasMailRoute("company-that-folded.com"); // false

Un attribut MX absent ou vide signifie que le domaine ne publie aucune route de messagerie ; une NameNotFoundException (une sous-classe de NamingException) signifie qu'il ne se résout pas du tout. Certains domaines acceptent le courrier sur un enregistrement A avec un MX implicite — si vous voulez honorer ce cas particulier, repliez-vous sur une interrogation de "A" lorsque l'ensemble MX revient vide. Pour l'immense majorité des adresses réelles, un contrôle MX est le bon filtre. Comme la recherche bloque sur un résolveur DNS, conservez les délais d'attente ci-dessus dans un parcours d'inscription et traitez un échec transitoire comme « à revérifier plus tard », et non comme un rejet ferme — un hoquet DNS ne devrait pas faire fuir un vrai client.

Couche 3 : la boîte aux lettres existe-t-elle vraiment ?#

Les couches 1 et 2 ne peuvent qu'écarter une adresse. Un domaine peut publier des enregistrements MX parfaits et n'avoir malgré tout aucune boîte aux lettres à l'adresse que vous détenez — noreply-9f2x@gmail.com est syntaxiquement valide sur un domaine doté d'une route de messagerie active, et c'est pourtant une boîte aux lettres qui n'a jamais été créée. Confirmer qu'une boîte aux lettres spécifique existe implique la conversation de livraison SMTP : se connecter à l'hôte de messagerie, émettre RCPT TO, lire la réponse, et se déconnecter avant d'envoyer quoi que ce soit. Il y a plus subtil encore — les domaines catch-all acceptent chaque adresse et déjouent une sonde naïve — le fonctionnement de la vérification d'e-mail parcourt le pipeline complet.

Vous pouvez scripter SMTP en Java avec un Socket brut, mais vous ne devriez pas l'exécuter depuis votre application. La plupart des fournisseurs cloud bloquent le port 25 sortant, la réponse dépend de la réputation de l'IP depuis laquelle vous vous connectez, et les serveurs récepteurs mettent en greylist et limitent le débit des expéditeurs inconnus — de sorte qu'une sonde qui passe dans un test local échoue silencieusement, ou vous fait blacklister, en production. C'est la couche qu'il vaut la peine de déléguer.

Le POST /verify de Qualisend exécute tout le pipeline — syntaxe, DNS et sonde de boîte aux lettres par SMTP — depuis une infrastructure à réputation gérée conçue pour cela, et renvoie un verdict. Java 11+ embarque java.net.http.HttpClient, vous n'avez donc besoin d'aucune dépendance HTTP ; associez-le à Jackson (ou Gson) pour lire le JSON. Gardez votre clé dans une variable d'environnement et ne la codez jamais en dur :

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class QualisendClient {
    // Placeholder endpoint — check /developers for the current base URL and shape.
    private static final String ENDPOINT = "https://api.qualisend.com/v1/verify";

    private final HttpClient http = HttpClient.newHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();

    public JsonNode verify(String email) throws IOException, InterruptedException {
        String apiKey = System.getenv("QUALISEND_API_KEY"); // holds YOUR_API_KEY
        String payload = mapper.writeValueAsString(Map.of("email", email));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(ENDPOINT))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "application/json")
            .timeout(Duration.ofSeconds(10))
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response =
            http.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IOException("Qualisend responded " + response.statusCode());
        }
        // The verdict lives inside a `result` envelope.
        return mapper.readTree(response.body()).get("result");
    }
}

Lisez les champs qui vous intéressent sur le nœud result :

JsonNode result = client.verify("jane@example.com");

String  status     = result.get("status").asText();  // deliverable | risky | undeliverable | unknown
int     score      = result.path("score").asInt();    // 0–100 confidence
String  reason     = result.hasNonNull("reason")
                         ? result.get("reason").asText()  // machine-readable reason, may be null
                         : null;
boolean disposable = result.path("sub_flags").path("disposable").asBoolean();

La forme exacte de la réponse — chaque clé sub_flags et chaque code reason — figure dans la référence de l'API. Pour la validation d'inscription en direct, le status suffit généralement pour agir : rejetez undeliverable, traitez risky et unknown selon votre politique, et signalez les adresses disposable dès la saisie.

Assembler les couches pour valider une adresse e-mail en Java#

Le moins cher d'abord, arrêtez-vous dès que vous avez une réponse. Un petit record porte le verdict (les records sont disponibles à partir de Java 16 ; sur 11–15, utilisez une classe ordinaire) :

public record Verdict(String status, String reason) {}

public Verdict validate(String email) throws IOException, InterruptedException {
    if (!isValidSyntax(email)) {
        return new Verdict("undeliverable", "invalid_email");
    }
    String domain = email.substring(email.lastIndexOf('@') + 1);
    if (!hasMailRoute(domain)) {
        return new Verdict("undeliverable", "invalid_domain");
    }
    JsonNode result = client.verify(email); // client is a QualisendClient
    return new Verdict(
        result.get("status").asText(),
        result.hasNonNull("reason") ? result.get("reason").asText() : null);
}

Les deux couches locales ne coûtent rien et attrapent l'essentiel des déchets instantanément ; l'API ne s'exécute que sur les adresses qui valent l'aller-retour réseau. Cet ordonnancement est toute l'astuce — la même structure que vous retrouverez dans les versions Node.js, Python et PHP de ce guide, parce que c'est le découpage en couches, et non le langage, qui fait fonctionner la validation.

Foire aux questions#

InternetAddress.validate() suffit-il pour valider un e-mail en Java ?#

Pour la syntaxe, c'est le bon outil de couche 1 — jakarta.mail.internet.InternetAddress avec validate() est mieux éprouvé qu'une regex maison et vous épargne de réimplémenter la RFC 822. Mais il ne valide que la forme : il ne résout jamais le DNS ni ne contacte de serveur de messagerie, et il est assez permissif pour accepter des domaines sans point comme jane@localhost. Associez-le à une recherche MX et à une vérification de boîte aux lettres par SMTP avant de faire confiance à l'adresse.

Comment vérifier les enregistrements MX en Java sans bibliothèque externe ?#

Utilisez le fournisseur DNS JNDI intégré au JDK. Créez un InitialDirContext avec java.naming.factory.initial défini sur com.sun.jndi.dns.DnsContextFactory, puis appelez getAttributes(domain, new String[] {"MX"}) et vérifiez l'attribut MX retourné. Cela ne nécessite aucune dépendance tierce et indique proprement si un domaine publie une route de messagerie. Interceptez NamingException et considérez un MX absent comme non délivrable.

Puis-je vérifier une boîte aux lettres en Java sans API ?#

En partie. Jakarta Mail et JNDI confirment que l'adresse est bien formée et que le domaine accepte le courrier — le tout gratuitement et avec peu de dépendances. Confirmer la boîte aux lettres elle-même implique une conversation SMTP, que vous pouvez tenter avec un Socket brut mais ne devriez pas exécuter depuis votre serveur applicatif : le port 25 est largement bloqué et le résultat dépend de la réputation de votre IP d'envoi. C'est cette dernière couche que gère un service de vérification.

Faut-il valider les e-mails à l'inscription ou lors du nettoyage d'une liste ?#

Les deux, à des profondeurs différentes. Exécutez les couches syntaxe et MX de façon synchrone à l'inscription — elles sont assez rapides pour bloquer la requête et donner un retour instantané — et agissez aussi sur le status de l'API à cet endroit. Réservez le travail par lots plus poussé au nettoyage de liste ; le guide sur l'inscription serverless montre le motif en temps réel, et le guide sur le nettoyage de liste couvre le volet par lots.


Prêt à ajouter la couche SMTP ? Passez n'importe quelle adresse dans le vérificateur d'e-mails gratuit pour voir une chaîne syntaxiquement parfaite revenir avec un vrai verdict, ou lisez la référence de l'API pour l'endpoint /verify, l'enveloppe result complète, et des exemples prêts à copier-coller dans tous les langages ci-dessus.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started