Springboot项目启动前,使用GUI做初始化配置页面并将Log4j2的日志实时显示在GUI上
效果预览
Mac Os效果图
Windows 10 效果图
需求分析
做这样的一个功能并不适用于所有系统,主要用于交付给用户的产品,这样方便客户自行维护。传统的服务一般都是集群部署在Linux,有专业的运维工程师去维护,对于局域网内单机部署在Windows的情况,此功能特别实用。客户并不具备用专业的技术去部署去配置,所以用一套傻瓜式的安装、配置、启动等显得尤为重要。
代码实现
主面板
/**
* 主面板
* @author YoungJ
*/
@Slf4j
public class MainPanel {
public static JPanel mainPanel;
public static EpilepsyDbConfig epilepsyDbConfig = readEpilepsyDbConfigFromProperties();
public static GdDbConfig gdDbConfig = readGdDbConfigFromProperties();
public static SysConfig sysConfig = readSysConfigFromProperties();
/**
* 左侧配置面板宽度
*/
public static final int CONFIG_PANEL_WIDTH = 300;
/**
* 日志面板宽度
*/
public static final int LOG_PANEL_WIDTH = 850;
/**
* 系统数据库面板起点坐标
*/
public static final int[] EPILEPSDB_PANEL_START_COOR = {5, 0};
/**
* 系统数据库面板高度
*/
public static final int EPILEPSDB_PANEL_HEIGHT = 190;
/**
* 光电数据库面板起点坐标
*/
public static final int[] GDDB_PANEL_START_COOR = {5, 200};
/**
* 光电数据库面板高度
*/
public static final int GDDB_PANEL_HEIGHT = 150;
/**
* 证书面板起点坐标
*/
public static final int[] CERT_PANEL_START_COOR = {5, 360};
/**
* 证书面板高度
*/
public static final int CERT_PANEL_HEIGHT = 160;
/**
* 服务面板起点坐标
*/
public static final int[] SERVER_PANEL_START_COOR = {5, 530};
/**
* 服务面板高度
*/
public static final int SERVER_PANEL_HEIGHT = 140;
/**
* 日志面板起点坐标
*/
public static final int[] LOG_PANEL_START_COOR = {310, 0};
/**
* 日志面板高度
*/
public static final int LOG_PANEL_HEIGHT = 670;
public MainPanel() {
mainPanel = new JPanel();
mainPanel.setLayout(null);
EpilepsysDbPanel.buildJpanel(mainPanel);
GdDbPanel.buildJpanel(mainPanel);
CertPanel.buildJpanel(mainPanel);
ServerPanel.buildJpanel(mainPanel);
LogPanel.buildJpanel(mainPanel);
buildFrame(mainPanel);
}
protected static JButton buildJButton(String name, int x, int y, int width, int height) {
JButton button = new JButton(name);
button.setBounds(x, y, width, height);
return button;
}
protected static JRadioButton buildJRadioButton(String name, int x, int y, int width, int height) {
JRadioButton button = new JRadioButton(name);
button.setBounds(x, y, width, height);
return button;
}
// 文本框
protected static JTextField buildJTextField(String value, String name, int columns, int x, int y, int width, int height) {
JTextField jtf = new JTextField(columns);
jtf.setText(value);
jtf.setName(name);
jtf.setBounds(x, y, width, height);
return jtf;
}
// 密码框
protected static JPasswordField buildJPasswordField(String value, String name, int columns, int x, int y, int width, int height) {
JPasswordField jtf = new JPasswordField(columns);
jtf.setText(value);
jtf.setName(name);
jtf.setBounds(x, y, width, height);
return jtf;
}
protected static JLabel buildJLabel(String name, int x, int y, int width, int height) {
JLabel label = new JLabel(name);
label.setBounds(x, y, width, height);
return label;
}
protected static JLabel buildJBorder(String name, int x, int y, int width, int height) {
JLabel label = new JLabel();
label.setBounds(x, y, width, height);
label.setBorder(BorderFactory.createTitledBorder(name));
return label;
}
private static void buildFrame(JComponent component) {
JFrame frame = new JFrame("癫痫数据管理系统");
component.setBounds(0, 0, 1055, 730);
frame.add(component);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().setLayout(null);
frame.getContentPane().add(BorderLayout.CENTER, component);
// 设置窗口最小尺寸
frame.setMinimumSize(new Dimension(1065, 730));
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int result = JOptionPane.showConfirmDialog(frame, "关闭窗口服务将停止运行,确定要关闭吗?", "关闭确认", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
// 如果用户点击是,执行关闭操作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 关闭时同时停止 Spring Boot
ServerPanel.taskStop();
saveConfigToFile();
}
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// frame.setResizable(false);
}
private static EpilepsyDbConfig readEpilepsyDbConfigFromProperties() {
EpilepsyDbConfig config = new EpilepsyDbConfig();
Properties properties = loadProperties();
config.setDatabase(properties.getProperty("epilepsysys.db.database"));
config.setUrl(properties.getProperty("epilepsysys.db.url"));
config.setPort(Integer.parseInt(properties.getProperty("epilepsysys.db.port")));
config.setUsername(properties.getProperty("epilepsysys.db.username"));
config.setPassword(properties.getProperty("epilepsysys.db.password"));
return config;
}
private static GdDbConfig readGdDbConfigFromProperties() {
GdDbConfig config = new GdDbConfig();
Properties properties = loadProperties();
config.setDatabase(properties.getProperty("gd.db.database"));
config.setUrl(properties.getProperty("gd.db.url"));
config.setUsername(properties.getProperty("gd.db.username"));
config.setPassword(properties.getProperty("gd.db.password"));
return config;
}
private static SysConfig readSysConfigFromProperties() {
SysConfig config = new SysConfig();
Properties properties = loadProperties();
config.setCertPath(properties.getProperty("spring.config.import"));
config.setAutoStart(properties.getProperty("sys.auto.start"));
config.setZipName(properties.getProperty("sys.license.name"));
return config;
}
private static Properties loadProperties() {
Properties properties = new Properties();
String filePath = System.getProperty("user.dir") + File.separator + "config/application.properties";
try (InputStream input = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)));) {
properties.load(input);
} catch (IOException e) {
e.printStackTrace();
}
return properties;
}
public static void saveConfigToFile() {
try {
EpilepsysDbPanel.updateConfigFromGui();
GdDbPanel.updateConfigFromGui();
String filePath = System.getProperty("user.dir") + File.separator + "config/application.properties";
Properties properties = new Properties();
properties.setProperty("epilepsysys.db.database", epilepsyDbConfig.getDatabase());
properties.setProperty("epilepsysys.db.url", epilepsyDbConfig.getUrl());
properties.setProperty("epilepsysys.db.port", String.valueOf(epilepsyDbConfig.getPort()));
properties.setProperty("epilepsysys.db.username", epilepsyDbConfig.getUsername());
properties.setProperty("epilepsysys.db.password", epilepsyDbConfig.getPassword());
properties.setProperty("gd.db.database", gdDbConfig.getDatabase());
properties.setProperty("gd.db.url", gdDbConfig.getUrl());
properties.setProperty("gd.db.username", gdDbConfig.getUsername());
properties.setProperty("gd.db.password", gdDbConfig.getPassword());
properties.setProperty("sys.auto.start", sysConfig.getAutoStart() == null ? "false" : sysConfig.getAutoStart());
properties.setProperty("spring.config.import", sysConfig.getCertPath() == null ? "" : sysConfig.getCertPath());
properties.setProperty("sys.license.name", sysConfig.getZipName() == null ? "" : sysConfig.getZipName());
try (OutputStream output = Files.newOutputStream(Paths.get(filePath))) {
properties.store(output, "Updated configuration");
}
} catch (IOException e) {
Toolkit.getDefaultToolkit().beep();
}
}
public static void disableConfigFields() {
EpilepsysDbPanel.disableConfigFields();
GdDbPanel.disableConfigFields();
CertPanel.disableConfigFields();
ServerPanel.disableConfigFields();
}
public static void enableConfigFields() {
EpilepsysDbPanel.enableConfigFields();
GdDbPanel.enableConfigFields();
CertPanel.enableConfigFields();
ServerPanel.enableConfigFields();
}
}
系统数据库配置面板
/**
* 系统数据库配置面板
* @author YoungJ
*/
@Slf4j
public class EpilepsysDbPanel {
private static JTextField dbNameTextField;
private static JTextField urlTextField;
private static JTextField portTextField;
private static JTextField usernameTextField;
private static JPasswordField passwordTextField;
private static final int y0 = MainPanel.EPILEPSDB_PANEL_START_COOR[1];
private static final int x0 = MainPanel.EPILEPSDB_PANEL_START_COOR[0];
private static EpilepsyDbConfig epilepsyDbConfig = MainPanel.epilepsyDbConfig;
public static void buildJpanel(JPanel panel) {
String dbName = epilepsyDbConfig.getDatabase();
String url = epilepsyDbConfig.getUrl();
String username = epilepsyDbConfig.getUsername();
Integer port = epilepsyDbConfig.getPort();
String password = epilepsyDbConfig.getPassword();
panel.add(MainPanel.buildJBorder("系统数据库配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.EPILEPSDB_PANEL_HEIGHT));
// 数据库
panel.add(MainPanel.buildJLabel("数据库:", x0 + 15, y0 + 40, 80, 25));
dbNameTextField = MainPanel.buildJTextField(dbName, "database", 20, 100, y0 + 40, 165, 25);
panel.add(dbNameTextField);
// URL
panel.add(MainPanel.buildJLabel("URL:", x0 + 15, y0 + 70, 80, 25));
urlTextField = MainPanel.buildJTextField(url, "url", 20, 100, y0 + 70, 165, 25);
panel.add(urlTextField);
// 端口
panel.add(MainPanel.buildJLabel("端口:", x0 + 15, y0 + 100, 80, 25));
portTextField = MainPanel.buildJTextField(port + "", "port", 20, 100, y0 + 100, 165, 25);
panel.add(portTextField);
// 用户名
panel.add(MainPanel.buildJLabel("用户名:", x0 + 15, y0 + 130, 80, 25));
usernameTextField = MainPanel.buildJTextField(username, "username", 20, 100, y0 + 130, 165, 25);
panel.add(usernameTextField);
// 密码
panel.add(MainPanel.buildJLabel("密码:", x0 + 15, y0 + 160, 80, 25));
passwordTextField = MainPanel.buildJPasswordField(password, "password", 20, 100, y0 + 160, 165, 25);
panel.add(passwordTextField);
}
// 为按钮绑定监听
private static void addActionListener(JButton saveButton) {
saveButton.addActionListener(
e -> updateConfigFromGui());
}
public static void updateConfigFromGui() {
epilepsyDbConfig.setDatabase(dbNameTextField.getText());
epilepsyDbConfig.setUrl(urlTextField.getText());
epilepsyDbConfig.setPort(Integer.parseInt(portTextField.getText()));
epilepsyDbConfig.setUsername(usernameTextField.getText());
epilepsyDbConfig.setPassword(new String(passwordTextField.getPassword()));
}
public static void disableConfigFields() {
dbNameTextField.setEnabled(false);
urlTextField.setEnabled(false);
portTextField.setEnabled(false);
usernameTextField.setEnabled(false);
passwordTextField.setEnabled(false);
}
public static void enableConfigFields() {
dbNameTextField.setEnabled(true);
urlTextField.setEnabled(true);
portTextField.setEnabled(true);
usernameTextField.setEnabled(true);
passwordTextField.setEnabled(true);
}
}
其他数据库配置面板
/**
* 光电数据库配置面板
* @author YoungJ
*/
public class GdDbPanel {
private static JTextField gdDbNameTextField;
private static JTextField gdUrlTextField;
private static JTextField gdUserNameTextField;
private static JPasswordField gdPasswordTextField;
private static final int y0 = MainPanel.GDDB_PANEL_START_COOR[1];
private static final int x0 = MainPanel.GDDB_PANEL_START_COOR[0];
private static GdDbConfig gdDbConfig = MainPanel.gdDbConfig;
public static void buildJpanel(JPanel panel) {
String dbName = gdDbConfig.getDatabase();
String url = gdDbConfig.getUrl();
String username = gdDbConfig.getUsername();
String password = gdDbConfig.getPassword();
//添加服务器配置区
panel.add(MainPanel.buildJBorder("光电数据库配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.GDDB_PANEL_HEIGHT));
//数据库
panel.add(MainPanel.buildJLabel("数据库:", x0 + 15, y0 + 30, 80, 25));
gdDbNameTextField = MainPanel.buildJTextField(dbName, "database", 20, 100, y0 + 30, 165, 25);
panel.add(gdDbNameTextField);
//URL
panel.add(MainPanel.buildJLabel("URL:", x0 + 15, y0 + 60, 80, 25));
gdUrlTextField = MainPanel.buildJTextField(url, "url", 20, 100, y0 + 60, 165, 25);
panel.add(gdUrlTextField);
//用户名
panel.add(MainPanel.buildJLabel("用户名:", x0 + 15, y0 + 90, 80, 25));
gdUserNameTextField = MainPanel.buildJTextField(username, "username", 20, 100, y0 + 90, 165, 25);
panel.add(gdUserNameTextField);
//密码
panel.add(MainPanel.buildJLabel("密码:", x0 + 15, y0 + 120, 80, 25));
gdPasswordTextField = MainPanel.buildJPasswordField(password, "password", 20, 100, y0 + 120, 165, 25);
panel.add(gdPasswordTextField);
}
// 为按钮绑定监听
private static void addActionListener(JButton saveButton) {
saveButton.addActionListener(e -> {
if (Objects.equals(saveButton.getText(), "保存")) {
updateConfigFromGui();
}
});
}
// save event
public static void updateConfigFromGui() {
gdDbConfig.setDatabase(gdDbNameTextField.getText());
gdDbConfig.setUrl(gdUrlTextField.getText());
gdDbConfig.setUsername(gdUserNameTextField.getText());
gdDbConfig.setPassword(new String(gdPasswordTextField.getPassword()));
}
public static void disableConfigFields() {
gdDbNameTextField.setEnabled(false);
gdUrlTextField.setEnabled(false);
gdUserNameTextField.setEnabled(false);
gdPasswordTextField.setEnabled(false);
}
public static void enableConfigFields() {
gdDbNameTextField.setEnabled(true);
gdUrlTextField.setEnabled(true);
gdUserNameTextField.setEnabled(true);
gdPasswordTextField.setEnabled(true);
}
}
证书配置面板
/**
* 证书配置面板
*
* @author YoungJ
*/
@Slf4j
public class CertPanel {
private static ImageIcon insertedIcon;
private static ImageIcon notInsertedIcon;
private static JLabel statusLabel;
private static JLabel licenseLabel;
private static JLabel licenseInfoLable;
private static JLabel licenseDescLabel;
private static JButton chooseButton;
private static JLabel validLabel;
private static JLabel failLabel;
private static final int y0 = MainPanel.CERT_PANEL_START_COOR[1];
private static final int x0 = MainPanel.CERT_PANEL_START_COOR[0];
private static final String basePath = System.getProperty("user.dir");
public static void buildJpanel(JPanel panel) {
// 配置区
panel.add(MainPanel.buildJBorder("证书配置", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.CERT_PANEL_HEIGHT));
insertedIcon = createStatusIcon(true);
notInsertedIcon = createStatusIcon(false);
panel.add(MainPanel.buildJLabel("加密锁:", x0 + 15, y0 + 30, 80, 25));
// 证书按钮
panel.add(MainPanel.buildJLabel("选择证书:", x0 + 15, y0 + 60, 80, 25));
chooseButton = MainPanel.buildJButton("选择…", x0 + 100, y0 + 60, 80, 25);
addActionListener(chooseButton, panel);
panel.add(chooseButton);
String certPath = MainPanel.sysConfig.getCertPath();
if (StringUtils.isNotBlank(certPath)) {
// 新增状态显示组件
JPanel licensePanel = new JPanel();
licensePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));
licenseLabel = new JLabel(insertedIcon); // 图标
licenseLabel.setText("已安装"); // 文本
licensePanel.add(licenseLabel);
licensePanel.setBounds(x0 + 220, y0 + 60, 80, 20);
panel.add(licensePanel);
if (checkFileExist("license.properties")
&& checkFileExist("license.lic")
&& checkFileExist("publicCerts.keystore")) {
chooseButton.setText("更换…");
panel.add(licenseDescLabel = MainPanel.buildJLabel("证书详情:", x0 + 15, y0 + 100, 80, 20));
panel.add(licenseInfoLable = MainPanel.buildJLabel(MainPanel.sysConfig.getZipName(), x0 + 100, y0 + 100, 165, 20));
}
} else {
JPanel licensePanel = new JPanel();
licensePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));
licenseLabel = new JLabel(notInsertedIcon); // 图标
licenseLabel.setText("未安装"); // 文本
licensePanel.add(licenseLabel);
licensePanel.setBounds(x0 + 220, y0 + 60, 80, 20);
panel.add(licensePanel);
}
// 新增状态显示组件
JPanel statusPanel = new JPanel();
statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));
statusLabel = new JLabel(notInsertedIcon); // 图标
statusLabel.setText("未检测"); // 文本
setDongleStatus(DongleManager.isLicenseInserted());
statusPanel.add(statusLabel);
statusPanel.setBounds(x0 + 220, y0 + 30, 80, 20);
panel.add(statusPanel);
// 初始化 USB 检查定时器,每隔一段时间检查一次状态
Timer dongleCheckTimer = new Timer(30000, e -> setDongleStatus(DongleManager.isLicenseInserted()));
dongleCheckTimer.start();
}
// 为按钮绑定监听
private static void addActionListener(JButton button, JPanel panel) {
button.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".zip") || f.isDirectory();
}
@Override
public String getDescription() {
return "ZIP Files (*.zip)";
}
});
int result = fileChooser.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
extractZip(selectedFile, panel);
}
});
}
private static void extractZip(File zipFile, JPanel panel) {
try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile.toPath()))) {
ZipEntry entry;
if (licenseDescLabel == null) {
panel.add(licenseDescLabel = MainPanel.buildJLabel("证书详情", x0 + 15, y0 + 100, 80, 25));
}
while ((entry = zipInputStream.getNextEntry()) != null) {
if (!entry.isDirectory()) {
Path outputPath = Paths.get(basePath, entry.getName());
Files.copy(zipInputStream, outputPath, StandardCopyOption.REPLACE_EXISTING);
}
}
if (licenseInfoLable == null) {
panel.add(licenseInfoLable = MainPanel.buildJLabel(zipFile.getName(), x0 + 100, y0 + 100, 80, 25));
} else {
licenseInfoLable.setText(zipFile.getName());
}
chooseButton.setText("更换…");
panel.updateUI();
MainPanel.sysConfig.setCertPath("file:" + basePath + File.separator + "license.properties");
MainPanel.sysConfig.setZipName(zipFile.getName());
setLicenseStatus(true);
MainPanel.saveConfigToFile();
JOptionPane.showMessageDialog(null, "安装成功!");
} catch (IOException e) {
log.error(e.getMessage());
JOptionPane.showMessageDialog(null, "安装失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
}
}
private static boolean checkFileExist(String fileName) {
Path filePath = Paths.get(basePath, fileName);
if (Files.exists(filePath)) {
return true;
} else {
return false;
}
}
public static void addCertLabel() {
if (LicenseInfo.VERIFY_SUCCESS) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String notBefore = sdf.format(LicenseInfo.notBefore);
String notAfter = sdf.format(LicenseInfo.notAfter);
MainPanel.mainPanel.add(validLabel = MainPanel.buildJLabel("有效期至: " + notAfter, x0 + 15, y0 + 130, 250, 20));
validLabel.setToolTipText(notBefore + " 至 " + notAfter);
} else {
MainPanel.mainPanel.add(validLabel = MainPanel.buildJLabel("失败原因: " + String.join(",", LicenseInfo.FAIL_MSG), x0 + 15, y0 + 130, 250, 20));
validLabel.setToolTipText("失败原因:" + String.join(",", LicenseInfo.FAIL_MSG));
}
MainPanel.mainPanel.updateUI();
}
public static void removeCertLabel() {
if (validLabel != null) {
MainPanel.mainPanel.remove(validLabel);
}
if (failLabel != null) {
MainPanel.mainPanel.remove(failLabel);
}
MainPanel.mainPanel.updateUI();
}
public static void disableConfigFields() {
chooseButton.setEnabled(false);
}
public static void enableConfigFields() {
chooseButton.setEnabled(true);
}
// 用于设置插入状态
private static void setDongleStatus(boolean status) {
if (status) {
statusLabel.setIcon(insertedIcon);
statusLabel.setText("已插入");
} else {
statusLabel.setIcon(notInsertedIcon);
statusLabel.setText("未插入");
}
}
// 用于设置证书安装状态
private static void setLicenseStatus(boolean status) {
if (status) {
licenseLabel.setIcon(insertedIcon);
licenseLabel.setText("已安装");
} else {
licenseLabel.setIcon(notInsertedIcon);
licenseLabel.setText("未安装");
}
}
// 新增方法用于创建状态图标
private static ImageIcon createStatusIcon(boolean status) {
int size = 12; // 调整图标大小
ImageIcon icon = new ImageIcon(new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB));
Graphics g = icon.getImage().getGraphics();
if (status) {
g.setColor(Color.GREEN);
} else {
g.setColor(Color.GRAY);
}
g.fillOval(0, 0, size, size);
g.dispose();
return icon;
}
}
服务管理面板
/**
* 服务面板
* @author YoungJ
*/
public class ServerPanel {
private static JButton stopBtn;
private static JButton startBtn;
private static JRadioButton yesRadioBtn;
private static JRadioButton noRadioBtn;
private static PrintStream consolePrintStream;
private static JLabel statusLabel;
// 使用单线程的 ExecutorService
public static ExecutorService executorService = Executors.newSingleThreadExecutor();
private static final int y0 = MainPanel.SERVER_PANEL_START_COOR[1];
private static final int x0 = MainPanel.SERVER_PANEL_START_COOR[0];
private enum StartupStatus {
STARTING, STARTED, NOT_STARTED
}
private static ImageIcon startingIcon;
private static ImageIcon startedIcon;
private static ImageIcon notStartedIcon;
public static JPanel buildJpanel(JPanel panel) {
panel.add(MainPanel.buildJBorder("服务管理", x0, y0 + 10, MainPanel.CONFIG_PANEL_WIDTH, MainPanel.SERVER_PANEL_HEIGHT));
// 初始化图标
startingIcon = createStatusIcon(StartupStatus.STARTING);
startedIcon = createStatusIcon(StartupStatus.STARTED);
notStartedIcon = createStatusIcon(StartupStatus.NOT_STARTED);
// 新增状态显示组件
JPanel statusPanel = new JPanel(); // 创建容器用于放置图标和文本
statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); // 设置容器布局
statusLabel = new JLabel(notStartedIcon); // 图标
statusLabel.setText("未启动"); // 文本
statusPanel.add(statusLabel);
statusPanel.setBounds(x0 + 220, y0 + 30, 80, 20);
panel.add(statusPanel);
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
panel.add(MainPanel.buildJLabel(osName + " " + osArch, x0 + 10, y0 + 30, 200, 20));
panel.add(MainPanel.buildJLabel("自动启动:", x0 + 10, y0 + 60, 80, 20));
panel.add(yesRadioBtn = MainPanel.buildJRadioButton("是", x0 + 90, y0 + 60, 50, 20));
panel.add(noRadioBtn = MainPanel.buildJRadioButton("否", x0 + 150, y0 + 60, 50, 20));
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(yesRadioBtn);
buttonGroup.add(noRadioBtn);
if ("true".equals(MainPanel.sysConfig.getAutoStart())) {
buttonGroup.setSelected(yesRadioBtn.getModel(), true);
taskStart();
} else {
buttonGroup.setSelected(noRadioBtn.getModel(), true);
}
addActionListener(yesRadioBtn);
addActionListener(noRadioBtn);
// 开始按钮
startBtn = MainPanel.buildJButton("启动", x0 + 50, y0 + 100, 80, 25);
addActionListener(startBtn);
panel.add(startBtn);
//添加停止上报按钮
stopBtn = MainPanel.buildJButton("停止", x0 + 150, y0 + 100, 80, 25);
stopBtn.setEnabled(false);
addActionListener(stopBtn);
panel.add(stopBtn);
return panel;
}
// 为按钮绑定监听
private static void addActionListener(JButton button) {
button.addActionListener(e -> {
if (Objects.equals(button.getText(), "启动")) {
taskStart();
} else if (Objects.equals(button.getText(), "停止")) {
taskStop();
}
});
}
// 为按钮绑定监听
private static void addActionListener(JRadioButton button) {
button.addActionListener(e -> {
if (Objects.equals(button.getText(), "是")) {
MainPanel.sysConfig.setAutoStart("true");
} else {
MainPanel.sysConfig.setAutoStart("false");
}
});
}
// 开始
private static void taskStart() {
SwingUtilities.invokeLater(() -> {
startBtn.setEnabled(false);
setStartupStatus(StartupStatus.STARTING);
MainPanel.disableConfigFields();
MainPanel.saveConfigToFile();
// 创建一个新的 PrintStream,用于重定向 System.out
consolePrintStream = new PrintStream(new LogPanel.LogOutputStream());
// 将 System.out 重定向到新的 PrintStream
System.setOut(consolePrintStream);
System.setErr(consolePrintStream);
// 使用新线程去启动,否则会阻塞其他线程
executorService.execute(() -> {
CertPanel.removeCertLabel();
try {
ManageApplication.startSpringBoot(MainPanel.epilepsyDbConfig, MainPanel.gdDbConfig);
} catch (Exception e) {
MainPanel.enableConfigFields();
stopBtn.setEnabled(false);
startBtn.setEnabled(true);
setStartupStatus(StartupStatus.NOT_STARTED);
return;
}
stopBtn.setEnabled(true);
startBtn.setEnabled(false);
setStartupStatus(StartupStatus.STARTED);
CertPanel.addCertLabel();
if (LicenseInfo.VERIFY_SUCCESS) {
openBrowser("http://localhost:9999");
}
});
});
}
public static void main(String[] args) {
try {
System.out.println("Local IP Addresses:");
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (!inetAddress.isLoopbackAddress() && !inetAddress.getHostAddress().contains(":")) {
System.out.println(" " + networkInterface.getName() + ": " + inetAddress.getHostAddress());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void openBrowser(String url) {
try {
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(new URI(url));
} else {
System.err.println("Desktop or Browser is not supported on this platform");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 停止服务
public static void taskStop() {
MainPanel.enableConfigFields();
// 开始执行任务
SwingUtilities.invokeLater(ManageApplication::stopSpringBoot);
stopBtn.setEnabled(false);
startBtn.setEnabled(true);
setStartupStatus(StartupStatus.NOT_STARTED);
}
// 新增方法用于设置启动状态
private static void setStartupStatus(StartupStatus status) {
switch (status) {
case STARTING:
statusLabel.setIcon(startingIcon);
statusLabel.setText("启动中");
break;
case STARTED:
statusLabel.setIcon(startedIcon);
statusLabel.setText("已启动");
break;
case NOT_STARTED:
statusLabel.setIcon(notStartedIcon);
statusLabel.setText("未启动");
break;
}
}
// 新增方法用于创建状态图标
private static ImageIcon createStatusIcon(StartupStatus status) {
int size = 12; // 调整图标大小
ImageIcon icon = new ImageIcon(new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB));
Graphics g = icon.getImage().getGraphics();
switch (status) {
case STARTING:
g.setColor(Color.ORANGE);
break;
case STARTED:
g.setColor(Color.GREEN);
break;
case NOT_STARTED:
g.setColor(Color.GRAY);
break;
}
g.fillOval(0, 0, size, size);
g.dispose();
return icon;
}
public static void disableConfigFields() {
yesRadioBtn.setEnabled(false);
noRadioBtn.setEnabled(false);
}
public static void enableConfigFields() {
yesRadioBtn.setEnabled(true);
noRadioBtn.setEnabled(true);
}
}
日志区域面板
/**
* 日志面板
*
* @author YoungJ
*/
public class LogPanel {
private static final int MAX_LOG_LINES = 1000; // 限制最大日志行数
public static JTextArea logTextArea;
private static final int y0 = MainPanel.LOG_PANEL_START_COOR[1];
private static final int x0 = MainPanel.LOG_PANEL_START_COOR[0];
private static JButton clearBtn;
public static void buildJpanel(JPanel panel) {
panel.add(MainPanel.buildJBorder("服务日志", x0, y0 + 10, MainPanel.LOG_PANEL_WIDTH, MainPanel.LOG_PANEL_HEIGHT));
panel.add(clearBtn = MainPanel.buildJButton("清空", x0 + 10, y0 + 28, 60, 20));
addActionListener(clearBtn);
logTextArea = new JTextArea();
logTextArea.setEditable(false);
panel.add(logTextArea);
JScrollPane logScrollPane = new JScrollPane();
logScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
logScrollPane.setBounds(x0 + 10, y0 + 50, MainPanel.LOG_PANEL_WIDTH - 10, MainPanel.LOG_PANEL_HEIGHT - 50);
logScrollPane.getViewport().add(logTextArea);
panel.add(logScrollPane);
LogPanelAppender.setTextArea(logTextArea);
}
public static void print(String message) {
SwingUtilities.invokeLater(() -> {
logTextArea.append(message);
int lineCount = logTextArea.getLineCount();
if (lineCount > MAX_LOG_LINES) {
try {
int startOffset = logTextArea.getLineStartOffset(0);
int endOffset = logTextArea.getLineEndOffset(lineCount - MAX_LOG_LINES);
logTextArea.replaceRange("", startOffset, endOffset);
} catch (Exception e) {
e.printStackTrace();
}
}
});
logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
}
// 自定义 OutputStream,用于重定向输出流到 LogPanel
static class LogOutputStream extends OutputStream {
@Override
public void write(int b) {
}
@Override
public void write(byte[] b, int off, int len) {
SwingUtilities.invokeLater(() -> print(new String(b, off, len)));
}
}
private static void addActionListener(JButton button) {
button.addActionListener(e -> {
if (Objects.equals(button.getText(), "清空")) {
logTextArea.setText("");
}
});
}
}
将Log4j2的日志输出到GUI上
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.layout.PatternLayout;
import javax.swing.*;
import java.io.Serializable;
/**
* 日志Appender,将log4j2的日志输出到GUI
* @author zhaoyajie
*/
public class LogPanelAppender extends AbstractAppender {
private static JTextArea textArea;
// 静态方法用于设置 textArea
public static void setTextArea(JTextArea logTextArea) {
textArea = logTextArea;
}
private LogPanelAppender(String name, Layout<? extends Serializable> layout, Filter filter, boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
@Override
public void append(LogEvent event) {
if (event != null && textArea != null) {
String message = new String(getLayout().toByteArray(event));
SwingUtilities.invokeLater(() -> textArea.append(message + "\n"));
}
}
// 需要提供一个工厂方法,Log4j2 才能正确初始化 Appender
public static Appender createAppender(String name, JTextArea area) {
textArea = area;
Layout<? extends Serializable> layout = PatternLayout.createDefaultLayout();
return new LogPanelAppender(name, layout, null, false);
}
}
log4j2配置文件增加输出配置
# 添加 LogPanelAppender
appender.logPanel.type = com.xxx.gui.LogPanelAppender
appender.logPanel.name = LogPanelAppender
appender.logPanel.layout.type = PatternLayout
appender.logPanel.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
rootLogger.appenderRefs = stdout,I,E,logPanel
rootLogger.appenderRef.logPanel.ref = LogPanelAppender
Springboot启动类
@SpringBootApplication
@Slf4j
public class ManageApplication {
private static boolean isSpringBootRunning = false;
private static ConfigurableApplicationContext context;
private static MainPanel mainPanel;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> mainPanel = new MainPanel());
}
public static void startSpringBoot(EpilepsyDbConfig epilepsyDbConfig, GdDbConfig gdDbConfig) {
if (!isSpringBootRunning) {
// 设置Spring Boot启动参数
SpringApplication app = new SpringApplication(ManageApplication.class);
Map<String, Object> properties = new HashMap<>();
properties.put("spring.profiles.active", "prod");
properties.put("epilepsysys.db.database", epilepsyDbConfig.getDatabase());
properties.put("epilepsysys.db.url", epilepsyDbConfig.getUrl());
properties.put("epilepsysys.db.port", String.valueOf(epilepsyDbConfig.getPort()));
properties.put("epilepsysys.db.username", epilepsyDbConfig.getUsername());
properties.put("epilepsysys.db.password", epilepsyDbConfig.getPassword());
properties.put("gd.db.database", gdDbConfig.getDatabase());
properties.put("gd.db.url", gdDbConfig.getUrl());
properties.put("gd.db.username", gdDbConfig.getUsername());
properties.put("gd.db.password", gdDbConfig.getPassword());
if (StringUtils.isNotBlank(MainPanel.sysConfig.getCertPath())) {
properties.put("spring.config.import", MainPanel.sysConfig.getCertPath());
}
app.setDefaultProperties(properties);
context = app.run();
isSpringBootRunning = true;
}
}
public static void stopSpringBoot() {
if (isSpringBootRunning) {
SpringApplication.exit(context);
isSpringBootRunning = false;
}
}
}
关注公众号“呲花是朵花”,查看更多的实用技术分享