性能加速包: SpringBoot 2.7&JDK 17,你敢尝一尝吗 | 京东物流技术团队
前言
众所周知,SpringBoot3.0迎来了全面支持JDK17的局面,且最低支持版本就是JDK17,这就意味着,Spring社区将完全抛弃JDK8,全面转战JDK17。作为JAVA开源生态里的扛把子,Spring可以说是整个JAVA生态的风向标,可以说,当Spring转战JDK17,会很快带领JAVA生态全面的跟进JDK17。而我本篇文章重点讲述Spring版本和JDK17升级中的实践整理。
为什么是Spring Boot 2.7
Spring Boot 3.0是全面放弃JDK8,而Spring社区当然不会把事情做的那么决绝,在推出3.0之前,Spring就开始着手布局JDK17升级。而2.7版本,就是Spring社区为了升级JDK17而推出的过渡版本,具体包括一以下几个方面的升级和改进:
总之,使用Spring Boot 2.7可以更好地利用JDK 17的特性,提高应用程序的性能和响应速度,同时还可以获得更好的兼容性和安全性。所以通过Spring Boot 2.7过渡升级JDK17,是一种更为温和方式,且遇到的兼容性问题最小。当然这不全是我自顾自说,Spring官方给出了这样的说明:
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.
为什么是JDK17
关于JDK17的新特性和优势,对于它支持的新语法和编程特性,我在此不再赘述,因为网上有很多文章介绍。
对于我落地JDK17的动力主要源于两个方面:一是更为安全的语言特性,二是更加优异的垃圾回收器和性能提升。
a. 安全的语言特性
JDK 17对反射进行了优化,主要表现在对反射调用进行了权限控制。具体来说,它通过setAccessible()方法启动或禁止访问安全检查开关。当参数值为true时,反射的对象在使用时取消安全检查,提高反射的效率;当参数值为false时,反射的对象执行安全检查。这样的优化使得在处理反射调用时,可以更加灵活地控制访问权限。
b. 垃圾收集器
JDK17引入了ZGC作为垃圾收集器,此处引用一下京东科技同事做的关于不同垃圾收集器在不同JDK版本下的压测结果:
压测服务背景:
DOS平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用JDK8、JDK11和JDK17进行部署和压测。整个压测过程限时60分钟,用180个虚拟用户并发请求一个接口,每次接口请求都创建512Kb的数据。最终产出不同GC回收器的各项指标数据,来分析GC的性能提升效果。

以上的压测结果对于我们来说非常诱人,ZGC配合JDK17的性能对于其他JDK和垃圾收集器组合来说是碾压获胜,且不论任何机器配置下,都推荐使用ZGC,ZGC的停顿时间达到亚毫秒级,吞吐量也比较高。这个对于一个高并发业务场景下,对于资源的优化是非常客观的。
c. OpenJDK17下载地址
提供了一个下载地址: https://adoptium.net/zh-cn/temurin/releases/?version=17&os=linux&arch=x64
行云部署上的实践方案
a. Spring Boot 2.7
1. pom.xml版本依赖
实践的版本选择上我选择了2.7大版本下的最新小版本,即Spring Boot 2.7.17版本。pom.xml依赖如下:
<?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>
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。
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来代替。
此外,还有一些被移除的配置属性,例如spring.http.converters.preferred-json-mapper、spring.jackson.serialization-features.default-pretty-print-xml、spring.jackson.serialization-features.sort-property-names-by-default等。其他信息大家可以看Spring的版本更新说明:
github上的release版本说明:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes
3. 单元测试升级
在Spring Boot 2.7版本,已经不再依赖JUnit4, 而是将Test换成了 JUnit Jupiter, 这也导致之前单元测试使用的方法和注解会产生变化。
常用的一些方法和注解变化如下:
| 变更项 | 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. hibernate-validator包依赖问题
Springboot从2.3以后,spring-boot-starter-web中不再引入hibernate-validator,需要手动引入。此处可以直接引用spring-boot-starter-validation的包,里面会间接引用hibernate-validator的包,且版本号可以被spring boot parent统一管理。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
4. 诊断升级兼容性方法
如果是老项目版本升级,Spring Boot 提供了一种在启动时分析应用程序环境并打印诊断信息的方法,而且还可以在运行时为您临时迁移属性。要启用该功能,请将以下依赖项添加到您的项目中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
b. 行云部署配置
1. 镜像配置
首先需要新的基础镜像包,包括OpenJDK17的。之前行云的镜像市场上是没有相关的镜像的,后来联系科技运维同事帮忙制作了新的镜像,新的镜像包是基于Tomcat应用类型的,大家可以在jdos的中国站的景象市场搜索到。镜像名如下,:
base_tomcat/java-jd-centos7-jdk17-tomcat8.5.42-ngx197:latest
2. 编译配置
编译配置中,需要选择的JDK版本为17,同时Maven的版本也可以尽量选高一些。因为按照惯例,maven的版本会对JDK的版本兼容性有所不同,一般越是高版本的Maven对JDK17兼容性更好。虽然官方没有明确说明Maven版本支持情况,但我们选择高版本的Maven是比较稳妥的选择,所以在JDOS上我们选择maven-3.9.0版本比较好。

3. JVM参数配置
然后就是配置JVM启动参数,我们需要开启ZGC.具体启动参数以4C8G的资源为例,配置参数如下:
-Xms5324m -Xmx5324m -XX:MaxMetaspaceSize此处需要注意,ZGC不要配置并行GC线程的数量,并发标记线程数等信息,配置了反而会出现启动报错情况。
c. 兼容性问题说明
关于兼容性问题之前一篇文章里详细介绍了,包括如何兼容京东的UMP, DUCC等。这些中间件的兼容性问题产生主要由于JDK17中对于反射和扫描的安全性检查导致的,一个简单的解决办法是将没开放的module强制对外开放。所以需要一些额外配置。
先整理结论,额外配置集合如下,该集合可以配置在VM 启动参数之中:
--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
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. 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 .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);
}
}
};
}
7. JDK维度兼容性问题(只挑我遇到的问题重点说)
以下列举一下javafx.util下的一些常用工具类(项目中尽量不要再用):
| 类名 | 方法说明 |
| 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,需要手动引入一些JAR包。但由于我们部署环境采用的是外置的Tomcat8,所以还是包含java EE的相关包。不需要额外加入,但本地debug时,需要加入。
尽管 Jakarta EE 是 Java EE 的继任者,但为了保持向后兼容性,许多 Java EE 规范和 API 在 Jakarta EE 中仍然存在,并且在 Jakarta EE 中的命名空间从 javax 变为 jakarta。且一些命名空间也改变了,比如javax包下的方法和属性都不能再试用,例如: javax.xml.bind.*更改为jakarta.xml.bind.*。以下有一个该问题引起的JSF报错修复:
关于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 groupId | replacement artifactId |
| java.activation | javax.activation | com.sun.activation | jakarta.activation |
| java.xml.ws.annotation | java.annotation | jakarta.annotation | jakarta.annotation-api |
| java.xml.bind | javax.xml.bind.* | jakarta.xml.bind.com.sun.xml.bind | jakarta.xml.bind-apijaxb-impl |
垃圾回收器的话,从JDK14开始,已经删除了CMS,所以在JDK17下,只建议使用ZGC。
还有一个最大的变化是之前的--illegal-access参数不在可用,如果在java 17使用这个参数访问受限的api则会报出InaccessibleObjectException,大多数情况下只要升级了依赖项是不会碰到这个情况的,但如果出现问题,则可以使用--add-opens来对不可访问的api授权。以上的SGM,R2M,DUCC,AKS,Pfinder的兼容性问题都是因为这个特性变化引起的。
脚手架支持
目前最的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
IDE配置注意事项

modules也需要更改Language level, 截图如下:

当本地debug时,需要配置debug的启动参数,配置VM options,并将上面【行云部署上的实践方案-c.兼容性问题说明】章节中所说的兼容性问题配置添加到该处。如下图所示:

此方法也适用于本地debug JUnit Test单元测试本地调试时用。当然,如果为了避免每个单元测试都要手动配置,可以点击图中的Edit configuration templates,配置模板,做到一劳永逸。具体配置如下图所示:

同理,在使用maven命令对工程进行test命令时,也需要额外配置启动参数,来兼容JDK17订单安全性检查,需要在pom文件中的maven-surefire-plugin增加如下配置:

总结
目前,将部门内的京旗API服务, 发货平台BFF服务,物流发货商家基础信息服务作为试点,已经在行云测试环境上用JDK17+Spring Boot2.7版本进行试运行,京东三方依赖包括JSF lite版本,ducc, easyJob,jmq, 云redis, ump, pfinder。经测试,兼容性没有太大问题,服务可用。后续还会进一步观察和测试。
据我了解,现在开源社区里,以apache为代表的大型开源项目都对JDK17有了不错的兼容, 未来可以逐步再从Spring Boot 2.7升级到Spring Boot 3.0。
作者:京东物流 赵勇萍
来源:京东物流 自猿其说 Tech 转载请注明来源
性能加速包: SpringBoot 2.7&JDK 17,你敢尝一尝吗 | 京东物流技术团队的更多相关文章
- 是时候考虑升级 JDK 17 了
Spring,作为 Java EE 的事实规范,在2022年11月16日发布了最新的 6.0.0 GA 版本.这个版本是框架后续新生代的初始版本,拥抱持续创新的 OpenJDK 和 Java 生态.新 ...
- 新项目决定用 JDK 17了
大家好,我是风筝,公众号「古时的风筝」,专注于 Java技术 及周边生态. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 最近在调研 JDK 1 ...
- 转 [PHP] - 性能加速 - 开启Opcache
原文地址:[PHP] - 性能加速 - 开启Opcache PHP7已经发布了, 作为PHP10年来最大的版本升级, 最大的性能升级, PHP7在多放的测试中都表现出很明显的性能提升 一.开启Opc ...
- [PHP] - 性能加速 - 开启opcache
说明 PHP 5.5+版本以上的,可以使用PHP自带的opcache开启性能加速(默认是关闭的).对于PHP 5.5以下版本的,需要使用APC加速,这里不说明,可以自行上网搜索PHP APC加速的方法 ...
- 黄聪:PHP5.6+7代码性能加速-开启Zend OPcache-优化CPU
说明 PHP 5.5+版本以上的,可以使用PHP自带的opcache开启性能加速(默认是关闭的).对于PHP 5.5以下版本的,需要使用APC加速,这里不说明,可以自行上网搜索PHP APC加速的方法 ...
- PHP5.6+7代码性能加速-开启Zend OPcache-优化CPU
说明 PHP 5.5+版本以上的,可以使用PHP自带的opcache开启性能加速(默认是关闭的).对于PHP 5.5以下版本的,需要使用APC加速,这里不说明,可以自行上网搜索PHP APC加速的方法 ...
- 智行火车票免费加速到VIP最高速抢票(不用朋友积攒或者购买加速包)
更新: 2018.11.07, 昨天我买火车票,已经不行了,这个bug已经没有了,被修复了, 望大家知悉!!! 智行火车票免费加速到VIP最高速抢票(不用朋友积攒或者购买加速包) 1)下过单后选择抢到 ...
- 性能加速 - 开启opcache
说明 PHP 5.5+版本以上的,可以使用PHP自带的opcache开启性能加速(默认是关闭的).对于PHP 5.5以下版本的,需要使用APC加速,这里不说明,可以自行上网搜索PHP APC加速的方法 ...
- 50 个加速包都抢不到车票,还不如这个 Python 抢票神器!
又到了一年一度的抢票大战,本来就辛苦劳累了一年,想着可以早点订到票跟家里人团聚.所以有挺多的人,宁愿多花些钱去找黄牛买票.但今年各种抢票软件的横行,还有官方出的加速包,导致连黄牛都不敢保证能买到票.你 ...
- 【python基础】Python性能加速的方法
参考 1. 给Python加速(性能加速的方法); 2. pythonSpeed_PerformanceTips; 完
随机推荐
- webpack性能优化(2):splitChunks用法详解
之前写的<webpack性能优化(0):webpack性能优化概况-优化构建速度>.<webpack性能优化(1):分隔/分包/异步加载+组件与路由懒加载> 如果使用vue-c ...
- Snack3 3.1.10的新特性及应用
<dependency> <groupId>org.noear</groupId> <artifactId>snack3</artifactId& ...
- python 升级后 yum 无法使用 File "/usr/bin/yum", line 30 except KeyboardInterrupt, e: `/usr/libexec/urlgrabber-ext-down`
原因为升级python后新建了软连接指向了新版本,除非同时升级yum不然无法使用.需要手动更改报错文件指向新版本后即可解决. [root@localhost pdserving]# yum insta ...
- Axure 母版与元件
需要重复使用的元件,建议创建成母版: 如果修改了母版,所有页面中的母版元件将会被同步修改 元件:添加后,所有的 Axure 都可以使用 母版:只适用当前的 Axure 原型 拖放行为: 任意位置:可以 ...
- Java 四种引用类型(强引用、软引用、弱引用、虚引用)
概述 Java 中的引用类似 C 语言中的指针,指向一个对象,比如: // person 就是指向 Person 实例"张三"的引用 Person person = new Per ...
- 初识QT、窗口以及信号槽
1 基本规范: 无论是写什么样的代码,第一步都应该是创建一个程序对象 #include <QApplication> int main(int argc, char *argv[]) { ...
- 【计算机网络】WebSocket 是什么原理?为什么可以实现持久连接?
一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接),循环连接的不算) 首先HTTP有1.1和1.0之说,也就是所谓的 ...
- AtCoder Beginner Contest 208 A~E个人题解
比赛链接:Here A - Rolling Dice 水题 一个六面的骰子,请问摇动 \(A\) 次最后的点数和能否为 \(B\) 如果 \(B \in [a,6a]\) 输出 YES C++ voi ...
- Educational Codeforces Round 98 (Rated for Div. 2) (A - E题题解)
呼,熬过一场考试,补下题吧 A. Robot Program 在一个二维无限方格中,初始时你在格子里,每秒你有5种决策:选择移动到上下左右四个格子中的一个或者停留在原地.你不能连续两秒做相同的决策,问 ...
- 图解 Promise 实现原理(二)—— Promise 链式调用
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/Xz2bGaLxVL4xw1M2hb2nJQ作者:Morrain 很多同学在学习 Promis ...