Servlet+JSP+MySQL前后端分离用户列表系统实战
1. 项目缘起与整体设计思路这个项目说起来挺有意思最早是我在帮一个做企业内训的朋友整理学员档案时碰到的需求。他们之前用的是一个单机版的小工具数据存在本地文件里几个人同时用就各种冲突导出个名单还得手动复制粘贴。后来找到我说能不能搞一个简单的Web端用户信息列表展示系统要求不高能录入、能查看、能搜索、能分页最好还能导出。预算有限服务器就是一台普通的云主机环境是现成的Tomcat。我当时第一反应是用Spring Boot一把梭但仔细想了想这个场景其实没必要上那么重的框架。一来服务器配置一般二来维护的人对Spring生态不熟出了问题排查成本高。最后我选了ServletJSPMySQL这套经典组合但做了一个关键调整——前后端分离。注意这里的“前后端分离”不是指现在流行的Vue/React那一套而是指JSP只负责页面骨架渲染数据交互全部走Servlet提供的接口通过AJAX异步获取。这样既保留了JSP上手快的优势又避免了传统JSP里嵌一堆Java代码的混乱局面。为什么这么设计我解释一下背后的考量。传统JSP开发模式里页面里经常混着% %脚本片段、JDBC查询、HTML标签一个文件几百行改一个字段要翻半天。而纯前后端分离比如后端只出JSON前端用Vue对这个项目来说又太重了引入Node构建工具链、跨域配置、打包部署维护成本反而上去了。折中方案就是JSP作为视图模板只输出HTML结构和少量初始化数据所有业务数据通过fetch或XMLHttpRequest调用Servlet接口获取Servlet返回JSON。这样职责清晰调试也方便——打开浏览器控制台就能看到每个请求的返回。技术选型上我列一下具体的版本和理由技术组件版本选择选型理由JDK1.8服务器上已有环境兼容性最好Tomcat 8.5对1.8支持稳定Tomcat8.5轻量启动快对Servlet 3.1规范支持完整MySQL5.7朋友服务器上已安装InnoDB引擎支持事务utf8mb4字符集Servlet3.1支持注解配置WebServlet省去web.xml繁琐配置JSP2.3配合JSTL标签库做页面渲染前端库原生JS Bootstrap 4不引入构建工具CDN直接引用降低维护门槛JSON库Jackson序列化方便性能稳定这个组合的好处是零构建、零额外依赖把项目打成war包扔进Tomcat的webapps目录就能跑。对于中小型内部系统来说这种“轻量级分离”方案在开发效率和可维护性之间找到了一个不错的平衡点。数据库设计方面用户信息表我设计了这些字段id自增主键、username用户名、real_name真实姓名、email邮箱、phone手机号、department部门、status状态1启用/0禁用、create_time创建时间、update_time更新时间。索引方面在username和phone上建了唯一索引department和status上建了普通索引用于筛选。字符集统一用utf8mb4避免emoji存储报错——这个坑我后面会细说。2. 核心细节解析与实操要点2.1 项目目录结构与职责划分目录结构这件事看似简单但规划不好后期维护很痛苦。我采用的是标准Maven Web项目结构但做了一些微调userlist/ ├── src/main/java/ │ ├── com/example/userlist/ │ │ ├── entity/ # 实体类与数据库表对应 │ │ ├── dao/ # 数据访问层纯JDBC操作 │ │ ├── service/ # 业务逻辑层 │ │ ├── servlet/ # 控制器层处理HTTP请求 │ │ └── util/ # 工具类数据库连接、JSON封装 ├── src/main/webapp/ │ ├── static/ │ │ ├── css/ # 自定义样式 │ │ ├── js/ # 页面交互脚本 │ │ └── lib/ # 第三方库本地备份 │ ├── WEB-INF/ │ │ ├── web.xml # 仅配置欢迎页和编码过滤器 │ │ └── views/ # JSP页面存放外部无法直接访问 │ └── index.jsp # 入口页重定向到列表页 └── pom.xml # Maven依赖管理这里有个关键点JSP页面放在WEB-INF/views/下面而不是直接放在webapp根目录。为什么因为放在WEB-INF下的JSP无法通过浏览器地址栏直接访问必须经过Servlet的RequestDispatcher.forward()转发。这样做的好处是所有页面访问都经过控制器可以在转发前做权限校验、数据预处理避免用户直接敲URL绕过业务逻辑。比如用户列表页必须先经过UserListServlet查询数据、设置请求属性再转发到list.jsp这样JSP里拿到的就是已经准备好的数据。2.2 数据库连接池的取舍数据库连接这块我纠结过要不要上连接池。理论上每次请求都DriverManager.getConnection()创建新连接在高并发下会直接把数据库连接数打满。但这个项目是内部使用同时在线人数撑死几十个用连接池反而增加了配置复杂度。不过最后我还是用了Druid连接池原因有两个一是Druid自带监控页面能直观看到SQL执行情况和连接池状态排查问题方便二是配置简单一个druid.properties文件加几行初始化代码就搞定。具体配置如下driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/userlist?useUnicodetruecharacterEncodingutf8mb4useSSLfalse usernameroot passwordyour_password initialSize5 minIdle5 maxActive20 maxWait30000 validationQuerySELECT 1 testWhileIdletrue testOnBorrowfalse testOnReturnfalse这里有几个参数值得说明。maxActive20是根据Tomcat默认最大线程数200来定的一般每个请求持有连接的时间很短20个连接足够周转。maxWait30000表示获取连接的超时时间是30秒超过就抛异常避免请求无限等待。validationQuerySELECT 1是连接有效性检测语句testWhileIdletrue表示空闲时检测这样能及时发现被数据库服务器断开的死连接。注意MySQL 8.0以上版本驱动类名是com.mysql.cj.jdbc.DriverURL里还需要加serverTimezoneAsia/Shanghai否则会报时区错误。我用的MySQL 5.7所以没这个问题但如果你环境不同记得调整。2.3 统一JSON响应格式设计前后端分离的核心是接口约定。我设计了一个统一的响应格式所有Servlet接口都返回这个结构{ code: 200, message: 操作成功, data: { ... }, total: 100 }code用HTTP状态码风格200成功、400参数错误、500服务器异常。message是给前端提示用的。data是实际业务数据可能是对象、数组或null。total专门用于分页场景表示总记录数。为什么要统一格式因为前端处理响应时可以写一个通用函数根据code判断成功失败成功就取data失败就弹message。不用每个接口写一套解析逻辑。我在util包里封装了一个Result类public class ResultT { private int code; private String message; private T data; private Long total; public static T ResultT success(T data) { ResultT r new Result(); r.code 200; r.message 操作成功; r.data data; return r; } public static T ResultT success(T data, Long total) { ResultT r success(data); r.total total; return r; } public static T ResultT error(int code, String message) { ResultT r new Result(); r.code code; r.message message; return r; } // getter/setter省略 }Servlet里这样用WebServlet(/api/user/list) public class UserListServlet extends HttpServlet { private UserService userService new UserService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(application/json;charsetUTF-8); PrintWriter out resp.getWriter(); try { int page Integer.parseInt(req.getParameter(page) null ? 1 : req.getParameter(page)); int limit Integer.parseInt(req.getParameter(limit) null ? 10 : req.getParameter(limit)); String keyword req.getParameter(keyword); String department req.getParameter(department); PageResultUser pageResult userService.queryUsers(page, limit, keyword, department); out.print(new ObjectMapper().writeValueAsString( Result.success(pageResult.getList(), pageResult.getTotal()))); } catch (Exception e) { out.print(new ObjectMapper().writeValueAsString( Result.error(500, 查询失败 e.getMessage()))); } } }这段代码里有个细节resp.setContentType(application/json;charsetUTF-8)必须在getWriter()之前调用否则编码设置不生效返回的中文会变成乱码。这个坑我踩过不止一次。3. 实操过程与核心环节实现3.1 数据库表创建与初始化数据先把表建好这是所有操作的基础。SQL脚本如下CREATE DATABASE IF NOT EXISTS userlist DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE userlist; CREATE TABLE sys_user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 登录账号, real_name varchar(50) NOT NULL COMMENT 真实姓名, email varchar(100) DEFAULT NULL COMMENT 电子邮箱, phone varchar(20) DEFAULT NULL COMMENT 手机号码, department varchar(50) DEFAULT NULL COMMENT 所属部门, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 状态1启用 0禁用, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username), UNIQUE KEY uk_phone (phone), KEY idx_department (department), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户信息表;插入一些测试数据方便调试INSERT INTO sys_user (username, real_name, email, phone, department, status) VALUES (zhangsan, 张三, zhangsanexample.com, 13800000001, 技术部, 1), (lisi, 李四, lisiexample.com, 13800000002, 市场部, 1), (wangwu, 王五, wangwuexample.com, 13800000003, 技术部, 1), (zhaoliu, 赵六, zhaoliuexample.com, 13800000004, 人事部, 0), (sunqi, 孙七, sunqiexample.com, 13800000005, 财务部, 1);提示create_time和update_time用了MySQL的默认值特性插入时不用手动传更新时update_time自动刷新。这样能减少代码里的时间处理逻辑也避免时区转换问题。3.2 分页查询的SQL实现与参数计算分页是列表展示的核心功能。我采用的是物理分页即用LIMIT offset, size在数据库层面截取数据而不是查全量再内存分页。为什么因为用户表数据量虽然不大但养成好习惯很重要万一以后数据涨到几万条内存分页会直接把JVM堆撑爆。分页SQL的拼接逻辑在DAO层public PageResultUser queryUsers(int page, int limit, String keyword, String department) { StringBuilder sql new StringBuilder(SELECT * FROM sys_user WHERE 11); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND (username LIKE ? OR real_name LIKE ? OR phone LIKE ?)); String likeKeyword % keyword.trim() %; params.add(likeKeyword); params.add(likeKeyword); params.add(likeKeyword); } if (department ! null !department.trim().isEmpty()) { sql.append( AND department ?); params.add(department.trim()); } // 先查总数 String countSql SELECT COUNT(*) FROM ( sql.toString() ) AS t; long total queryCount(countSql, params.toArray()); // 再查当前页数据 sql.append( ORDER BY id DESC LIMIT ?, ?); params.add((page - 1) * limit); params.add(limit); ListUser list queryList(sql.toString(), params.toArray()); return new PageResult(list, total); }这里有个性能优化点先查总数再查数据而不是用SQL_CALC_FOUND_ROWS。因为SQL_CALC_FOUND_ROWS在MySQL 8.0已经废弃而且它需要扫描所有匹配行性能不如单独执行COUNT(*)。另外COUNT(*)查询我包了一层子查询这样能复用WHERE条件避免重复拼接。参数计算方面offset (page - 1) * limit。比如第1页每页10条offset0第2页offset10。前端传参时page从1开始limit默认10最大限制100防止恶意请求拉全表。3.3 前端AJAX请求与表格渲染前端这块我用原生fetchAPI不引入jQuery。虽然jQuery的$.ajax写起来更短但原生fetch是标准而且能强制自己理解Promise和异步流程。列表页的核心逻辑在static/js/user-list.js里const API_BASE /userlist/api/user; let currentPage 1; const pageSize 10; async function loadUserList() { const keyword document.getElementById(searchKeyword).value.trim(); const department document.getElementById(searchDepartment).value; const params new URLSearchParams({ page: currentPage, limit: pageSize, keyword: keyword, department: department }); try { const response await fetch(${API_BASE}/list?${params}); const result await response.json(); if (result.code 200) { renderTable(result.data); renderPagination(result.total); } else { alert(加载失败 result.message); } } catch (error) { console.error(请求异常, error); alert(网络异常请稍后重试); } } function renderTable(users) { const tbody document.getElementById(userTableBody); if (!users || users.length 0) { tbody.innerHTML trtd colspan7 classtext-center暂无数据/td/tr; return; } tbody.innerHTML users.map(user tr td${user.id}/td td${escapeHtml(user.username)}/td td${escapeHtml(user.realName)}/td td${escapeHtml(user.email || -)}/td td${escapeHtml(user.phone || -)}/td td${escapeHtml(user.department || -)}/td td span classbadge ${user.status 1 ? badge-success : badge-secondary} ${user.status 1 ? 启用 : 禁用} /span /td /tr ).join(); } function escapeHtml(str) { if (!str) return ; return str.replace(/[]/g, function(match) { const escapeMap { : amp;, : lt;, : gt;, : quot;, : #39; }; return escapeMap[match]; }); }这里有个安全细节所有从后端返回的字符串都要经过escapeHtml转义再插入DOM。虽然这个系统是内部使用但万一有人把用户名改成scriptalert(1)/script直接innerHTML就会触发XSS。养成转义习惯比事后补救强。分页组件我用Bootstrap的pagination样式动态生成页码function renderPagination(total) { const totalPages Math.ceil(total / pageSize); const pagination document.getElementById(pagination); if (totalPages 1) { pagination.innerHTML ; return; } let html ; // 上一页 html li classpage-item ${currentPage 1 ? disabled : } a classpage-link href#>let searchTimer null; document.getElementById(searchKeyword).addEventListener(input, function() { clearTimeout(searchTimer); searchTimer setTimeout(() { currentPage 1; // 搜索时重置到第一页 loadUserList(); }, 300); });300毫秒的延迟是实测比较舒服的值。太短了请求太频繁太长了用户觉得卡顿。部门筛选用change事件立即触发查询因为下拉选择是明确操作不需要防抖。搜索和筛选条件可以叠加比如同时按“技术部”和关键词“张”查询。后端SQL里用AND连接条件前端把参数拼在URL里一起传。这里要注意每次改变搜索条件都要把currentPage重置为1否则可能出现当前在第5页搜索后结果只有2页页面显示空白的情况。4. 常见问题与排查技巧实录4.1 中文乱码问题的三层排查中文乱码是Java Web开发的老大难问题我遇到过至少三种不同的乱码场景排查思路如下乱码现象可能原因排查方法解决方案表单提交后数据库存的是问号请求编码未设置检查request.getCharacterEncoding()在Filter里统一设置request.setCharacterEncoding(UTF-8)页面显示中文正常但接口返回乱码响应编码未设置浏览器控制台看Response Headers的Content-Typeresp.setContentType(application/json;charsetUTF-8)数据库查询出来是乱码连接URL或表字符集不对执行SHOW VARIABLES LIKE character%URL加characterEncodingutf8mb4表用utf8mb4我写了一个编码过滤器在web.xml里配置对所有请求生效WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); chain.doFilter(request, response); } }注意response.setContentType()和response.setCharacterEncoding()的区别。前者设置MIME类型和编码后者只设置编码。对于JSON接口必须用setContentType(application/json;charsetUTF-8)否则浏览器可能按默认编码解析。4.2 分页查询总数与数据不一致这个问题比较隐蔽用户列表显示总共100条翻到第10页却只有5条数据。原因通常是查询总数和查询数据的条件不一致。比如总数查询用了keyword条件但数据查询忘了加或者反过来。我的解决办法是把条件拼接逻辑抽成一个方法总数查询和数据查询共用同一套WHERE子句private String buildWhereClause(String keyword, String department, ListObject params) { StringBuilder where new StringBuilder( WHERE 11); if (keyword ! null !keyword.trim().isEmpty()) { where.append( AND (username LIKE ? OR real_name LIKE ? OR phone LIKE ?)); String likeKeyword % keyword.trim() %; params.add(likeKeyword); params.add(likeKeyword); params.add(likeKeyword); } if (department ! null !department.trim().isEmpty()) { where.append( AND department ?); params.add(department.trim()); } return where.toString(); }这样总数SQL就是SELECT COUNT(*) FROM sys_user whereClause数据SQL就是SELECT * FROM sys_user whereClause LIMIT ?,?。条件永远一致不会出现对不上的情况。4.3 数据库连接泄漏的排查连接泄漏的表现是系统运行一段时间后所有请求都卡住日志里出现wait millis 30000, active 20这样的错误。原因是某个地方获取了连接但没关闭。排查方法在Druid监控页面看活跃连接数如果持续增长不下降就是泄漏了。或者用lsof -i:3306看服务器上的连接数。我的经验是所有JDBC操作必须放在try-with-resources里public ListUser queryList(String sql, Object... params) { ListUser list new ArrayList(); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { list.add(mapRow(rs)); } } } catch (SQLException e) { throw new RuntimeException(查询失败, e); } return list; }try-with-resources会自动关闭Connection、PreparedStatement和ResultSet即使中间抛异常也不影响。这比手动在finally里close靠谱得多。4.4 前端跨域与404问题虽然这个项目前后端在同一个Tomcat里不存在跨域但如果你把前端页面单独部署比如用Nginx托管静态页就会遇到CORS问题。解决方案是在Servlet里加响应头resp.setHeader(Access-Control-Allow-Origin, *); resp.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); resp.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization);另外OPTIONS预检请求要直接返回200不执行业务逻辑if (OPTIONS.equalsIgnoreCase(req.getMethod())) { resp.setStatus(HttpServletResponse.SC_OK); return; }404问题通常是路径写错了。我的建议是Servlet的WebServlet路径和前端请求路径保持完全一致并且注意context path。比如项目部署在/userlist下前端请求要写/userlist/api/user/list而不是/api/user/list。可以在JSP里用${pageContext.request.contextPath}动态获取const API_BASE ${pageContext.request.contextPath}/api/user;这样无论项目部署在什么路径下前端都能正确请求。4.5 常见问题速查表问题现象可能原因快速解决页面空白控制台报404请求路径错误或Servlet未注册检查WebServlet路径和前端请求URL接口返回500日志报ClassNotFound缺少Jackson或MySQL驱动依赖检查pom.xml重新mvn clean package分页点击无反应事件绑定失败或页码计算错误控制台看是否有JS报错检查>mvn clean package -DskipTests生成的war包在target/userlist.war复制到Tomcat的webapps目录下启动Tomcat会自动解压部署。访问地址是http://服务器IP:8080/userlist/。Tomcat配置方面我调整了server.xml里的连接器参数Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 maxThreads200 minSpareThreads20 acceptCount100 URIEncodingUTF-8/URIEncodingUTF-8很重要否则GET请求里的中文参数会乱码。maxThreads200是默认值对于内部系统够用了。acceptCount100表示等待队列长度超过就拒绝连接。5.2 JSP预编译与静态资源缓存JSP第一次访问时会编译成Servlet这个过程比较慢。可以在Tomcat的conf/web.xml里开启JSP预编译servlet servlet-namejsp/servlet-name servlet-classorg.apache.jasper.servlet.JspServlet/servlet-class init-param param-namedevelopment/param-name param-valuefalse/param-value /init-param init-param param-namegenStringAsCharArray/param-name param-valuetrue/param-value /init-param /servletdevelopmentfalse会关闭JSP的运行时编译检查提升性能。静态资源CSS/JS/图片可以配置缓存头减少重复请求WebFilter(/static/*) public class CacheFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse resp (HttpServletResponse) response; resp.setHeader(Cache-Control, max-age86400); chain.doFilter(request, response); } }max-age86400表示缓存一天。如果更新了静态文件改一下文件名或加版本号参数即可。5.3 数据库查询优化用户列表查询主要慢在LIKE %keyword%上因为前置通配符无法走索引。优化方案有两个一是限制搜索字段只对username和phone做前缀匹配LIKE keyword%这样能走索引二是如果数据量大引入全文索引或搜索引擎。对于这个项目数据量预计在几千条以内LIKE %keyword%的全表扫描在毫秒级完成没必要过度优化。但我还是加了一个查询超时设置防止意外慢查询拖垮系统ps.setQueryTimeout(5); // 5秒超时另外ORDER BY id DESC如果id是主键MySQL会直接按主键倒序扫描效率很高。如果按create_time排序记得给create_time建索引。5.4 日志配置与问题追踪日志是排查问题的眼睛。我用Logback替换了默认的JUL配置如下configuration appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender file/var/log/userlist/app.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePattern/var/log/userlist/app.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy encoder pattern%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern /encoder /appender logger namecom.example.userlist levelDEBUG/ root levelINFO appender-ref refFILE/ /root /configuration关键日志点请求进入Servlet时记录参数SQL执行前记录语句和参数异常时记录完整堆栈。这样出问题时看日志就能还原整个请求链路。6. 项目扩展方向与个人体会这个项目上线后运行了半年多中间迭代了几次。后来加的功能包括用户信息导出Excel用POI、批量启用/禁用、操作日志记录。导出功能特别受欢迎因为之前他们手动复制粘贴要花半小时现在点一下按钮几秒钟搞定。如果让我重新设计我会在几个地方做改进。第一引入MyBatis或JPA替代手写JDBC。虽然手写JDBC让我对SQL有完全控制但重复的mapRow和参数绑定代码太多用ORM能省不少事。第二前端用轻量级框架比如Alpine.js或Petite-Vue比原生JS操作DOM更优雅又不像Vue那么重。第三加一层Redis缓存把部门列表、状态字典这些不常变的数据缓存起来减少数据库查询。不过话说回来技术选型没有绝对的对错关键看场景。这个项目的核心诉求是“简单、稳定、好维护”ServletJSPMySQL这套组合虽然老但胜在成熟、资料多、出问题好查。对于内部工具类项目快速交付和低维护成本比技术先进性更重要。最后分享一个我踩过的坑Tomcat的work目录缓存。有次改了JSP页面重启Tomcat后还是显示旧内容折腾了半天才发现是work/Catalina/localhost/userlist下的编译缓存没清。后来我养成了习惯部署前先删work目录或者直接在Tomcat的context.xml里加Context reloadabletrue开启热加载。这个细节文档里很少提但实际开发中经常遇到。

相关新闻

基于时间序列分析与Django的小麦环境监测系统设计详解

基于时间序列分析与Django的小麦环境监测系统设计详解

又到了毕设季,不少同学在选题时纠结了很久,最后定下了“基于时间序列分析的小麦生长环境监测系统的设计”这个方向。乍一看题目带“时间序列”“环境监测”“Django”,感觉什么都沾一点,但摸不清到底要做什么、答辩要怎么讲。这篇…

2026/10/15 7:08:53 阅读更多 →
开源虚拟化平台选型对比:OpenNebula 与 Proxmox VE 的云化管理分水岭

开源虚拟化平台选型对比:OpenNebula 与 Proxmox VE 的云化管理分水岭

最近有不少朋友在做虚拟化选型时,都把 OpenNebula 和 Proxmox VE 放到一起问:都是开源、都支持 KVM、都号称可以搭私有云,到底该选哪个?这也是我早年间反复纠结过的问题。今天这篇不打算当复读机,不吹不黑,…

2026/10/15 7:08:53 阅读更多 →
SpringBoot在线音乐网站毕业设计:开题报告与核心技术方案解析

SpringBoot在线音乐网站毕业设计:开题报告与核心技术方案解析

每年到这个时间点,总会有几个同学拿着类似“SpringBoot在线音乐网站”的题目来找我聊开题报告的事。这个题目在毕业设计里算是常青树了,乍一看有点“老”,但真要把它做扎实,从选题逻辑、技术选型到数据库设计、权限控制&#xff0…

2026/10/15 7:08:53 阅读更多 →

最新新闻

PLC与协作机器人Profinet通信实战:博途组态、报文设计与调试避坑指南

PLC与协作机器人Profinet通信实战:博途组态、报文设计与调试避坑指南

早几年给一条装配线做集成时,我第一次把主流德系品牌PLC和协作机器人一起挂到Profinet网络上。那会儿最头疼的不是机器人轨迹怎么调,而是PLC和机器人之间的那根网线死活不通:博途认不到设备、设备名对不上、字节序反了、偶发掉线,…

2026/10/15 7:46:01 阅读更多 →
AnyPS5:从系统设置到存储排错的PS5全流程调校指南

AnyPS5:从系统设置到存储排错的PS5全流程调校指南

最近我在反复做同一件事:拿到任何一台PS5,都按照同一套流程把它调校到顺手状态。这个习惯被我顺手叫做“AnyPS5”——因为不管是新拆封的机器、帮朋友代管的主机,还是我手边不同房间里的几台设备,只要照着这套流程走一遍&#xff…

2026/10/15 7:46:01 阅读更多 →
离线证件照生成方案:HivisionIDPhotos与MODNet实战

离线证件照生成方案:HivisionIDPhotos与MODNet实战

证件照这件事,说起来简单,真到自己要用的时候,麻烦事一堆。报名系统要求白底一寸,签证要蓝底两寸,简历上想放个稍微好看点的,翻遍手机相册发现全是自拍和合影,勉强挑出一张,背景还是…

2026/10/15 7:46:01 阅读更多 →
AnyPS5实战:用轻量自动化思路打造PS5游戏主机统一管理方案

AnyPS5实战:用轻量自动化思路打造PS5游戏主机统一管理方案

家里那台PS5吃灰半年之后,我决定不再让它当摆件。前阵子痛下决心做了个折腾型项目,代号就叫“AnyPS5”,核心目标只有一句话:让任何一台PS5,都能被统一管理、快速恢复、按需联动。听起来有点像搞机房运维的人把游戏机当…

2026/10/15 7:46:01 阅读更多 →
智能论文写作工具测评与使用指南

智能论文写作工具测评与使用指南

1. 论文写作工具的现状与需求分析本科阶段的学术写作一直是困扰众多学生的难题。根据2023年高等教育质量报告显示,超过78%的本科生在毕业论文撰写过程中遇到过结构混乱、格式不规范、查重率过高等问题。传统的写作方式需要学生自行查阅大量文献、整理笔记、构建框架…

2026/10/15 7:46:01 阅读更多 →
你的时间都去哪了?用 Tai 给 Windows 装一块“时间电表“

你的时间都去哪了?用 Tai 给 Windows 装一块“时间电表“

你的时间都去哪了?用 Tai 给 Windows 装一块"时间电表" 【免费下载链接】Tai 👻 在Windows上统计软件使用时长和网站浏览时长 项目地址: https://gitcode.com/GitHub_Trending/ta/Tai 忙到深夜合上电脑,回顾一天却只能说&qu…

2026/10/15 7:45:01 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →