老系统改造增加初始化,自动化数据源配置

    • 一、前言
    • 二、改造描述
      • 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 的核心特…