老系统改造增加初始化,自动化数据源配置(tomcat+jsp+springmvc)

发布于:2025-07-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、前言

在技术飞速迭代的当下,许多老旧项目因未及时升级,陷入了部署困境。这类系统往往缺乏标准化配置,依赖特殊运行环境,加之团队中运维角色的缺失,每次上线部署都得劳烦开发人员远程操作。繁琐的手动配置、环境依赖冲突、版本兼容问题层出不穷,不仅占用开发精力,还常因操作失误导致部署失败,拖慢业务推进节奏。为此,亟需一套极简的部署方案
—— 无需专业技术背景,让普通员工通过点击几次鼠标,就能完成系统的安装与上线,彻底摆脱对开发人员的依赖,化解老系统部署的效率瓶颈。

二、改造描述

1、环境说明

springmvc + mybatis plus + jsp + mavenjavaweb 项目

2、实现步骤简要思考

  1. 准备初始化sql结构文件 定义绕过权限的接口,访问初始化配置页面
  2. 启动时自动读取jdbc文件,创建数据源,如未配置,需要一个默认的临时数据源
  3. 编辑jdbc配置,保存配置,根据配置创建新的数据源,并销毁就的数据源,同时改变新数据源的SqlSessionFactory,执行初始化脚本
  4. 正常访问系统

三、开始改造

1、准备sql初始化文件

不涉及代码,不过多介绍,把准备好的sql脚本放到资源文件下,新建一个sql目录,注意后续增量sql继续增加后面,按照增量时间改名。后续初始化的时候,会按照时间顺序执行初始化脚本

在这里插入图片描述

注意:1、调整maven的pom.xml文件,让maven能把sql脚本文件编译到class里面去,不然后面执行,找不到文件

在这里插入图片描述

2、启动时自动读取jdbc文件,创建数据源,如未配置,需要一个默认的临时数据源

2.1去掉sping mvc原本配置的固定dataSource,改为动态dataSource

在这里插入图片描述

注意:需要添加包扫描,保证动态dataSource那部分bean能被正常扫描

2.2 代码类,这里是示例,我就不管规范了,放到一起
2.2.1 DynamicDataSourceConfig.java
package com.luozc.config.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope; // 添加缺失的Scope导入
import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    private JdbcConfig jdbcConfig;

    @Bean(name = "dataSource")
    @Scope("prototype")
    public DataSource dataSource() throws SQLException {
        System.out.println("[动态数据源] 创建新数据源...");
        
        jdbcConfig.loadConfig();
        
        String driver = jdbcConfig.getDriverClassName();
        String url = jdbcConfig.getUrl();
        String username = jdbcConfig.getUsername();
        String password = jdbcConfig.getPassword();
        
        System.out.println("[动态数据源] 使用以下配置创建数据源:");
        System.out.println("driver: " + driver);
        System.out.println("url: " + url);
        System.out.println("username: " + username);
        System.out.println("password: " + (password != null ? "******" : "null"));
        
        if (driver == null || url == null) {
            System.err.println("[动态数据源] 配置为空,返回代理数据源");
            return new ProxyDataSource(jdbcConfig);
        }
        
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        
        // 验证数据库连接
        try {
            // 获取并关闭连接,验证连接有效性
            dataSource.getConnection().close();
            System.out.println("[动态数据源] 新数据源已成功创建并验证。");
        } catch (SQLException e) {
            System.err.println("[动态数据源] 数据库连接验证失败: " + e.getMessage());
            // 记录异常堆栈信息
            e.printStackTrace();
            throw e;
        }
        
        return dataSource;
    }
}   
2.2.2 JdbcConfig.java
package com.luozc.config.jdbc;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.file.Files;
import java.util.Properties;

@Component
public class JdbcConfig {
    private static final String JDBC_CONFIG_FILE = "jdbc.properties";
    private String driverClassName; // 默认驱动
    private String url;  // 默认URL
    private String username;
    private String password;
    private boolean initialized = false;

    public void init() {
        System.out.println("JdbcConfig loaded:");
        System.out.println("driverClassName: " + driverClassName);
        System.out.println("url: " + url);
        System.out.println("username: " + username);
    }
    // 加载配置
    public void loadConfig() {
        Properties props = new Properties();
        String userDir = System.getProperty("user.dir");
        File file = new File(userDir + File.separator + JDBC_CONFIG_FILE);
        if(file.exists()){
            try (InputStream is = Files.newInputStream(file.toPath())) {
                props.load(is);
                driverClassName = props.getProperty("jdbc.driverClassName");
                url = props.getProperty("jdbc.url");
                username = props.getProperty("jdbc.username");
                password = props.getProperty("jdbc.password");
                initialized = Boolean.parseBoolean(props.getProperty("jdbc.initialized", "false"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    // 验证配置是否有效
    public boolean isValid() {
        return driverClassName != null && !driverClassName.isEmpty() &&
                url != null && !url.isEmpty() &&
                username != null && !username.isEmpty();
    }
    // 保存配置到属性文件
    public void saveConfig(boolean persist) {
        Properties props = new Properties();
        String userDir = System.getProperty("user.dir");
        props.setProperty("jdbc.driverClassName", driverClassName);
        props.setProperty("jdbc.url", url);
        props.setProperty("jdbc.username", username);
        props.setProperty("jdbc.password", password);
        initialized = persist;
        props.setProperty("jdbc.initialized", String.valueOf(persist));
        File file = new File(userDir + File.separator + JDBC_CONFIG_FILE);
        try (OutputStream os = new FileOutputStream(file)) {
            props.store(os, "Sys Configuration");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Getters and Setters
    public String getDriverClassName() { return driverClassName; }
    public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; }
    public String getUrl() { return url; }
    public void setUrl(String url) { this.url = url; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public boolean isInitialized() { return initialized; }
    public void setInitialized(boolean initialized) { this.initialized = initialized; 	   }
}
2.2.3 ProxyDataSource.java
package com.luozc.config.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 代理数据源,在数据库配置完成前处理请求
 */
@Component
public class ProxyDataSource implements DataSource {

    private final JdbcConfig jdbcConfig;
    private DataSource fallbackDataSource; // 新增备用数据源

    public ProxyDataSource(JdbcConfig jdbcConfig) {
        this.jdbcConfig = jdbcConfig;
        this.fallbackDataSource = createFallbackDataSource();
    }

    private DataSource createFallbackDataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("org.h2.Driver");
        ds.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
        ds.setUsername("sa");
        ds.setPassword("");
        ds.setInitialSize(1);
        ds.setMinIdle(1);
        ds.setMaxActive(5);
        return ds;
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (!jdbcConfig.isInitialized()) {
            return fallbackDataSource.getConnection();
        }
        throw new UnsupportedOperationException("代理数据源不支持直接获取连接");
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }

    // 实现其他 DataSource 方法...
    @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; }
    @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
    @Override public PrintWriter getLogWriter() throws SQLException { return null; }
    @Override public void setLogWriter(PrintWriter out) throws SQLException {}
    @Override public void setLoginTimeout(int seconds) throws SQLException {}
    @Override public int getLoginTimeout() throws SQLException { return 0; }
    @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; }

}    

注意:这里的备用数据源,需要引入包

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>

3. 编辑jdbc配置,保存配置,根据配置创建新的数据源,并销毁就的数据源,同时改变新数据源的SqlSessionFactory

3.1放开登录拦截

在这里插入图片描述
代码如下

3.1.1 登录拦截器添加代码
 // 获取JdbcConfig Bean
        ServletContext context = request.getServletContext();
        JdbcConfig jdbcConfig = (JdbcConfig) context.getAttribute("jdbcConfig");

        // 如果配置为空,先加载配置
        if (jdbcConfig == null) {
            jdbcConfig = new JdbcConfig();
            jdbcConfig.loadConfig();
            if(jdbcConfig.isValid()){
                context.setAttribute("jdbcConfig", jdbcConfig);
            }
        }

        // 排除配置相关的URL
        String requestURI = request.getRequestURI();
        if (requestURI.contains("/setup") || requestURI.contains("/init-db")
                || requestURI.startsWith("/resources")) {
            return true;
        }

        // 如果未初始化或配置无效,重定向到配置页面
        if (!jdbcConfig.isInitialized() || !jdbcConfig.isValid()) {
            response.sendRedirect(request.getContextPath() + "/setup");
            return false;
        }
3.2 控制器实现,和前端页面代码
3.2.1 SetupController.java
package com.luozc.config.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Controller
public class SetupController {

    @Autowired
    private JdbcConfig jdbcConfig;

    @Autowired
    private DatabaseInitService initService;

    @Autowired
    private ApplicationContext applicationContext; // 注入上下文

    @Autowired
    private SqlSessionFactory sqlSessionFactory; // 新增注入

    @GetMapping("/setup")
    public String showSetupForm(Model model) {
        return "setup"; // 返回配置页面
    }

    @PostMapping("/setup")
    public String saveConfig(
            @RequestParam("driverClassName") String driverClassName,
            @RequestParam("url") String url,
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            @RequestParam(name="sfInit",required = false) String sfInit,
            Model model) {

        // 保存数据库配置
        jdbcConfig.setDriverClassName(driverClassName);
        jdbcConfig.setUrl(url);
        jdbcConfig.setUsername(username);
        jdbcConfig.setPassword(password);
        if(StringUtils.isNotBlank(sfInit)){
            jdbcConfig.saveConfig(false);
            return "redirect:/init-db";
        }else{
            jdbcConfig.saveConfig(true);
            // 可选:手动刷新数据源
            try {
                // 获取当前数据源并尝试关闭(如果是DruidDataSource)
                DataSource currentDataSource = (DataSource) applicationContext.getBean("dataSource");
                if (currentDataSource instanceof DruidDataSource) {
                    ((DruidDataSource) currentDataSource).close();
                    System.out.println("[数据源切换] 旧数据源已关闭。");
                }

                // 获取Bean工厂并移除旧的数据源定义
                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
                if (beanFactory.containsBeanDefinition("dataSource")) {
                    beanFactory.removeBeanDefinition("dataSource");
                }

                // 获取新的数据源实例
                DataSource newDataSource = (DataSource) applicationContext.getBean("dataSource");

                // 正确刷新 MyBatis 的 SqlSessionFactory,确保 TransactionFactory 不为空
                TransactionFactory transactionFactory = sqlSessionFactory.getConfiguration().getEnvironment().getTransactionFactory();
                if (transactionFactory == null) {
                    transactionFactory = new JdbcTransactionFactory(); // 使用默认事务工厂
                }

                sqlSessionFactory.getConfiguration().setEnvironment(
                        new org.apache.ibatis.mapping.Environment("default", transactionFactory, newDataSource)
                );
                System.out.println("[MyBatis] 环境已刷新,使用新数据源。");

                // 验证新数据源连接
                try (Connection conn = newDataSource.getConnection()) {
                    System.out.println("[数据源切换] 新数据源验证成功,当前数据库: " + conn.getCatalog());
                }
                System.out.println("[数据源切换] 新数据源已激活。");
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("[数据源切换] 切换失败: " + e.getMessage());
            }
            return "redirect:/"; // 已初始化,重定向到首页
        }
    }
    
    @GetMapping("/init-db")
    public String showInitPage(Model model) {
        if (jdbcConfig.isInitialized()) {
            return "redirect:/"; // 已初始化,重定向到首页
        }
        return "init-db"; // 返回初始化页面
    }

    @PostMapping("/init-db")
    public String initDatabase(Model model) {
        try {
            initService.initDatabase();
            // 可选:手动刷新数据源
            try {
                // 获取当前数据源并尝试关闭(如果是DruidDataSource)
                DataSource currentDataSource = (DataSource) applicationContext.getBean("dataSource");
                if (currentDataSource instanceof DruidDataSource) {
                    ((DruidDataSource) currentDataSource).close();
                    System.out.println("[数据源切换] 旧数据源已关闭。");
                }

                // 获取Bean工厂并移除旧的数据源定义
                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
                if (beanFactory.containsBeanDefinition("dataSource")) {
                    beanFactory.removeBeanDefinition("dataSource");
                }

                // 获取新的数据源实例
                DataSource newDataSource = (DataSource) applicationContext.getBean("dataSource");

                // 正确刷新 MyBatis 的 SqlSessionFactory,确保 TransactionFactory 不为空
                TransactionFactory transactionFactory = sqlSessionFactory.getConfiguration().getEnvironment().getTransactionFactory();
                if (transactionFactory == null) {
                    transactionFactory = new JdbcTransactionFactory(); // 使用默认事务工厂
                }

                sqlSessionFactory.getConfiguration().setEnvironment(
                        new org.apache.ibatis.mapping.Environment("default", transactionFactory, newDataSource)
                );
                System.out.println("[MyBatis] 环境已刷新,使用新数据源。");

                // 验证新数据源连接
                try (Connection conn = newDataSource.getConnection()) {
                    System.out.println("[数据源切换] 新数据源验证成功,当前数据库: " + conn.getCatalog());
                }
                System.out.println("[数据源切换] 新数据源已激活。");
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("[数据源切换] 切换失败: " + e.getMessage());
            }
            model.addAttribute("message", "数据库初始化成功!请重启应用服务器。");
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("error", "初始化失败: " + e.getMessage());
            return "init-db";
        }
        return "message"; // 返回成功消息页面
    }
}
3.2.2 DatabaseInitService.java
package com.luozc.config.jdbc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

@Service
public class DatabaseInitService {

    @Autowired
    private JdbcConfig jdbcConfig;

    public void initDatabase() throws Exception {
        // 加载数据库驱动
        Class.forName(jdbcConfig.getDriverClassName());
        // 建立数据库连接
        try (Connection conn = DriverManager.getConnection(
                jdbcConfig.getUrl(),
                jdbcConfig.getUsername(),
                jdbcConfig.getPassword())) {
            // 执行初始化SQL脚本
            executeSqlScript(conn, "/sql/init.sql");
            List<String> sortedSqlFiles = getSortedSqlFiles();
            for (int i = 0; i < sortedSqlFiles.size(); i++) {
                // 执行初始化SQL脚本
                executeSqlScript(conn, "/sql/"+sortedSqlFiles.get(i));
            }
            // 标记数据库已初始化
            jdbcConfig.saveConfig(true);
        }
    }

    private void executeSqlScript(Connection conn, String sqlResourcePath) throws IOException, SQLException {
        InputStream is = getClass().getResourceAsStream(sqlResourcePath);
        if (is == null) {
            throw new IOException("SQL脚本文件未找到: " + sqlResourcePath);
        }
        
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
            StringBuilder sqlStatement = new StringBuilder();
            String line;
            
            while ((line = reader.readLine()) != null) {
                // 跳过注释
                if (line.startsWith("--") || line.trim().isEmpty()) {
                    continue;
                }
                sqlStatement.append(line);
                
                // 如果是完整的SQL语句
                if (line.trim().endsWith(";")) {
                    String sql = sqlStatement.toString().replace(";", "");
                    try (Statement stmt = conn.createStatement()) {
                        stmt.execute(sql);
                    }
                    sqlStatement.setLength(0); // 清空
                }
            }
        }
    }

    // 修改getSortedSqlFiles方法,使用Spring的ResourceUtils读取资源文件
    public List<String> getSortedSqlFiles() {
        List<String> sqlFiles = new ArrayList<>();

        try {
            // 使用Spring的ResourceUtils获取资源目录
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:sql/*.sql");

            // 提取文件名并添加到列表
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    String filename = resource.getFilename();
                    if (filename != null && filename.endsWith(".sql")&&!filename.contains("init")) {
                        sqlFiles.add(filename);
                    }
                }
            }

            // 按文件名中的数字排序
            sqlFiles.sort((f1, f2) -> {
                // 提取文件名中的数字部分
                int num1 = extractNumber(f1);
                int num2 = extractNumber(f2);
                return Integer.compare(num1, num2);
            });

        } catch (Exception e) {
            e.printStackTrace();
        }

        return sqlFiles;
    }

    // 从文件名中提取数字部分
    private int extractNumber(String filename) {
        // 去掉.sql扩展名
        String name = filename.substring(0, filename.lastIndexOf("."));

        // 提取数字后缀
        int i = name.length() - 1;
        while (i >= 0 && Character.isDigit(name.charAt(i))) {
            i--;
        }

        // 如果文件名以数字结尾,则返回对应的数字,否则返回0
        if (i < name.length() - 1) {
            return Integer.parseInt(name.substring(i + 1));
        } else {
            return 0; // 对于没有数字后缀的文件,默认为0
        }
    }
}    

jsp代码如下,注意放的目录为你配置spring mvc的上下文目录

3.2.3 setup.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>数据库配置</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container { max-width: 800px; margin: 0 auto; padding: 10px; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input { width: 100%; padding: 8px; box-sizing: border-box; }
        button { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; }
    </style>
</head>
<body>
<div class="container">
    <h2>创建数据库用户sql示例:</h2>
    <form >
        <div class="form-group">
            <label >查询表空间地址:</label>
            <div style="color: red;">
                SELECT T.TABLESPACE_NAME,D.FILE_NAME,D.AUTOEXTENSIBLE,D.BYTES,D.MAXBYTES,D.STATUS FROM DBA_TABLESPACES T, DBA_DATA_FILES D
                WHERE T.TABLESPACE_NAME = D.TABLESPACE_NAME ORDER BY TABLESPACE_NAME, FILE_NAME;
            </div>
        </div>
        <div class="form-group">
            <label >创建永久表空间,初始大小500MB,自动扩展每次50MB,最大2GB:</label>
            <div style="color: red;">
                CREATE TABLESPACE xxx DATAFILE '/usr/local/oracle/oradata/orcl/xxx.dbf'
                SIZE 500M AUTOEXTEND ON NEXT 50M MAXSIZE 2G EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
            </div>
        </div>
        <div class="form-group">
            <label >创建用户并分配表空间:</label>
            <div style="color: red;">
                CREATE USER xxx IDENTIFIED BY xxx DEFAULT TABLESPACE xxx ;
            </div>
        </div>
        <div class="form-group">
            <label >允许用户无限制使用表空间:</label>
            <div style="color: red;">
                ALTER USER xxx QUOTA UNLIMITED ON xxx;
            </div>
        </div>
        <div class="form-group">
            <label >基础权限(登录、建表):</label>
            <div style="color: red;">
                GRANT CREATE SESSION, CREATE TABLE TO xxx;
            </div>
        </div>
        <div class="form-group">
            <label >高级权限(建视图、序列、存储过程):</label>
            <div style="color: red;">
                GRANT CREATE VIEW, CREATE SEQUENCE TO xxx;
            </div>
        </div>
    </form>
    <h2>数据库配置</h2>
    <form action="setup" method="post">
        <div class="form-group">
            <label for="sfInit">是否初始化:</label>
            <input type="checkbox" id="sfInit"  name="sfInit" value="true" style="width: auto">
        </div>
        <div class="form-group">
            <label for="driverClassName">driverClassName:</label>
            <input type="text" id="driverClassName" name="driverClassName"  required>
        </div>
        <div class="form-group">
            <label for="url">JDBC URL:</label>
            <input type="text" id="url" name="url"  required>
        </div>
        <div class="form-group">
            <label for="username">用户名:</label>
            <input type="text" id="username" name="username" required>
        </div>
        <div class="form-group">
            <label for="password">密码:</label>
            <input type="password" id="password" name="password" required>
        </div>
        <button type="submit">保存配置</button>
    </form>
</div>
</body>
</html>
3.2.4 init-db.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>初始化数据库</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container { max-width: 500px; margin: 0 auto; padding: 20px; }
        .error { color: red; }
    </style>
</head>
<body>
<div class="container">
    <h2>初始化数据库</h2>
    <p>检测到新的数据库配置,需要初始化数据库。</p>

    <% if (request.getAttribute("error") != null) { %>
    <p class="error"><%= request.getAttribute("error") %></p>
    <% } %>

    <form action="init-db" method="post">
        <button type="submit">开始初始化</button>
    </form>
</div>
</body>
</html>
3.2.5 message.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>操作结果</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container { max-width: 500px; margin: 0 auto; padding: 20px; }
    </style>
</head>
<body>
<div class="container">
    <h2>操作结果</h2>
    <p><%= request.getAttribute("message") %></p>
    <p>点击下面的链接返回首页:</p>
    <a href="<%= request.getContextPath() %>/">首页</a>
</div>
</body>
</html>

4、正常访问系统

至此系统正常访问
注意:此系统可随时切换数据源,直接访问地址 /setup 重新配置即可

四、结语

此方法使用于老项目,并且不会做升级的老项目。示例代码不一定规范,各位可按照逻辑做调整。页面美化,可自行根据需要调整


网站公告

今日签到

点亮在社区的每一天
去签到