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.