Helidon升级Hibernate至6.6.23.Final后Native Image的ByteBuddy问题
将Helidon框架中的Hibernate从6.3.1.Final版本升级至6.6.23.Final版本后,构建Native Image时出现新错误:多个ByteBuddy相关类本应在运行时初始化,却在镜像构建阶段被意外初始化。
我们已通过org.hibernate.orm.tooling:hibernate-enhance-maven-plugin在构建时增强实体,认为无需运行时ByteBuddy增强。
尝试在native-image.properties中添加-H:ServiceLoaderFeatureExcludeServices=org.hibernate.bytecode.spi.BytecodeProvider后,又出现ServiceConfigurationError,提示找不到BytecodeProviderImpl。
现提出两个问题:
- 为何不需要ByteBuddy却仍被加载?
- 如何解决该问题?
重现步骤
git clone https://github.com/helidon-io/helidon.git cd helidon # 更新dependencies/pom.xml中的版本 # <version.lib.hibernate.family>6.6</version.lib.hibernate.family> # <version.lib.hibernate>${version.lib.hibernate.family}.23.Final</version.lib.hibernate mvn clean install -DskipTests cd tests/integration/packaging/mp-2 mvn clean verify -Pnative-image
1. 为何不需要ByteBuddy却仍被加载?
Hibernate 6.6.x版本中,即便启用了构建时实体增强,框架核心逻辑仍默认通过ServiceLoader机制自动加载BytecodeProvider的实现类(ByteBuddyBytecodeProvider)。这是因为Hibernate的基础架构依赖ServiceLoader来发现字节码提供者,无论是否使用运行时增强,启动阶段都会尝试加载该服务实现。此外,部分Hibernate内部组件可能隐含依赖ByteBuddy的API,导致其被拉入Native Image的构建流程。
2. 如何解决该问题?
不能直接排除整个BytecodeProvider服务(Hibernate必须要有一个字节码提供者实例),正确做法是指定使用无操作字节码提供者(NoopBytecodeProvider),同时确保构建时增强的实体被正确识别,避免运行时触发ByteBuddy初始化:
配置Hibernate使用NoopBytecodeProvider
在persistence.xml中添加配置:<property name="hibernate.bytecode.provider" value="noop"/>或是在Helidon MP的application.properties中添加:
hibernate.bytecode.provider=noop调整Native Image配置
无需排除BytecodeProvider服务,而是确保NoopBytecodeProvider在构建阶段初始化,同时将ByteBuddy相关类推迟到运行时初始化。在native-image.properties中添加:--initialize-at-build-time=org.hibernate.bytecode.internal.none.NoopBytecodeProvider --initialize-at-run-time=net.bytebuddy,org.hibernate.bytecode.internal.bytebuddy验证构建时增强配置
确认hibernate-enhance-maven-plugin的配置覆盖所有实体增强需求,示例配置:<plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>${version.lib.hibernate}</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>enhance</goal> </goals> </execution> </executions> <configuration> <enableLazyInitialization>true</enableLazyInitialization> <enableDirtyTracking>true</enableDirtyTracking> <enableAssociationManagement>true</enableAssociationManagement> </configuration> </plugin>
通过以上步骤,Hibernate会使用无操作字节码提供者规避运行时ByteBuddy依赖,同时Native Image构建时不会触发ByteBuddy类的初始化,从而解决构建错误。
内容的提问来源于stack exchange,提问作者ravenskater

