如何修复IDEA GUI Designer二进制类文件选项无法生效问题
运行自行开发的Swing GUI应用jar包时抛出如下异常:
Exception in thread "main" java.awt.IllegalComponentStateException: contentPane cannot be set to null. at java.desktop/javax.swing.JRootPane.setContentPane(JRootPane.java:594) at java.desktop/javax.swing.JDialog.setContentPane(JDialog.java:1036) at ui.ConnectToServerDialog.<init>(ConnectToServerDialog.java:22) at common.Client.main(Client.java:14)
该异常出现在使用IDEA内置Swing UI Designer创建界面、打包运行的场景。网上多数方案建议将GUI设计器生成选项切换为「Java源代码」,但该模式会将UI生成代码混入业务代码,维护体验差,目标是让「二进制类文件(binary class files)」生成模式正常工作,不修改生成策略也能正常打包运行。
选择「二进制类文件」模式时,IDEA不会把UI组件初始化的逻辑直接插入Java源代码,而是在编译阶段通过自带的构建任务给class文件注入UI初始化的字节码。如果打包流程没有触发这个注入逻辑,运行时$$$setupUI$$$等初始化方法就不存在,绑定的contentPane等组件会是null,最终抛出上述异常。
1. 确认IDEA全局GUI设计器配置
打开IDEA设置,进入Editor > GUI Designer页,确认配置:
- 「Generate GUI into」选择Binary class files
- 勾选
Automatically copy form runtime classes to the output directory
2. 按构建方式配置打包流程
方式一:使用IDEA自带Artifact打包
- 打开
Project Structure > Artifacts,新建对应Jar包的Artifact配置 - 在「Output Layout」页,确认已经把项目编译输出加入包结构,同时必须勾选
Include in project build选项 - 不要直接用Maven/Gradle的普通package任务打包,直接用IDEA的
Build > Build Artifacts动作构建,构建时IDEA会自动注入UI初始化的字节码
方式二:使用Maven构建
在pom.xml中加入IDEA GUI Designer的Maven插件,配置编译阶段自动注入字节码:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>ideauidesigner-maven-plugin</artifactId> <version>1.0-beta-1</version> <executions> <execution> <goals> <goal>javac2</goal> </goals> </execution> </executions> <configuration> <fork>true</fork> <debug>true</debug> <failOnError>true</failOnError> </configuration> </plugin> </plugins> </build>
同时引入forms运行时依赖:
<dependency> <groupId>com.intellij</groupId> <artifactId>forms_rt</artifactId> <version>7.0.3</version> </dependency>
配置完成后直接执行mvn package即可,插件会在编译时自动完成字节码注入,不需要修改源码生成策略。
方式三:使用Gradle构建
引入IDEA的GUI Designer Gradle插件,在build.gradle中添加配置:
plugins { id 'java' id 'org.jetbrains.intellij' version '1.13.3' } intellij { version = '2023.1' // 替换为本地使用的IDEA版本 } dependencies { implementation "com.intellij:forms_rt:231.8109.175" } compileJava { doLast { ant.taskdef(name: 'javac2', classname: 'com.intellij.ant.Javac2', classpath: configurations.compileClasspath.asPath) ant.javac2(srcdir: sourceSets.main.java.srcDirs, destdir: sourceSets.main.java.outputDir, classpath: configurations.compileClasspath.asPath) } }
执行gradle build即可完成带字节码注入的打包。
打包完成后,可以解压jar包,查看对应UI类(比如报错的ConnectToServerDialog)的class文件,若存在$$$setupUI$$$方法,说明字节码注入成功,运行时就不会出现contentPane为null的异常。
注意:不要混用不同版本的IDEA和构建插件版本,否则可能出现字节码注入失败、版本不兼容的问题。
内容的提问来源于stack exchange,提问作者David Blbulyan

