Spring Boot整合Swing部署外部Tomcat遇HeadlessException等Bean异常
解决Spring Boot+Swing打包War部署外部Tomcat的HeadlessException问题
你这踩了个Spring Boot整合Swing的典型坑——外部Tomcat默认以Headless模式运行,而你的Rest Controller同时继承了JFrame,Spring启动时会自动实例化这个Bean,直接触发图形环境缺失的异常,而且你之前在main方法里设置的builder.headless(false)在War部署场景下根本不会生效,因为这时候Spring是通过SpringBootServletInitializer启动的,main方法压根不会被执行。
下面是一步步的具体解决方案:
1. 分离Swing组件与Rest Controller(核心修复)
你的TLOG_Backup_Report同时是@RestController和JFrame,Spring容器启动时会自动实例化这个Bean,而外部Tomcat没有图形环境,直接就会抛出HeadlessException。正确的做法是把Swing的UI逻辑抽成独立类,不要让Spring管理它的实例,只在请求触发时才动态创建。
重构后的代码示例:
独立的Swing UI类
import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Vector; @SuppressWarnings("serial") class SpringTable1 extends JTable { public SpringTable1(DefaultTableModel dm) { super(dm); setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); } public SpringTable1() { setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); } @Override public void doLayout() { int width = getWidth(); int columnCount = getColumnCount(); int columnSize = width / columnCount; for (int index = 0; index < columnCount; index++) { TableColumn column = getColumnModel().getColumn(index); column.setResizable(true); column.setPreferredWidth(width); column.setMinWidth(100); } super.doLayout(); } @Override public boolean getScrollableTracksViewportWidth() { return true; } } // 独立的Swing窗口类,不要被Spring容器管理 class TlogReportFrame extends JFrame { public TlogReportFrame(DefaultTableModel tableModel) { SpringTable1 table = new SpringTable1(tableModel); table.setRowHeight(40); setTitle("TLOG Backup Report"); setSize(1000, 1000); setResizable(true); setLocationRelativeTo(null); getContentPane().add(new JScrollPane(table)); setVisible(true); } }
修改后的Rest Controller
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.sql.*; import java.util.Vector; import javax.swing.table.DefaultTableModel; @RestController public class TLOG_Backup_Report { @RequestMapping("/tlog") public String getTlogStatusReport() throws ClassNotFoundException, SQLException { // 1. 先处理数据库查询逻辑 Class.forName("你的数据库驱动类"); String dbURL = "你的数据库连接URL"; // 使用try-with-resources自动关闭资源 try (Connection con = DriverManager.getConnection(dbURL); CallableStatement proc_stmt = con.prepareCall("你的存储过程调用语句"); ResultSet rs = proc_stmt.executeQuery()) { DefaultTableModel tableModel = buildTableModel(rs); // 2. 动态关闭Headless模式,再创建Swing窗口 System.setProperty("java.awt.headless", "false"); new TlogReportFrame(tableModel); return "Results are displayed in the Swing window"; } } public static DefaultTableModel buildTableModel(ResultSet rs) throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); Vector<String> columnNames = new Vector<>(); int columnCount = metaData.getColumnCount(); for (int column = 1; column <= columnCount; column++) { columnNames.add(metaData.getColumnName(column)); } Vector<Vector<Object>> data = new Vector<>(); while (rs.next()) { Vector<Object> vector = new Vector<>(); for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) { vector.add(rs.getObject(columnIndex)); } data.add(vector); } return new DefaultTableModel(data, columnNames); } }
2. 正确配置Spring Boot启动类的Headless参数
打包成War后,Spring是通过SpringBootServletInitializer的configure方法启动的,原来main方法里的设置不会生效,所以要在configure方法里补充配置:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan({"service.my.rest.app"}) public class ConnectionTest extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplicationBuilder builder = new SpringApplicationBuilder(ConnectionTest.class); builder.headless(false); builder.run(args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { // War部署时,这里的配置才会生效 return application.sources(ConnectionTest.class).headless(false); } }
3. 配置外部Tomcat允许图形环境
即使代码里设置了,有些Tomcat环境还是会强制Headless,这时候需要修改Tomcat的启动参数:
- 找到Tomcat目录下的
bin/catalina.bat(Windows)或bin/catalina.sh(Linux) - 在文件开头添加:
# Linux/Mac CATALINA_OPTS="-Djava.awt.headless=false" # Windows set CATALINA_OPTS=-Djava.awt.headless=false - 或者在Tomcat的管理控制台(如果有)添加JVM启动参数
-Djava.awt.headless=false
为什么原来的设置没用?
- War部署时,外部Tomcat是容器,Spring Boot应用作为Web应用被加载,
main方法不会被执行,所以你在main里的builder.headless(false)完全没起作用。 - 即使你在configure里设置了headless=false,只要Spring容器初始化时要创建继承JFrame的Controller Bean,就会在Tomcat的Headless环境下尝试创建图形组件,直接抛出异常——这也是为什么必须分离Swing组件和Controller的核心原因。
内容的提问来源于stack exchange,提问作者Vallapu Charan
相关产品推荐
相关产品推荐

