背景
老的项目使用jdk1.8,spring5.x,使用外置tomcat部署。由于低版本存在安全隐患,需要把老项目升级为jdk17,springboot3.x,外置tomcat改为内置tomcat启动。
老版本spring漏洞
- 特殊匹配模式下身份认证绕过漏洞(CVE-2023-20860)
- spring mvc 反射型文件下载漏洞
- spring web uriComponentsBuilder url解析不当漏洞
升级思路
- 修改pom引用,jdk8->17,spring 5.x->springboot3.x
- 排除第三方包中spring的低版本引用
- 对项目进行编译,启动,依次对不兼容组件进行升级
- 解决jar报冲突
- 配置内置tomcat 启动参数,需要调优
涉及相关组件升级,相关改动如下
javax.servlet->jakarta.servlet
2018年,Java EE更名为Jakarta EE,spring 6.x后使用jdk17,并且对javax.servlet改为jakarta.servlet,升级为springboot3.x后,javax.servlet需要全部替换
servlet相关类包名替换,javax.servlet->jakarta.servlet
相关annotaion包名替换,javax.annotation->jakarta.annotation
JSR-330的@Inject、JSR 250的@PostConstruct、@Predestroy以及及其常用的@Resource注解,Resource包名变成了jakarta.annotation.Resource。
需要注意的是,如果第三方包中有使用javax.annotation相关注解,可能不再生效.
spring高版本本身有做javax.annotation相关的兼容 如javax.annotation.PostConstruct注解 spring版本6.1.10,源码如下
CommonAnnotationBeanPostProcessor
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
// Jakarta EE 9 set of annotations in jakarta.annotation package
addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));
// Tolerate legacy JSR-250 annotations in javax.annotation package
addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
// java.naming module present on JDK 9+?
if (jndiPresent) {
this.jndiFactory = new SimpleJndiBeanFactory();
}
}
//获取注解时如果类路径未引用,返回的null
@Nullable
private static Class<? extends Annotation> loadAnnotationType(String name) {
try {
return (Class<? extends Annotation>)
ClassUtils.forName(name, CommonAnnotationBeanPostProcessor.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
return null;
}
}
所以,如果仍需要使用javax.annotation注解,需要引用该包
<!--兼容老的javax.annotation注解-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
第三方包中如果使用servlet,如果能反编译,则反编译后重复做上面两个步骤;如果不能反编译,看下是否有高版本支持;如果既不能反编译又没有高版本支持,比如hystrix,可以寻找替换方案
hystrix 替换为 resilience4j,需要注意老的线程池等参数配置,尽量保持一致。改完之后需要对新的组件进行压测 参考 io.dropwizard.metrics 替换为 springboot-actuator 参考
兼容老的spring配置
为了尽量少的修改代码,对老的xml,properties配置做兼容处理
properties文件加载
方式一,使用@PropertySource注解,这种方式需要把每一个property文件做配置
@PropertySource({"classpath:applicationConfigurations/allowedHeaders.properties",
"classpath:applicationConfigurations/couchbase.properties"})
@Configuration
public class MyConfigs{
}
方式二,手动读取配置加载,这种方式好处是如果配置很多,不用一个个的配置,可以直接读取加载相对路径下所有的配置
public class PropertySourcesInit implements ApplicationContextInitializer<AbstractApplicationContext> {
@Override
public void initialize(AbstractApplicationContext ctx) {
ConfigurableEnvironment environment = ctx.getEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
URL defaultPropertiesFolder = this.getClass().getClassLoader().getResource("properties 文件相对路径");
PropertiesFolderPropertySource aggProps = new PropertiesFolderPropertySource("internal files: "
+ DEFAULT_PROP_PATH, defaultPropertiesFolder);
propertySources.addLast(propertySource);
}
然后在application.properties文件中指定context初始化
context.initializer.classes=com.xxx.PropertySourcesInit
加载xml配置
通过ImportResource注解加载老的xml配置,该注解支持通配符
@PropertySource({"classpath:applicationConfigurations/allowedHeaders.properties",
"classpath:applicationConfigurations/couchbase.properties"})
@ImportResource({"classpath:Application*.xml","classpath:*Context.xml","classpath:*Source.xml","classpath:*context.xml","classpath:*aop*.xml"})
@Configuration
public class MyConfigs{
}
一些报错及解决方案
springboot本身有很多auto config,如果报错重复加载,增加以下配置
The bean 'serviceProperties', defined in class path resource [ApplicationConfigurationContext.xml], could not be registered. A bean with that name has already been defined in file [/xxx/ApplicationConfigurationContext.xml] and overriding is disabled.
application.properties中增加下面配置,允许重写
spring.main.allow-bean-definition-overriding= true
如果循环依赖,可以增加以下配置
报错信息:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xxxService': Requested bean is currently in creation: Is there an unresolvable circular reference?
The dependencies of some of the beans in the application context form a cycle:
application.properties中增加下面配置,允许
spring.main.allow-circular-references= true
web.xml处理
老的war包部署到tomcat中,依赖web.xml配置,改为springboot内置tomcat部署后,需要把web.xml中内容迁移到配置类中 比如老的web.xml中配置的Filter
<filter>
<filter-name>staticFilter</filter-name>
<filter-class>com.xxx.StaticFilter</filter-class>
</filter>
改为配置类
@Configuration
@ImportResource({"classpath:*Context.xml", "classpath:*Source.xml", "classpath:*context.xml", "classpath:*aop*.xml"})
public class CustomerConfigs {
@Bean
public FilterRegistrationBean<Filter> getStaticFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new StaticFilter());
filterRegistrationBean.addUrlPatterns("/docs/*");
filterRegistrationBean.setName("staticFilter");
return filterRegistrationBean;
}
}
迁移时需要注意Filter的顺序,web.xml中执行顺序为 配置顺序,改为config bean之后,需要手动设置order
org.springframework.data.repository.CrudRepository兼容处理
新旧版本,CrudRepository 方法有些不同,根据新版本的做兼容处理即可
org.springframework.beans.factory.annotation.Required
org.springframework.beans.factory.annotation.Required在spring高版本中已删除
从Spring 5.1开始,@Required 注解已经被弃用,并在未来版本中可能会被移除。如果你使用的是Spring 5.1或更高版本,应该使用 @Autowired 注解来标记依赖必须在构造函数、工厂方法或字段注入时提供。
如果你需要保持对Setter注入的强制性,可以使用 @RequiredArgsConstructor 和 @NonNull 注解,这需要Lombok库的支持。
redis
使用springboot-data自动加载
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
如果没使用到jpa,排除jpa自动加载
报错信息:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': JPA metamodel must not be empty
Caused by: java.lang.IllegalArgumentException: JPA metamodel must not be empty
启动类排除
exclude = {
JpaRepositoriesAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
}
jedis升级为高版本
报错信息:
Caused by: java.lang.ClassNotFoundException: redis.clients.jedis.JedisClientConfig
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency>
报错信息:
Caused by: java.lang.ClassNotFoundException: redis.clients.jedis.Queable
jedis版本不匹配,改为下面版本
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.2</version>
</dependency>
EhCache
EhCache升级为3.10.8
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
报错信息:
Caused by: java.lang.ClassNotFoundException: org.springframework.cache.ehcache.EhCacheManagerFactoryBean
springboot 3.x之后使用JcacheManager适配cache 1,删除xml配置
<bean id="ehcache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
<property name="shared" value="true" />
</bean>
2,使用@EnableCaching开启缓存
3,通过application.properties指定ehcache.xml路径
spring.cache.jcache.config=classpath:ehcache.xml
4,ehcache.xml配置为高版本配置,需要注意新老配置是否一致!!!
旧的配置
<cache name="cacheNames"
maxEntriesLocalHeap="1000"
eternal="false"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"
statistics="true">
<persistence strategy="localTempSwap" />
</cache>
新的配置
<eh:cache-template name="default">
<eh:expiry>
<!-- 过期策略 ttl:time to live,指定条目的存活时间 -->
<eh:ttl unit="seconds">1800</eh:ttl>
<!-- 过期策略 tti:time to idle,条目在指定时间段内未被使用,则过期 -->
<!-- <eh:tti unit="seconds">600</eh:tti>-->
<!-- 不过期 只能手动删除 -->
<!--<eh:none/>-->
</eh:expiry>
<eh:resources>
<!--堆内内存可以的条目-->
<eh:heap unit="entries">1000</eh:heap>
<!-- 超过条目,保存到堆外内存 不配表示关闭-->
<!--<eh:offheap unit="MB">10</eh:offheap>-->
<!-- 堆外内存超了之后 保存至磁盘 -->
<!--<eh:disk unit="MB">10</eh:disk>-->
<!--缓存顺序-->
</eh:resources>
</eh:cache-template>
<!-- 继承自defalt的配置 -->
<eh:cache alias="cacheNames" uses-template="default">
</eh:cache>
CXFServlet
apache cxf改为jersey 1,删除web.xml中配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
2,pom文件引入jersey
<!--jersey-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>3.1.3</version>
</dependency>
3,配置jersey
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
//配置扫描包
packages("com.xxx.rest");
//注册Mapper
register(RetrofitErrorMapper.class);
//注册Provider
register(new JacksonJsonProvider(objectMapper));
//注册Filter
register(getFilteredLoggingInInterceptor());
}
private FilteredLoggingInInterceptor getFilteredLoggingInInterceptor(){
FilteredLoggingInInterceptor filter = new FilteredLoggingInInterceptor();
filter.setPrettyLogging(true);
filter.setShowBinaryContent(true);
filter.setFilterPatterns(List.of("/swagger.json","/admin/","/health/"));
filter.setOutputJSON(true);
return filter;
}
}
jooq
jooq-codegen-maven从3.4.4升级到3.17.16版本,兼容jakarta.servlet jooq官方文档 www.jooq.org/doc/3.17/ma…
1,修改配置文件
原有配置文件
<plugin>-->
<!-- <groupId>org.jooq</groupId>-->
<!-- <artifactId>jooq-codegen-maven</artifactId>-->
<!-- <version>3.4.4</version>-->
<!-- <executions>-->
<!-- <!– Generate the required class from the database –>-->
<!-- <execution>-->
<!-- <id>generate-h2</id>-->
<!-- <phase>generate-sources</phase>-->
<!-- <goals>-->
<!-- <goal>generate</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- <dependencies>-->
<!-- <dependency>-->
<!-- <groupId>com.h2database</groupId>-->
<!-- <artifactId>h2</artifactId>-->
<!-- <version>${h2.version}</version>-->
<!-- </dependency>-->
<!-- </dependencies>-->
<!-- <configuration>-->
<!-- <jdbc>-->
<!-- <driver>${db.driver}</driver>-->
<!-- <url>${db.url}</url>-->
<!-- <user>${db.username}</user>-->
<!-- <password>${db.password}</password>-->
<!-- </jdbc>-->
<!-- <generator>-->
<!-- <database>-->
<!-- <!– Configure the used database dialect –>-->
<!-- <name>org.jooq.util.h2.H2Database</name>-->
<!-- <!– Include all tables found from the PUBLIC -->
<!-- schema –>-->
<!-- <includes>.*</includes>-->
<!-- <excludes />-->
<!-- <inputSchema>PUBLIC</inputSchema>-->
<!-- <outputSchemaToDefault>true</outputSchemaToDefault>-->
<!-- </database>-->
<!-- <!– Generate classes for tables and records –>-->
<!-- <generate>-->
<!-- <records>true</records>-->
<!-- </generate>-->
<!-- <!– Configure the target package and directory –>-->
<!-- <target>-->
<!-- <packageName>com.xxx.domain.jooq</packageName>-->
<!-- <directory>target/generated-sources/jooq</directory>-->
<!-- </target>-->
<!-- </generator>-->
<!-- </configuration>-->
<!-- </plugin>-->
改后配置文件
<plugin>
<!-- Use org.jooq for the Open Source Edition
org.jooq.pro for commercial editions with Java 17 support,
org.jooq.pro-java-11 for commercial editions with Java 11 support,
org.jooq.pro-java-8 for commercial editions with Java 8 support,
org.jooq.trial for the free trial edition with Java 17 support,
org.jooq.trial-java-11 for the free trial edition with Java 11 support,
org.jooq.trial-java-8 for the free trial edition with Java 8 support
Note: Only the Open Source Edition is hosted on Maven Central.
Install the others locally using the provided scripts, or access them from here: https://repo.jooq.org -->
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>3.17.16</version>
<!-- The jOOQ code generation plugin is also executed in the generate-sources phase, prior to compilation -->
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
</dependencies>
<!-- This is a minimal working configuration. See the manual's section about the code generator for more details -->
<configuration>
<jdbc>
<driver>${db.driver}</driver>
<url>${db.url}</url>
<user>${db.username}</user>
<password>${db.password}</password>
</jdbc>
<generator>
<database>
<!-- Configure the used database dialect -->
<!-- <name>org.jooq.util.h2.H2Database</name>-->
<!-- Include all tables found from the PUBLIC
schema -->
<includes>.*</includes>
<excludes />
<inputSchema>PUBLIC</inputSchema>
<outputSchemaToDefault>true</outputSchemaToDefault>
</database>
<!-- Generate classes for tables and records -->
<generate>
<records>true</records>
</generate>
<!-- Configure the target package and directory -->
<target>
<packageName>com.xxx.domain.jooq</packageName>
<directory>target/generated-sources/jooq</directory>
</target>
</generator>
</configuration>
</plugin>
jooq升级新版本后问题
日期类型问题 报错信息
对于set(org.jooq.TableField<com.xxx.TrackerRecord,java.time.LocalDateTime>,java.sql.Timestamp), 找不到合适的方法
SQL DATE modelled by java.time.LocalDate and JDBC's java.sql.Date
SQL TIME modelled by java.time.LocalTime and JDBC's java.sql.Time
SQL TIMESTAMP modelled by java.time.LocalDateTime and JDBC's java.sql.Timestamp
解决方案: 修改业务代码,兼容新版本日期类型。原来数据库是UTC时间,接口返回的是东八区时间,需要确认jooq高版本时间是否一致 generator 时指定sql Date类型生成为TIMESTAMP类型,避免改动业务代码---这种方式未生效,使用的修改代码 www.jooq.org/xsd/jooq-co… 高版本时间类型默认为LocalDateTime Record.set()不再支持Timestamp参数,需要改成LocalDateTime Record.get()不再支持Date,需要改成LocalDateTime
rabbit-mq
报错信息:
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.3.2.2: Attribute 'publisher-confirms' is not allowed to appear in element 'rabbit:connection-factory'.
老的publisher-confirms 配置方式需要改为confirm-type
kafka
Caused by: java.lang.NoClassDefFoundError: org/springframework/kafka/retrytopic/RetryTopicConfiguration
升级spring-kafka版本
从1.2.2.RELEASE升级到3.0.12
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.0.12</version>
</dependency>
log4j
报错信息:
Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.boot.logging.log4j2.Log4J2LoggingSystem and org.apache.logging.log4j.util.PropertiesUtil
Spring Boot 3.0.2 依赖于不易受到攻击的 log4j 2.19,因此您必须有一个属性将其显式设置为 2.18,否则这将成为一个问题。不需要这样的属性(正如您所发现的,这是一种回归,无论如何都会使其不兼容。)
升级log4j版本
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.20.0</version>
</dependency>
第三方jar依赖低版本jdk
Caused by: java.lang.ClassNotFoundException: javax.xml.ws.Service
引入相关jar
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.1</version>
</dependency>
Caused by: javax.xml.soap.SOAPException: Unable to create SAAJ meta-factory: Provider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found
引入
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.5.1</version>
</dependency>
Caused by: java.lang.NullPointerException: Cannot invoke "javax.xml.ws.spi.Provider.createServiceDelegate(java.net.URL, javax.xml.namespace.QName, java.lang.Class)" because the return value of "javax.xml.ws.spi.Provider.provider()" is null
引入
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>rt</artifactId>
<version>2.3.1</version>
</dependency>
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @5e600dd5
启动参数增加
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
quartz
Caused by: org.quartz.SchedulerConfigException: DataSource name not set.
https://blog.csdn.net/a15835774652/article/details/135550896
在springboot 2.6 之前 集成quartz 配置 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX 来定义quartz默认的数据源支持
但是在2.6之后 不在支持此方式进行配置默认数据源 需要使用 新的类
org.springframework.scheduling.quartz.LocalDataSourceJobStore
报错及解决
servlet编码问题
客户端请求后,postman报错 Error: aborted并且无法正常接收相应结果;debug发现服务端有正常返回。 使用curl -v 发现返回有乱码,并报错curl: (18) transfer closed with 32 bytes remaining to read,判断是编码格式不对
curl -v --location 'localhost:8088/xxxx/profile/event?srcId=123' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--data '{
"srcId":"123"
}'
* Connected to localhost (::1) port 8088
> POST /xxxx/profile/event?srcId=123 HTTP/1.1
> Host: localhost:8088
> User-Agent: curl/8.4.0
> Content-Type: application/json
> Accept: */*
> Content-Length: 21
>
< HTTP/1.1 400
< vary: Origin
< X-Conversation-Id: SYS449294038695-2183008-2015353-1760374
< X-Page-ID:
< X-Correlation-ID: SYS449294038695-4533294-3745116-4862556
< X-CorrelationId: a30f861d-86f7-4f38-a476-a544d0302973
< X-Disney-Internal-Service-Stopwatch: 546
< X-Disney-Internal-Service-Host: localhost:8088
< Content-Type: application/json;charset=ISO-8859-1
< Content-Length: 576
< Date: Mon, 12 Aug 2024 03:40:39 GMT
< Connection: close
<
* transfer closed with 32 bytes remaining to read
* Closing connection
curl: (18) transfer closed with 32 bytes remaining to read
{"errors":[{"errorCode":"WDPRO_3","errorType":"FIELD_VALIDATION_ERROR","errorLocation":"handleProfileEvent.context.srcSys","errorMessage":"????"},{"errorCode":"WDPRO_3","errorType":"FIELD_VALIDATION_ERROR","errorLocation":"handleProfileEvent.context.id","errorMessage":"????"},{"errorCode":"WDPRO_3","errorType":"FIELD_VALIDATION_ERROR","errorLocation":"handleProfileEvent.context.type","errorMessage":"????"},{"errorCode":"WDPRO_3","errorType":"FIELD_VALIDATION_ERROR","errorLocation":"handleProfileEvent.context.data","errorMessage":"????"}]} **%**
解决方案是,springboot 增加如下配置
server.servlet.encoding.enabled=true
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
jdk模块化
报错信息
java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.time.LocalDateTime java.time.ZonedDateTime.dateTime accessible: module java.base does not "opens java.time" to unnamed module @4de4b452
在 Java8 中,没有人能阻止你访问特定的包,比如 sun.misc,对反射也没有限制,只要 setAccessible(true) 就可以了。Java9 模块化以后,一切都变了,只能通过 --add-exports 和 --add-opens 来打破模块封装
--add-opens导出特定的包--add-opens允许模块中特定包的类路径深度反射访问
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/jdk.internal.access=ALL-UNNAMED
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
总结
升级过程中主要改动
1,开源组件间版本不兼容,需要升级版本(json,swagger等),更改配置方式(ehcache),修改方法使用方式(spring-rabbit confirm方法,jooq时间类型),包名替换(javax-》jakarta,rest相关组件都需要升级支持jakarta,jersey,swagger)
2,非开源组件升级,重写(copy改)或者动态替换(BeanPostProcessor)