依存を jakarta.mail:jakarta.mail-api に切り替えて版を上げたら、コンパイルが次のエラーで止まることがあります。
[ERROR] /app/src/main/java/App.java:[1,20] package javax.mail does not exist
コードは変えていません。変わったのは、依存が提供する package の名前です。javax.mail 配下の別のクラスを
import していれば、その package 名で同じ形のエラーになります。
直し方の要点を先に
import を jakarta.mail.* に書き換えます。依存の版は 2.x のまま上げておきます。
// 旧:jakarta.mail-api 1.6 系が提供していたのは javax.mail
// import javax.mail.Session;
// 新:2.0.0 以降が提供するのは jakarta.mail
import jakarta.mail.Session;
jakarta.mail-api を 1.6 系に下げると、コンパイルは通ります(実測:1.6.7 で終了コード 0)。
ただしそれは Jakarta EE 8 世代へ 1 つ戻す選択で、Jakarta EE 9 以降へ移る作業ではありません。
Jakarta EE 9+ の世代(Spring Boot 3、Tomcat 10 など)へ進むのが目的なら、2.x のまま import を進めます。
逆に、周辺のライブラリが javax.* 世代のままで、当面それを維持するのなら、1.6 系が整合する選択になります。
どちらを選ぶかは、周辺が要求している名前空間で決まります。
なぜ 2.0.0 以降で出るのか:Maven 座標が先に改名され、package は後から動いた
境界は2つあり、別の版で起きています。
- Maven 座標の移行:
com.sun.mail:javax.mail→jakarta.mail:jakarta.mail-api(1.6.3 から)。 groupId も artifactId もjakartaを名乗るようになります。 - Java package の移行:
javax.mail→jakarta.mail(2.0.0 から)。
この2つは同時に起きていません。 座標が jakarta を名乗り始めた 1.6.3 の時点でも、その jar が提供するクラスは
まだ javax.mail パッケージに入っています。名前が先に変わり、中身は 2.0.0 で動きました。
同じ import javax.mail.Session; だけのソースを、依存の座標と版だけ変えてコンパイルした観測点です。
| 依存 | 結果 |
|---|---|
com.sun.mail:javax.mail:1.6.2(旧座標) | 成功(終了コード 0) |
jakarta.mail:jakarta.mail-api:1.6.7 | 成功(終了コード 0) |
jakarta.mail:jakarta.mail-api:2.0.1 | package javax.mail does not exist(終了コード 1) |
jakarta.mail:jakarta.mail-api:2.1.3 | package javax.mail does not exist(終了コード 1) |
ソースは4回とも同一で、変えたのは依存の座標だけです。この4点は観測点で、package が動く境界そのものは 2.0.0 です
(jakarta.mail-api の公開版は 1.6.3〜1.6.8 と 2.0.0 以降で、1.6 系はすべて javax.mail、2.0.0 以降が jakarta.mail を
提供します。検証環境の節を参照)。
この2段構えが、次の形で表面化します。
- 座標を替えた時点では通り、あとで版を上げたときに落ちる。
com.sun.mail:javax.mailからjakarta.mail:jakarta.mail-apiの 1.6 系へ替える変更は、import を1行も触らずに通ります。座標はjakartaに なったので移行が済んだように見えますが、package はまだjavax.mailです。落ちるのは、その次に 2.x へ上げたときです。 - 依存の名前で移行状況を判断すると外れる。 依存ツリーに
jakarta.mail:jakarta.mail-apiがあることは、jakarta.mailパッケージを使えることを意味しません。版まで見る必要があります。
旧座標 com.sun.mail:javax.mail:1.6.2 でも通るのは、こちらも中身が javax.mail だからです。
「座標が jakarta を名乗っているか」ではなく「その版の中身がどちらの名前空間か」だけが結果を決めます。
既存の JAXB のエラーとは別物です
javax.* が見つからないエラーは JDK 11 の JAXB 削除でも出ますが、別の機構です。混同すると直し方を間違えます。
| NoClassDefFoundError: javax/xml/bind/JAXBException | この記事 | |
|---|---|---|
| 何が変わったか | JDK が JAXB の同梱をやめた(Java 11 / JEP 320) | ライブラリが package 名を改名した(Jakarta EE 9) |
| いつ落ちるか | 実行時(NoClassDefFoundError) | コンパイル時(package ... does not exist) |
| 直し方 | 依存を足す | import を書き換える |
javax.mail を提供する古い jar を足せば、コンパイル自体は通ります。ただしそれは 1.6 系に下げるのと同じで、
Jakarta EE 9 へ移る作業にはなりません。
「Maven 座標が先に改名される」という仕組み自体は、JAXB でも同じように起きています
(jakarta.xml.bind-api は 2.3.x の中身が javax.xml.bind、3.x 以降が jakarta.xml.bind)。
それでも勧めている向きが逆に見えるのは、選ぶ基準が「周辺が要求している名前空間」だからです。
- JAXB の記事が 2.3.x を勧めるのは、周辺が
javax世代のままだから。 JDK から消えた API を戻すのが目的で、import javax.xml.bind.*のコードを動かしたい。だからjavaxを含む版を選びます。 - こちらで 2.x のまま import を進めるのは、周辺を Jakarta EE 9+ へ進める前提だから。 周辺が
javax世代なら、 上に書いたとおり 1.6 系が整合します。
つまりどちらも「周辺が要求する名前空間に、その版の中身を合わせる」という同じ基準で選んでいます。 向きが違って見えるのは、周辺の世代が違うからです。
切り分け(うまくいかないとき)
package javax.persistence does not existが出ている:JPA 側の同じ世代ずれです。 package javax.persistence does not exist の直し方(Spring Boot 3) を参照してください。- コンパイルは通るのに、実行時に
NoClassDefFoundError: javax/servlet/...が出る:自分のコードではなく、 依存しているライブラリが古い名前空間を要求しています。 NoClassDefFoundError: javax/servlet/Filter の直し方 を参照してください。 javax.mail配下の別の package でも同じエラーが出る:javax.mail.internetなどの import が残っていないか 確認します。名前空間は配下ごと動くので、javax.mail.internet.MimeMessageはjakarta.mail.internet.MimeMessageになります(Jakarta EE 9 の名前空間変更に沿った仕様で、 この記事の実測はjavax.mail.Sessionの1点です)。- 依存ツリーに両方の世代が入っている:
mvn dependency:treeでjakarta.mail:jakarta.mail-apiとcom.sun.mail:javax.mailが同居していないかを見ます。同居が問題になるのは、両方がjavax.mailを提供するとき (=新座標の 1.6 系と旧座標の組み合わせ)で、このとき同じ完全修飾名が2つの jar から供給されます。 2.x と旧座標の組み合わせでは、提供する package がjakarta.mailとjavax.mailで別々なので、 どちらかがもう一方を隠すことはありません(この場合の問題は、2つの世代を同時に抱えている構成そのものです)。
検証環境
eclipse-temurin:17-jdkの中で Maven を実行(依存の取得にのみネットワークを使用)- 再現:
import javax.mail.Session;のみのクラスをjakarta.mail:jakarta.mail-api:2.0.1でコンパイルすると、package javax.mail does not existで終了コード 1 - 修正:import を
jakarta.mail.Sessionに変更すると、同じ依存(2.0.1)のまま終了コード 0・シグネチャ消滅
再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は import 文の1行だけで、
pom.xml の依存の版は同一です。コンパイル結果(旧座標 1.6.2 と新座標 1.6.7 で通り、2.0.1 と 2.1.3 で落ちる)は、
同一ソースを依存の座標だけ変えて実測しました。
package が動く版を確定させるため、jar の中身も直接確認しました——jakarta.mail:jakarta.mail-api の
1.6.3(新座標の最初の版)と 1.6.8(1.6 系の最後の版)は javax/mail/ のエントリだけを含み
(javax/mail/Session.class を含む)、2.0.0 は jakarta/mail/ のエントリだけを含んで javax/mail/ を
1つも含みません。したがって package の境界は 2.0.0 です。座標の境界(com.sun.mail:javax.mail →
jakarta.mail:jakarta.mail-api)は 1.6.3 で、この2つは別の版で起きています(各結果はケースの
verification/probes.txt に記録)。package が javax.mail から jakarta.mail へ移ったこと自体は、
Jakarta EE 9 以降の名前空間変更に沿っています。