性能加速包:SpringBoot 2.7&JDK 17,你敢尝一尝吗
Tech导读
本文将探讨如何通过结合使用SpringBoot 2.7和JDK 17来优化和加速Java应用的性能。通过分析SpringBoot的新特性和改进,以及JDK 17的性能提升点,讨论它们如何协同工作,为Java开发者带来更高效的编程体验。本文还将涉及迁移现有应用至这一新环境的可能挑战和注意事项,为那些追求最前沿技术的勇敢尝试者提供实践建议。
导读
本文将探讨如何通过结合使用SpringBoot 2.7和JDK 17来优化和加速Java应用的性能。通过分析SpringBoot的新特性和改进,以及JDK 17的性能提升点,讨论它们如何协同工作,为Java开发者带来更高效的编程体验。本文还将涉及迁移现有应用至这一新环境的可能挑战和注意事项,为那些追求最前沿技术的勇敢尝试者提供实践建议。
01 前言
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
众所周知,SpringBoot3.0迎来了全面支持JDK17的局面,且最低支持版本就是JDK17,这就意味着,Spring社区将完全抛弃JDK8,全面转战JDK17。作为JAVA开源生态里的扛把子,Spring可以说是整个JAVA生态的风向标,可以说,当Spring转战JDK17,会很快带领JAVA生态全面的跟进JDK17。而本篇文章重点讲述Spring版本和JDK17升级中的实践整理。
02
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
If you’re currently running with an earlier version of Spring Boot, we strongly recommend that you upgrade to Spring Boot 2.7 before migrating to Spring Boot 3.0.
03 为什么是JDK17
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
关于JDK17的新特性和优势,对于它支持的新语法和编程特性,此处不再赘述。
落地JDK17的动力主要源于两个方面:一是更为安全的语言特性,二是更加优异的垃圾回收器和性能提升。
3.1 安全的语言特性
3.2 垃圾收集器
JDK17引入了ZGC作为垃圾收集器,此处引用一下京东科技的曲振富做的关于不同垃圾收集器在不同JDK版本下的压测结果:
压测服务背景:
3.3 OpenJDK17下载地址
本文为读者提供了一个OpenJDK17的下载地址:
https://adoptium.net/zh-cn/temurin/releases/?version=17&os=linux&arch=x64
04
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
4.1 Spring Boot 2.7
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jd.magnus</groupId>
<artifactId>magnus-multi-ddd</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
</parent>
<modules>
<module>magnus-multi-ddd-adapter</module>
<module>magnus-multi-ddd-application</module>
<module>magnus-multi-ddd-domain</module>
<module>magnus-multi-ddd-infrastructure</module>
<module>magnus-multi-ddd-client</module>
<module>magnus-multi-ddd-worker</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jsf-lite.version>1.0.0-HOTFIX-T2</jsf-lite.version>
<ump.version>20221231.1</ump.version>
</properties>
</project>
4.1.2. 动态配置
Spring Boot 2.7对动态配置进行了更新。具体来说,Spring Boot 2.7更改了自动配置注册文件的路径和格式,从META-INF/spring.factories变更为META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
同时,Spring Boot 2.7还引入了新的注解@SpringBootApplication,该注解包含了@EnableAutoConfiguration和@ComponentScan等注解,使得配置更加简洁和方便。此外,Spring Boot 2.7还更新了一些自动配置的类和方法,以支持新版本的Spring Framework和Java。
1.废弃的方法和类删除。列一下主要删除的方法和类:
SpringBootServletInitializer:在Spring Boot 2.7中,该类已经被移除,建议使用SpringBootServletWebServerApplicationContext来代替。
ServletWebServerFactoryCustomizer:这个接口已经从Spring Boot 2.7中移除,可以使用WebServerFactoryCustomizer来代替。
BasicErrorController:这个类已经从Spring Boot 2.7中移除,可以使用ErrorController接口来代替。
ContentNegotiationStrategy:这个接口已经从Spring Boot 2.7中移除,可以使用RequestMappingHandlerMapping的setContentTypeResolver(ContentTypeResolver)方法来代替。
HttpMessageConverters:这个接口已经从Spring Boot 2.7中移除,可以使用HttpMessageConvertingComparator来代替。
HttpMessageConvertingComparator:这个类已经从Spring Boot 2.7中移除,可以使用ComparatorChain来代替。
ServletWebServerFactoryCustomizerBeanPostProcessor:这个类已经从Spring Boot 2.7中移除。
SpringBootApplicationContextLoader:这个类已经从Spring Boot 2.7中移除,可以使用SpringApplicationWebApplicationContext来代替。
SpringBootServletInitializerAutoConfiguration:这个类已经从Spring Boot 2.7中移除,可以使用SpringBootServletWebServerApplicationContextAutoConfiguration来代替。
github上的release版本说明:
4.1.3. 单元测试升级
变更项 | JUnit4 | JUnit Jupiter |
@Test注解 | 包路径:org.junit.Test | 包路径:org.junit.jupiter.api.Test |
断言 | 类:org.junit.Assert | 类:rg.junit.jupiter.api.Assertions,提供了更简洁的断言方法 |
@RunWith | 需要使用@RunWith注解来指定测试运行器 | @RunWith移除,不再需要,单侧只需要@SpringBootTest |
参数化接口测试 | @RunWith配合 @Parameters实现参数化 | @ParameterizedTest和@ValueSource注解配合使用 |
4.1.4. hibernate-validator包依赖问题
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
4.2 行云部署配置
首先需要新的基础镜像包,包括OpenJDK17的。之前行云的镜像市场上是没有相关的镜像的,后来联系科技运维同事帮忙制作了新的镜像,新的镜像包是基于Tomcat应用类型的,大家可以在jdos的中国站的景象市场搜索到。镜像名如下,
base_tomcat/java-jd-centos7-jdk17-tomcat8.5.42-ngx197:latest
然后就是配置JVM启动参数,需要开启ZGC。具体启动参数以4C8G的资源为例,配置参数如下:
-Xms5324m -Xmx5324m -XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=256m -XX:MaxDirectMemorySize=983m
-Djava.library.path=/usr/local/lib -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/Logs
-Djava.awt.headless=true -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000
-Djmagick.systemclassloader=no -Dnetworkaddress.cache.ttl=300 -Dsun.net.inetaddr.ttl=300
-XX:+UseZGC
此处需要注意,ZGC不要配置并行GC线程的数量,并发标记线程数等信息,配置了反而会出现启动报错情况。
4.3 兼容性问题说明
--add-opens java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.util.calendar=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.management/java.lang.management=ALL-UNNAMED
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
--add-opens java.management/sun.management=ALL-UNNAMED
--add-opens java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
1. SGM依赖需要加入
--add-opens java.management/java.lang.management=ALL-UNNAMED
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
--add-opens java.management/sun.management=ALL-UNNAMED
2. R2M需要加入
--add-opens java.base/java.time=ALL-UNNAMED
3. DUCC依赖需要加入
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
4. AKS依赖需要加入
--add-exports java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.util.calendar=ALL-UNNAMED
5. Pfinder依赖需要加入
--add-opens java.base/sun.net.util=ALL-UNNAMED
6. jsf依赖需要加入
--add-opens java.base/java.time=ALL-UNNAMED #当使用LacalDate做出入参时需要添加--add-opens java.base/java.io=ALL-UNNAMED
7. Swagger兼容性配置
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
/**
* 增加如下配置可解决Spring Boot 2.7.15 与Swagger 3.0.0 不兼容问题
**/
@Bean
public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
类名 | 方法说明 |
javafx.util.Pair | getKey():获取 Pair 对象的键。getValue():获取 Pair 对象的值。setKey(K key):设置 Pair 对象的键。setValue(V value):设置 Pair 对象的值。 |
avafx.util.Duration | toSeconds():将持续时间转换为秒。toMillis():将持续时间转换为毫秒。toNanos():将持续时间转换为纳秒。add(Duration other):将另一个持续时间添加到当前持续时间。subtract(Duration other):从当前持续时间中减去另一个持续时间。 |
javafx.util.converter | fromString(String value):将字符串值转换为目标类型。toString(T value):将目标类型的值转换为字符串。 |
javafx.util.StringConverter | fromString(String value):将字符串值转换为目标类型。toString(T value):将目标类型的值转换为字符串。 |
关于JSF启动有报错信息:运行时找不到 javax.xml.bind.JAXBException 类。在 JDK 9 及更高版本中,javax.xml.bind 包被移除了,并且不再包含在标准的 Java SE 中。
如果您的项目依赖于 JAXB API,您可以尝试以下解决方法之一:
如果您使用的是 JDK 8 或更早版本,请确保您的项目使用的是兼容的 JDK 版本。
如果您使用的是 JDK 9 或更高版本,并且需要使用 JAXB API,您可以添加以下依赖项来解决该问题:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version> <!-- 根据您的需求选择合适的版本 -->
</dependency>
module | packages | replacement groupld | replacement artifactld |
java.activation | javax.activtion | com.sun.activation | jakarta.activation |
java.xml.ws.annotation | java.annotation | jakartaa.annotation | jakarta.annotation-api |
java.xml.bind | javax.xml.bind.* | jakarta.xml.bind.com.sun.xml.bind | jakarta.xml.bind-apijab-impl |
除此之外,JSF在使用ForkJoinPool会导致反序列化问题,包括底层使用ForkJoinPool的CompletableFuture.runAsync 有同样问题。原因是升级JDK后,jsf依赖了jaxb包在新JDK中是缺失的,会导致注册中心自动切换有问题,解决方案是手动引入如下包:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.8</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.8</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.8</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
05 脚手架支持
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
目前最的DDD脚手架已经支持Spring Boot 2.7.17 和JDK17 ,下载脚本如下:
mvn archetype:generate \
-DarchetypeGroupId=com.jd.magnus \
-DarchetypeArtifactId=magnus-multi-ddd-archetype \
-DarchetypeVersion=1.0.0-SNAPSHOT \
-DinteractiveMode=false \
-DarchetypeCatalog=remote \
-Dversion=1.0.0-SNAPSHOT \
-DgroupId=com.jdl.sps \
-DartifactId=bff-demo1
该脚手架以在京东内部申请为开源项目,开源项目地址如下:
http://xingyun.jd.com/shendeng/openSource/detail/793
06 总结
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
求分享
求点赞
求在看