Springboot项目启动前,使用GUI做初始化配置页面并将Log4j2的日志实时显示在GUI上

Springboot项目启动前,使用GUI做初始化配置页面并将Log4j2的日志实时显示在GUI上

效果预览

Mac Os效果图

image-20231219104412561

Windows 10 效果图

image-20231219104640971

需求分析

做这样的一个功能并不适用于所有系统,主要用于交付给用户的产品,这样方便客户自行维护。传统的服务一般都是集群部署在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;
        }
    }

}

qrcode_for_gh_905f664473ad_344

关注公众号“呲花是朵花”,查看更多的实用技术分享

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
核心功能 文章/图片/视频发布、喜欢、统计阅读次数。 文章标签tag功能、支持按tag分类 文章支持ueditor/markdown编辑器切换(后台配置) 评论功能,支持回复,支持表情。 第三方(微博、QQ)登录。 lucene实现的站内搜索。 响应式布局 支持用户订阅 先看效果图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) http://localhost:8080/admin/group/list SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)SpringBoot开发非常美观的java博客系统(包含后台管理功能) 技术选型: JDK8 数据库MySQL 主框架 (Spring-bootSpring-data-jpa) 安全权限 Shiro 搜索工具 Lucene 缓存 Ehcache 视图模板 Freemarker 其它 Jsoup、fastjson jQuery、Seajs Bootstrap 端框架 UEditor/Markdown编辑器 font-Awesome 字体/图标 准备工作(sql文件在项目里面) 安装 Jdk8 安装 Maven 准备 IDE (如果你不看源码,可以忽略下面的步骤,直接通过Maven编译war包:mvn clean package -DskipTests) IDE 需要配置的东西 编码方式设为UTF-8 配置Maven 设置Jdk8 关于这些配置,网上有一大把的资料,所以此处不再重复。 获取代码导入到IDE 下载代码 导入到IDE的时候请选择以Maven的方式导入 项目配置参考 系统配置手册 配置完毕 启动项目,在控制台看到Mblog加载完毕的信息后,表示启动成功 打开浏览器输入:http//localhost/mblog/ (此处仅是示例,具体具体端口因人而异),访问成功即部署完毕 后台管理的地址是 /admin, 如果你是管理员账号点导航栏的头像会看到"后台管理" 启动成功后,你应该去后台的系统配置配置你的网站信息等。 常见问题总结 进入系统后, 菜单加载不出来, 那应该是你没有导 db_init.sql 点标签显示乱码, 请设置Tomcat的 URIEncoding 为 UTF-8 项目截图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 转自:https://gitee.com/mtons/mblog SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注意: 一、java main方式运行mblog-web下的BootApplication.java时抛出异常的解决方案 Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean. SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注释掉后下面图片的这段后,记得maven要重新reimport SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 否则maven依赖不生效还是会抛出以上的异常 二、第三方登录点击后无响应,那是因为第三方开放平台回调的url失效导致,需要你去对应的第三方开放平台注册app后获取对应的oauth帐号 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 三、idea以maven项目导入该项目后,发现没有maven的依赖包时,需要对每个maven module进行clear和install,并且注意maven的依赖顺序 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 四、访问地址是http://localhost:8080 登录时,帐号,密码只要自己找个密码,然后md5下在更新到db中即可登录成功。 比如:zuidaima 111111,md5后密码是 3931MUEQD1939MQMLM4AISPVNE,md5的java类 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YoungJ5788

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值