Jakarta EE 就是 Java EE 的新名词。这里的 EE 全称是 Enterprise Edition,它是专门为企业级 Java 应用定义的一套规范,与 Java SE (Java Platform, Standard Edition) 相对应。
Java EE 是从 Java 1.2 版本开始推出的 Java 企业级开发平台,最初的名称是 J2EE (Java 2 Platform, Enterprise Edition)。随着 Java 的发展,它的名称于 Java 1.5 版本时更改为 Java EE (Java Platform, Enterprise Edition)。2009 年 Oracle 收购了 Sun,Java EE 开始由 Oracle 通过 JCP (Java Community Process) 开发和维护。
直到 2017 年,Oracle 将 Java EE 提交给了 Eclipse 基金会,并命名为 Eclipse Enterprise for Java。然而,由于"Java"这个名字的商标归 Oracle 所有,Eclipse 基金会无法继续使用 javax.* 和 java.*,因此,项目名称改为 Jakarta EE。
Jakarta EE 包含了许多技术规范和 API,涵盖了 Web 应用、数据库访问、消息传递、事务处理、安全性等方面的功能,其中包括但不限于下列规范:
Jakarta Servlet:前身是 J2EE Servlet,定义了如何管理 HTTP 请求的规范。这应该是大部分 Java Web 开发者最熟悉的,同时也是许多其它规范的基础。
Jakarta Server Page (JSP):服务端动态生成网页的技术,可以看作 Java 版本的 PHP 和 ASP。
Jakarta Websocket:定义了一套 WebSocket 连接相关的 API,用于实现全双工通信。
Jakarta RESTful Web Services:开发符合 REST 原则的 Web 服务的一套规范。
Jakarta JSON Binding:Java 类和 JSON 字符串互相转换的规范。
Jakarta XML Binding:Java 类和 XML 的映射规范。
Jakarta Enterprise Beans (EJB):这个规范比较复杂,包括 EJB 容器,RMI(远程过程调用),并发控制,依赖注入等。
Jakarta Persistent (JPA):ORM 规范,定义了 Java 类和数据库表直接的映射规范。
Jakarta Transactions (JTA):包含了事务相关的接口和注解类,也用于管理分布式事务。
Jakarta Messaging (JMS):消息系统的规范,用于实现异步消息传递,比如 Apache 的 ActiveMQ 就实现了这套规范。
比如常见的 servlet-api:
<!-- 老版本 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 新版本 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>比如:
// 老版本
import javax.servlet.Filter;
// 新版本
import jakarta.servlet.Filter;如果不兼容会发生什么,比如:
javax.servlet.ServletRequest can not cast to jakarta.servlet.ServletRequest比如下面定义是 javax.persistence.Table,开源或者自定义的数据库中间件件一般习惯按注解 jakarta.persistence.Table 进行扫描:
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "mt_field_extra")
public class FieldExtra {
@Id
String id;
}private static void scanTableName(Class<?> clazz, PersistMeta meta, boolean mapCamelCaseToUnderscore) {
jakarta.persistence.Table table = clazz.getAnnotation(jakarta.persistence.Table.class);
String tableName;
if (null != table) {
tableName = table.name();
} else {
tableName = nameConvert(clazz.getSimpleName(), mapCamelCaseToUnderscore);
}
}如果项目能直接升级到最新版本当然更好,但是作为中间件团队提供的组件,往往需要在过渡期间提供一种兼容两种模式的方案,既能让原有老服务正常使用,又兼容 Spring Boot 3+ Jakarta EE 9+新服务。
Jakarta EE 兼容是一个复杂的课题,当前只记录 package namespace 差异解决方法。
假设父 POM 使用 dependencyManagement 定义如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta-java21</classifier>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta-java23</classifier>
</dependency>
</dependencies>
</dependencyManagement>Maven 官方文档说明:
NOTE: In two of these dependency references, we had to specify the
<type/>element. This is because the minimal set of information for matching a dependency reference against a dependencyManagement section is actually {groupId, artifactId, type, classifier}. In many cases, these dependencies will refer to jar artifacts with no classifier. This allows us to shorthand the identity set to {groupId, artifactId}, since the default for the type field is jar, and the default classifier is null.
基于以上说明,假设子模块,依次按以下方式填写 dependency,实际生效的是哪个:
<dependencies>
<!-- 方式 1 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
</dependency>
<!-- 方式 2 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>5.0.0</version>
</dependency>
<!-- 方式 3 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<classifier>jakarta</classifier>
</dependency>
<!-- 方式 4,同时引入 2 个 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<classifier>jakarta-java23</classifier>
</dependency>
</dependencies>假设有个第三方组件,比如 rocketmq-support,他定义了一个 profile 叫 jakarta,独立加了 dependencies,deploy 到中央仓库。
<profile>
<id>jakarta</id>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
</profile>有另外一个组件 B 依赖了 rocketmq-support,并且也定义了一个 jakarta profile 激活。那么这个组件 B 会自动依赖 jakarta.servlet-api 吗?
不会。profile 为自身编译期服务,deploy 后失效。
如果 deploy 后还能依赖传递的话,那某些人定义写一些恶意代码和插件然后定义一堆通用的 profile,比如大家喜欢叫 dev,然后 deploy 就能变相投毒了。
如果父 pom 定义了 profile,子模块直接引用,是会继承生效的。
Tomcat Migration Tool 是 Apache Tomcat 提供的迁移工具。
java -jar jakartaee-migration-*-shaded.jar <source> <destination>The source should be a path to a compressed archive, a folder or an individual file. The destination will be created at the specified path as a resource of the same type as the source.
用途:
缺陷:
另外 Tomcat 10 提供了一种自动转换的方法,参考文档:https://tomcat.apache.org/migration-10.html 将 war 放到 webapps-javaee 目录下,就会自动转换后放到 webapp 下运行。然而这种方案最大的问题在于:
用途:
缺陷:
上面我们都是假设只有 javax 和 jakarta namespace 差异,只需要转换字节码。但是,
assembly-plugin 就可以帮助我们通过 include、exclude 不同的源码、resource、依赖项,重组编译发布不同的包。
这是适合复杂不兼容的代码,但是对于多模块项目,需要某个模块开发指定 assembly xml,会非常痛苦。
目前其他几种工具只处理 javax 和 jakarta package 差异,后续肯定会遇到除了 package 不同,接口或使用方式也不兼容的情况,等遇到了再单独处理。
OpenRewrite 迁移菜谱 Migrate to Jakarta EE 9 。
# 命令行运行
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:RELEASE \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta \
-Drewrite.exportDatatables=false用途:
缺陷:
transformer-maven-plugin 是 eclipse 出的字节码和配置文件转换工具,默认内置了一些 jakarta 的替换规则。
它能帮我们实现:
<plugin>
<!-- 这个插件自动把 javax 转为 jakarta,打包出 javax 和 jakarta 两个包,通过 classifier 区分 -->
<groupId>org.eclipse.transformer</groupId>
<artifactId>transformer-maven-plugin</artifactId>
<version>1.0.0</version>
<extensions>true</extensions>
<executions>
<execution>
<!-- javax 替换成 jakarta -->
<id>jakarta-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<rules>
<jakartaDefaults>true</jakartaDefaults>
</rules>
<artifact>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
</artifact>
</configuration>
</execution>
</executions>
</plugin>用途:
xmlns="http://xmlns.jcp.org/xml/ns/javaee"缺陷:
此插件运行需要 Java 21 版本,发布业务包的时候,必须使用 Java 21 编译,编译目标 release 版本可以是 Java 8,在 pom 中增加以下设置:
<properties>
<java.version>21</java.version>
<maven.compiler.release>8</maven.compiler.release>
</properties>一般咱们典型的 web-fragment 定义有如下两个版本,3.0 和 3.1:
3.0 版本:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0" metadata-complete="true">
</web-fragment>3.1 版本:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
version="3.1" metadata-complete="true">
</web-fragment>其中涉及 jakarta 修改的替换的是其中的 xmlns、xsi、version 字段。
Tomcat 8 以后支持 3.1 版本并兼容 3.0 版本,Tomcat 7 对应 3.0 版本。
transformer-maven-plugin 已经不支持 3.0 版本的替换,所以如果还在用 3.0 的需要咱们手动改成以上 3.1 版本的定义,transformer-maven-plugin 在转换的过程中才能自动升级到 jakarta,自动升级后,升级到 5.0 后的结果如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-fragment_5_0.xsd"
version="5.0" metadata-complete="true">
</web-fragment>我们的 maven 项目大部分都是多模块,对于打包成 war 自运行不需要发布给别人用的项目,一般都不需要转换,可以设置 properties transform.skip=true 跳过。
<properties>
<!-- 不发布 -->
<maven.deploy.skip>true</maven.deploy.skip>
<!-- 不转换 -->
<transform.skip>true</transform.skip>
</properties>业界主流方案就是用版本号区分或者用 classifier 区分这两种方案。
classifier 最主要的问题是无法通过父 pom 继承,这样将导致各个业务方需要大量改 pom.xml 文件。
假设一个项目依赖了 app-a,而 app-a 又依赖 b、c、d.
首先因为父 pom 定义 classifier 并不会继承。那么,我是不是指定 app-a 的 classifier 就行了。
不是,除非 app-a 的特定 classifier 自身 pom 里已经为给个模块指定了 classifier,否则 app-a 依赖的各个都要重新指定一遍,这就使用 pom 文件长得又长又臭。如果哪天 app-a 又新增依赖 e, 但是使用者漏掉了 e classifier 声明的,就可能带来问题,这是很危险的动作。
所以我们最终选择用版本号来区分。
版本号规则,在原版本基础上增加固定前缀:9999.${project.version},如原 9.6.0 对应的 jakarta 版本号是 9999.9.6.0。
父 pom 区分版本号,Spring Boot 3.x 版本依赖,重申版本号。
这样各个使用者,只需要选择不同的父 pom,遵循父 pom 版本定义,dependency 可以原样不变。
目前暂定选择 transformer-maven-plugin,他不完美,但是足够快,相对稳定。
发布目标:两次发布,第一次发布常规版本,第二次发布自动转换后的版本,版本号增加前缀。
发布步骤:
在公司级父 pom 增加如下 profile:
<profiles>
<profile>
<id>jakarta-transformer</id>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.transformer</groupId>
<artifactId>transformer-maven-plugin</artifactId>
<version>1.0.0</version>
<extensions>true</extensions>
<executions>
<execution>
<!-- 此处配置 javax 替换成 jakarta,artifactId 不变,无 classifier -->
<id>jakarta-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<rules>
<jakartaDefaults>true</jakartaDefaults>
</rules>
<artifact>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
</artifact>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>发布 jakarta 版本时,先改版本号增加前缀,再指定 profile 发布:
# 获取当前版本号
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
# 如果项目都是自己写版本号的,使用 version 插件设置新版本号,增加前缀
mvn versions:set -DnewVersion=9999.9.6.0 -DgenerateBackupPoms=false
# 重新发布,指定 -Pjakarta profile,父 pom 里的插件自动转换 javax
mvn deploy -Pjakarta-transformer
# 如果使用 flatten-maven-plugin 插件管理版本号的,设置 revision
mvn deploy -Drevision=9999.9.6.0 -Pjakarta-transformerJakarta EE 就是 Java EE 的新名词。这里的 EE 全称是 Enterprise Edition,它是专门为企业级 Java 应用定义的一套规范,与 Java SE (Java Platform, Standard Edition) 相对应。
Java EE 是从 Java 1.2 版本开始推出的 Java 企业级开发平台,最初的名称是 J2EE (Java 2 Platform, Enterprise Edition)。随着 Java 的发展,它的名称于 Java 1.5 版本时更改为 Java EE (Java Platform, Enterprise Edition)。2009 年 Oracle 收购了 Sun,Java EE 开始由 Oracle 通过 JCP (Java Community Process) 开发和维护。
直到 2017 年,Oracle 将 Java EE 提交给了 Eclipse 基金会,并命名为 Eclipse Enterprise for Java。然而,由于"Java"这个名字的商标归 Oracle 所有,Eclipse 基金会无法继续使用 javax.* 和 java.*,因此,项目名称改为 Jakarta EE。
Jakarta EE 包含了许多技术规范和 API,涵盖了 Web 应用、数据库访问、消息传递、事务处理、安全性等方面的功能,其中包括但不限于下列规范:
Jakarta Servlet:前身是 J2EE Servlet,定义了如何管理 HTTP 请求的规范。这应该是大部分 Java Web 开发者最熟悉的,同时也是许多其它规范的基础。
Jakarta Server Page (JSP):服务端动态生成网页的技术,可以看作 Java 版本的 PHP 和 ASP。
Jakarta Websocket:定义了一套 WebSocket 连接相关的 API,用于实现全双工通信。
Jakarta RESTful Web Services:开发符合 REST 原则的 Web 服务的一套规范。
Jakarta JSON Binding:Java 类和 JSON 字符串互相转换的规范。
Jakarta XML Binding:Java 类和 XML 的映射规范。
Jakarta Enterprise Beans (EJB):这个规范比较复杂,包括 EJB 容器,RMI(远程过程调用),并发控制,依赖注入等。
Jakarta Persistent (JPA):ORM 规范,定义了 Java 类和数据库表直接的映射规范。
Jakarta Transactions (JTA):包含了事务相关的接口和注解类,也用于管理分布式事务。
Jakarta Messaging (JMS):消息系统的规范,用于实现异步消息传递,比如 Apache 的 ActiveMQ 就实现了这套规范。
比如常见的 servlet-api:
<!-- 老版本 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 新版本 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>比如:
// 老版本
import javax.servlet.Filter;
// 新版本
import jakarta.servlet.Filter;如果不兼容会发生什么,比如:
javax.servlet.ServletRequest can not cast to jakarta.servlet.ServletRequest比如下面定义是 javax.persistence.Table,开源或者自定义的数据库中间件件一般习惯按注解 jakarta.persistence.Table 进行扫描:
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "mt_field_extra")
public class FieldExtra {
@Id
String id;
}private static void scanTableName(Class<?> clazz, PersistMeta meta, boolean mapCamelCaseToUnderscore) {
jakarta.persistence.Table table = clazz.getAnnotation(jakarta.persistence.Table.class);
String tableName;
if (null != table) {
tableName = table.name();
} else {
tableName = nameConvert(clazz.getSimpleName(), mapCamelCaseToUnderscore);
}
}如果项目能直接升级到最新版本当然更好,但是作为中间件团队提供的组件,往往需要在过渡期间提供一种兼容两种模式的方案,既能让原有老服务正常使用,又兼容 Spring Boot 3+ Jakarta EE 9+新服务。
Jakarta EE 兼容是一个复杂的课题,当前只记录 package namespace 差异解决方法。
假设父 POM 使用 dependencyManagement 定义如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta-java21</classifier>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>3.0.0</version>
<classifier>jakarta-java23</classifier>
</dependency>
</dependencies>
</dependencyManagement>Maven 官方文档说明:
NOTE: In two of these dependency references, we had to specify the
<type/>element. This is because the minimal set of information for matching a dependency reference against a dependencyManagement section is actually {groupId, artifactId, type, classifier}. In many cases, these dependencies will refer to jar artifacts with no classifier. This allows us to shorthand the identity set to {groupId, artifactId}, since the default for the type field is jar, and the default classifier is null.
基于以上说明,假设子模块,依次按以下方式填写 dependency,实际生效的是哪个:
<dependencies>
<!-- 方式 1 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
</dependency>
<!-- 方式 2 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<version>5.0.0</version>
</dependency>
<!-- 方式 3 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<classifier>jakarta</classifier>
</dependency>
<!-- 方式 4,同时引入 2 个 -->
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
</dependency>
<dependency>
<groupId>com.tester</groupId>
<artifactId>rocketmq-support</artifactId>
<classifier>jakarta-java23</classifier>
</dependency>
</dependencies>假设有个第三方组件,比如 rocketmq-support,他定义了一个 profile 叫 jakarta,独立加了 dependencies,deploy 到中央仓库。
<profile>
<id>jakarta</id>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
</profile>有另外一个组件 B 依赖了 rocketmq-support,并且也定义了一个 jakarta profile 激活。那么这个组件 B 会自动依赖 jakarta.servlet-api 吗?
不会。profile 为自身编译期服务,deploy 后失效。
如果 deploy 后还能依赖传递的话,那某些人定义写一些恶意代码和插件然后定义一堆通用的 profile,比如大家喜欢叫 dev,然后 deploy 就能变相投毒了。
如果父 pom 定义了 profile,子模块直接引用,是会继承生效的。
Tomcat Migration Tool 是 Apache Tomcat 提供的迁移工具。
java -jar jakartaee-migration-*-shaded.jar <source> <destination>The source should be a path to a compressed archive, a folder or an individual file. The destination will be created at the specified path as a resource of the same type as the source.
用途:
缺陷:
另外 Tomcat 10 提供了一种自动转换的方法,参考文档:https://tomcat.apache.org/migration-10.html 将 war 放到 webapps-javaee 目录下,就会自动转换后放到 webapp 下运行。然而这种方案最大的问题在于:
用途:
缺陷:
上面我们都是假设只有 javax 和 jakarta namespace 差异,只需要转换字节码。但是,
assembly-plugin 就可以帮助我们通过 include、exclude 不同的源码、resource、依赖项,重组编译发布不同的包。
这是适合复杂不兼容的代码,但是对于多模块项目,需要某个模块开发指定 assembly xml,会非常痛苦。
目前其他几种工具只处理 javax 和 jakarta package 差异,后续肯定会遇到除了 package 不同,接口或使用方式也不兼容的情况,等遇到了再单独处理。
OpenRewrite 迁移菜谱 Migrate to Jakarta EE 9 。
# 命令行运行
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:RELEASE \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta \
-Drewrite.exportDatatables=false用途:
缺陷:
transformer-maven-plugin 是 eclipse 出的字节码和配置文件转换工具,默认内置了一些 jakarta 的替换规则。
它能帮我们实现:
<plugin>
<!-- 这个插件自动把 javax 转为 jakarta,打包出 javax 和 jakarta 两个包,通过 classifier 区分 -->
<groupId>org.eclipse.transformer</groupId>
<artifactId>transformer-maven-plugin</artifactId>
<version>1.0.0</version>
<extensions>true</extensions>
<executions>
<execution>
<!-- javax 替换成 jakarta -->
<id>jakarta-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<rules>
<jakartaDefaults>true</jakartaDefaults>
</rules>
<artifact>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
</artifact>
</configuration>
</execution>
</executions>
</plugin>用途:
xmlns="http://xmlns.jcp.org/xml/ns/javaee"缺陷:
此插件运行需要 Java 21 版本,发布业务包的时候,必须使用 Java 21 编译,编译目标 release 版本可以是 Java 8,在 pom 中增加以下设置:
<properties>
<java.version>21</java.version>
<maven.compiler.release>8</maven.compiler.release>
</properties>一般咱们典型的 web-fragment 定义有如下两个版本,3.0 和 3.1:
3.0 版本:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0" metadata-complete="true">
</web-fragment>3.1 版本:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
version="3.1" metadata-complete="true">
</web-fragment>其中涉及 jakarta 修改的替换的是其中的 xmlns、xsi、version 字段。
Tomcat 8 以后支持 3.1 版本并兼容 3.0 版本,Tomcat 7 对应 3.0 版本。
transformer-maven-plugin 已经不支持 3.0 版本的替换,所以如果还在用 3.0 的需要咱们手动改成以上 3.1 版本的定义,transformer-maven-plugin 在转换的过程中才能自动升级到 jakarta,自动升级后,升级到 5.0 后的结果如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-fragment_5_0.xsd"
version="5.0" metadata-complete="true">
</web-fragment>我们的 maven 项目大部分都是多模块,对于打包成 war 自运行不需要发布给别人用的项目,一般都不需要转换,可以设置 properties transform.skip=true 跳过。
<properties>
<!-- 不发布 -->
<maven.deploy.skip>true</maven.deploy.skip>
<!-- 不转换 -->
<transform.skip>true</transform.skip>
</properties>业界主流方案就是用版本号区分或者用 classifier 区分这两种方案。
classifier 最主要的问题是无法通过父 pom 继承,这样将导致各个业务方需要大量改 pom.xml 文件。
假设一个项目依赖了 app-a,而 app-a 又依赖 b、c、d.
首先因为父 pom 定义 classifier 并不会继承。那么,我是不是指定 app-a 的 classifier 就行了。
不是,除非 app-a 的特定 classifier 自身 pom 里已经为给个模块指定了 classifier,否则 app-a 依赖的各个都要重新指定一遍,这就使用 pom 文件长得又长又臭。如果哪天 app-a 又新增依赖 e, 但是使用者漏掉了 e classifier 声明的,就可能带来问题,这是很危险的动作。
所以我们最终选择用版本号来区分。
版本号规则,在原版本基础上增加固定前缀:9999.${project.version},如原 9.6.0 对应的 jakarta 版本号是 9999.9.6.0。
父 pom 区分版本号,Spring Boot 3.x 版本依赖,重申版本号。
这样各个使用者,只需要选择不同的父 pom,遵循父 pom 版本定义,dependency 可以原样不变。
目前暂定选择 transformer-maven-plugin,他不完美,但是足够快,相对稳定。
发布目标:两次发布,第一次发布常规版本,第二次发布自动转换后的版本,版本号增加前缀。
发布步骤:
在公司级父 pom 增加如下 profile:
<profiles>
<profile>
<id>jakarta-transformer</id>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.transformer</groupId>
<artifactId>transformer-maven-plugin</artifactId>
<version>1.0.0</version>
<extensions>true</extensions>
<executions>
<execution>
<!-- 此处配置 javax 替换成 jakarta,artifactId 不变,无 classifier -->
<id>jakarta-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<rules>
<jakartaDefaults>true</jakartaDefaults>
</rules>
<artifact>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
</artifact>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>发布 jakarta 版本时,先改版本号增加前缀,再指定 profile 发布:
# 获取当前版本号
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
# 如果项目都是自己写版本号的,使用 version 插件设置新版本号,增加前缀
mvn versions:set -DnewVersion=9999.9.6.0 -DgenerateBackupPoms=false
# 重新发布,指定 -Pjakarta profile,父 pom 里的插件自动转换 javax
mvn deploy -Pjakarta-transformer
# 如果使用 flatten-maven-plugin 插件管理版本号的,设置 revision
mvn deploy -Drevision=9999.9.6.0 -Pjakarta-transformer