老系統改造增加初始化,自動化數據源配置(tomcat+jsp+springmvc)

老系統改造增加初始化,自動化數據源配置

    • 一、前言
    • 二、改造描述
      • 1、環境說明
      • 2、實現步驟簡要思考
    • 三、開始改造
      • 1、準備sql初始化文件
      • 2、啟動時自動讀取jdbc文件,創建數據源,如未配置,需要一個默認的臨時數據源
        • 2.1去掉sping mvc原本配置的固定dataSource,改為動態dataSource
        • 2.2 代碼類,這里是示例,我就不管規范了,放到一起
          • 2.2.1 DynamicDataSourceConfig.java
          • 2.2.2 JdbcConfig.java
          • 2.2.3 ProxyDataSource.java
      • 3. 編輯jdbc配置,保存配置,根據配置創建新的數據源,并銷毀就的數據源,同時改變新數據源的SqlSessionFactory
        • 3.1放開登錄攔截
          • 3.1.1 登錄攔截器添加代碼
        • 3.2 控制器實現,和前端頁面代碼
          • 3.2.1 SetupController.java
          • 3.2.2 DatabaseInitService.java
          • 3.2.3 setup.jsp
          • 3.2.4 init-db.jsp
          • 3.2.5 message.jsp
      • 4、正常訪問系統
    • 四、結語

一、前言

在技術飛速迭代的當下,許多老舊項目因未及時升級,陷入了部署困境。這類系統往往缺乏標準化配置,依賴特殊運行環境,加之團隊中運維角色的缺失,每次上線部署都得勞煩開發人員遠程操作。繁瑣的手動配置、環境依賴沖突、版本兼容問題層出不窮,不僅占用開發精力,還常因操作失誤導致部署失敗,拖慢業務推進節奏。為此,亟需一套極簡的部署方案
—— 無需專業技術背景,讓普通員工通過點擊幾次鼠標,就能完成系統的安裝與上線,徹底擺脫對開發人員的依賴,化解老系統部署的效率瓶頸。

二、改造描述

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 {@Autowiredprivate 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;  // 默認URLprivate 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 Setterspublic 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;}@Overridepublic Connection getConnection() throws SQLException {if (!jdbcConfig.isInitialized()) {return fallbackDataSource.getConnection();}throw new UnsupportedOperationException("代理數據源不支持直接獲取連接");}@Overridepublic 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 BeanServletContext context = request.getServletContext();JdbcConfig jdbcConfig = (JdbcConfig) context.getAttribute("jdbcConfig");// 如果配置為空,先加載配置if (jdbcConfig == null) {jdbcConfig = new JdbcConfig();jdbcConfig.loadConfig();if(jdbcConfig.isValid()){context.setAttribute("jdbcConfig", jdbcConfig);}}// 排除配置相關的URLString 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 {@Autowiredprivate JdbcConfig jdbcConfig;@Autowiredprivate DatabaseInitService initService;@Autowiredprivate ApplicationContext applicationContext; // 注入上下文@Autowiredprivate 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 {@Autowiredprivate 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--;}// 如果文件名以數字結尾,則返回對應的數字,否則返回0if (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 DWHERE 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 重新配置即可

四、結語

此方法使用于老項目,并且不會做升級的老項目。示例代碼不一定規范,各位可按照邏輯做調整。頁面美化,可自行根據需要調整

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88524.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88524.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88524.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

衛星通信終端天線的5種對星模式之二:DVB跟蹤

要實現穩定可靠的衛星通信&#xff0c;地面終端天線必須精準地對準遠方的衛星。對星的過程是一個不斷搜索、不斷逼近的過程&#xff0c;其目標是讓天線波束中心精確指向衛星&#xff0c;從而獲得最大信號接收與發射效率。 衛星通信終端天線的對星技術是保障衛星通信鏈路穩定的…

重構下一代智能電池“神經中樞”:GCKontrol定義高性能BMS系統級設計標桿

概述BMS&#xff08;電池管理系統&#xff09;作為新能源汽車動力電池與整車的核心紐帶&#xff0c;通過實時監控電壓、電流、溫度及SOC等參數&#xff0c;控制電池充放電過程&#xff0c;保障電池安全性與使用壽命。隨著電動汽車智能化發展&#xff0c;對BMS的響應速度、精度和…

面試150 對稱二叉樹

思路 聯想遞歸三部曲&#xff1a;傳入參數、遍歷方式、返回什么。本題聯想到先序遍歷的方式,需要遍歷整顆二叉樹,最后返回的是一個布爾值。然后我們需要傳入的是左子樹和左子樹的節點,然后分別進行比較。 # Definition for a binary tree node. # class TreeNode: # def __…

多線程的區別和聯系

進程和線程的區別和聯系1.一個進程可以包含多個線程&#xff0c;不能夠沒有線程2.進程是系統資源分配的基本單位&#xff0c;線程是系統調度執行的基本單位3.同一個進程里的線程之間&#xff0c;共用同一份系統資源4.線程是當下實現并發編程的主流方式&#xff0c;通過多線程&a…

兩個文件夾自動同步

兩個文件夾自動同步&#xff0c;非常簡單&#xff0c;利用一些工具就可以輕松做到&#xff0c;設置完源和目標文件夾&#xff0c;點擊啟動就馬上可以兩個文件夾自動同步&#xff0c;對于一些有文件同步、文件災備需求的老登&#xff0c;用起來會非常順手&#xff0c;比如PanguF…

虛擬商品交易維權指南:數字經濟時代的消費者權益保護

首席數據官高鵬律師數字經濟團隊創作AI輔助在元宇宙、NFT、虛擬情緒產品等新興領域蓬勃發展的今天&#xff0c;虛擬商品交易已成為數字經濟的重要組成部分。從游戲皮膚、在線課程到數字藏品&#xff0c;消費者在享受虛擬商品便捷性的同時&#xff0c;也面臨著諸多法律風險。作為…

mysql 一條語句的執行流程

文章目錄一條查詢語句的執行流程連接器管理連接權限校驗分析器優化器采樣統計優化器選錯索引改正執行器查詢緩存存儲引擎一條update語句的執行流程redo logredo log buffer結構redo log日志類型寫入時機配置innodb_flush_log_at_trx_commitbinlogredo log和binlog 對比配置兩階…

【視頻觀看系統】- 需求分析

&#x1f3af; 一、項目目標 構建一個功能完備的視頻觀看網站&#xff0c;用戶可以上傳、瀏覽、觀看視頻&#xff0c;并在觀看過程中實時發送/接收彈幕。系統具備良好的性能、可擴展性與用戶體驗&#xff0c;未來可逐步擴展為多媒體平臺。&#x1f464; 二、用戶角色分析用戶類…

模型驅動的架構MDA的案例

在一個企業資源規劃&#xff08;ERP&#xff09;系統開發項目中&#xff0c;目標是為一家中型制造企業打造一套高效且可擴展的管理系統&#xff0c;涵蓋訂單處理、庫存管理等多個業務模塊。項目團隊采用了 MDA 的設計思想進行開發。?首先是業務需求分析與計算獨立模型&#xf…

第一次搭建數據庫

本文詳細介紹第一次搭建數據庫安裝和配置過程, 包括卸載舊版本、下載安裝、配置服務、環境變量等等 第一步下載mysql 在下載之前需要檢查電腦上有沒有安裝mysql, 如果有再安裝, 80%就會有問題 檢查方法: 電腦-右鍵找到管理-服務-在服務中找有沒有mysql服務若有請先 1.停止服務 …

洛谷題解 | UVA1485 Permutation Counting

目錄題目描述題目思路AC 代碼題目描述 https://onlinejudge.org/external/14/p1485.pdf 題目思路 dp。 定義 dpi,jdp_{i,j}dpi,j? 為前 iii 個數的排列中恰好有 jjj 個小于號的排列總數。 考慮將數字 iii 插入到前 i?1i-1i?1 個數的排列中不同的位置&#xff1a; 如果…

飛算科技:以原創技術賦能電商企業數字化轉型

在電商行業從流量競爭邁向精細化運營的當下&#xff0c;技術能力已成為決定企業生存與發展的核心要素。然而&#xff0c;高并發場景下的系統穩定性、個性化推薦算法的迭代效率、營銷活動的快速響應等挑戰&#xff0c;讓許多電商企業陷入“技術投入大、見效慢”的困境。作為國家…

人工智能自動化編程:傳統軟件開發vs AI驅動開發對比分析

人工智能自動化編程&#xff1a;傳統軟件開發vs AI驅動開發對比分析 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 ? 用代碼丈量…

用java實現一個自定義基于logback的日志工具類

? 動態創建: 無需配置文件&#xff0c;通過代碼動態創建logback日志對象 ? Class對象支持: 使用LogUtil.getLogger(MyClass.class)的方式獲取日志 ? 日期格式文件: 自動生成info.%d{yyyy-MM-dd}.log格式的日志文件 ? 文件數量管理: 只保留最近3個文件&#xff0c;自動刪除歷…

面試現場:奇哥扮豬吃老虎,RocketMQ高級原理吊打面試官

“你了解RocketMQ的高級原理和源碼嗎&#xff1f;” 面試官推了推眼鏡&#xff0c;嘴角帶笑&#xff0c;眼神里透著一絲輕蔑。 奇哥笑而不語&#xff0c;開始表演。面試場景描寫 公司位于高樓林立的CBD&#xff0c;電梯直達28樓。面試室寬敞明亮&#xff0c;空氣中混著咖啡香與…

Django Nginx+uWSGI 安裝配置指南

Django Nginx+uWSGI 安裝配置指南 引言 Django 是一個高級的 Python Web 框架,用于快速開發和部署 Web 應用程序。Nginx 是一個高性能的 HTTP 和反向代理服務器,而 uWSGI 是一個 WSGI 服務器,用于處理 Python Web 應用。本文將詳細介紹如何在您的服務器上安裝和配置 Djang…

外設數據到昇騰310推理卡 之二dma_alloc_attrs

目錄 內核源碼及路徑 CONFIG_DMA_DECLARE_COHERENT DTS示例配置 dma_direct_alloc 特殊屬性快速路徑 (DMA_ATTR_NO_KERNEL_MAPPING) 主體流程 1. 內存分配核心 2. 地址轉換 3. 緩存一致性處理 映射 attrs不同屬性的cache處理 cache的標示&#xff08;ARM64&#xff0…

Java 大視界:基于 Java 的大數據可視化在智慧城市能源消耗動態監測與優化決策中的應用(2025 實戰全景)

??摘要??在“雙碳”戰略深化落地的 2025 年&#xff0c;城市能源管理面臨 ??實時性??、??復雜性??、??可決策性?? 三重挑戰。本文提出基于 Java 技術棧的智慧能源管理平臺&#xff0c;融合 ??Flink 流處理引擎??、??Elasticsearch 實時檢索??、??ECh…

微信小程序控制空調之微信小程序篇

目錄 前言 下載微信開發者工具 一、項目簡述 核心功能 技術亮點 二、MQTT協議實現詳解 1. MQTT連接流程 2. 協議包結構實現 CONNECT包構建 PUBLISH包構建 三、核心功能實現 1. 智能重連機制 2. 溫度控制邏輯 3. 模式控制實現 四、調試系統實現 1. 調試信息收集…

spring boot 詳解以及原理

Spring Boot 是 Spring 框架的擴展&#xff0c;旨在簡化 Spring 應用的開發和部署。它通過自動配置和約定優于配置的原則&#xff0c;讓開發者能夠快速搭建獨立運行的、生產級別的 Spring 應用。以下是 Spring Boot 的詳細解析和工作原理&#xff1a; 一、Spring Boot 的核心特…