简介:《软件工程实习记录》是一份面向高校软件工程及相关专业学生、实习指导教师,以及需要撰写实习材料的读者整理的本科实习文档,也可作为毕业实习记录修改的参考。压缩包内共1个doc文件,大小约40KB,以按周记录的实习日志为主体,串联起可行性分析、需求分析、概要设计、详细设计、编码与项目验收等开发流程,并涉及项目经理职责、开发岗位分工和技术栈说明。内容结合具体实习场景,记录了Eclipse Swing文件查找与排序、HTML/CSS/JavaScript教师信息展示、Node.js与Express页面请求、MySQL数据导入及增删改查、Linux与ARM开发环境熟悉等过程,还包含遇到问题后向指导老师请教并调整实现的思路。该文档已有2924人学习,适合用来了解软件工程实习的日常安排、任务推进方式和记录写法,也能为整理周记、实习报告提供较完整的素材参照。
1. 代码没改却跑不起来:环境类故障为什么总挑验收前发作
大学软件工程专业的实习记录里,最容易被略过、也最常在复盘时被翻出来重看的部分,往往不是某个功能怎么实现,而是那些卡了大半天的环境故障:页面打不开、浏览器报 500、数据库连不上。代码一行没改,换台机器就崩,这类问题的排查成本远高于写业务逻辑。这份软件工程实习记录按周记了下来,从 Swing 桌面查找、HTML 静态页,到 Express 单页、JSP 商城和 Tomcat 部署,四周里撞上的报错恰好覆盖了毕业实习最典型的几类环境坑。下面按故障现象归类拆开讲,每一条都给到能照抄的命令和配置。
2. IIS 本地站点打不开:匿名来宾账户没权限,改完还得看磁盘格式
2.1 现象:文件双击能开,网址一敲就废
先把现象记清楚,因为后面的判断全靠它。系统装好、IIS 组件勾上、在管理器里把默认网站的主目录指到项目文件夹,地址栏输入本机地址回车,结果要么转很久之后报"无法显示此页",要么直接甩一个 403。这种时候多数人的第一反应是服务没起来,于是去重启 IIS、去换端口、去关防火墙,折腾一圈问题还在原地。
有个细节能帮你少走两小时弯路:把同一个 index.html 双击用浏览器打开是完全正常的,说明文件本身、编码、路径都没毛病;一旦走 http 协议就出 403,说明卡点在"IIS 这个进程以什么身份去读你的文件"。请求进得来、响应出不去,方向就该从权限查,而不是从网络查。
另一种表现是返回 401.3,浏览器会弹出一个要求输入账号密码的框。这个更直白,等于服务器在说"我不认识你"。
2.2 根因:匿名访问用的是独立账户,不是你
这一层讲透,后面所有操作才有依据。IIS 处理匿名请求时,默认不会用你当前登录 Windows 的账户去读磁盘,而是使用一个专门的内置账户 IUSR(早期版本叫 Internet 来宾账户)。这个账户的权限被刻意压得很低,只在很小范围内有权利,站点目录如果只把权限给了管理员组和 SYSTEM,IUSR 连目录都进不去。
NTFS 权限的判定规则是"拒绝优先、允许取并集",只要 IUSR 不在允许列表里,哪怕你是管理员、哪怕你用资源管理器点开毫无障碍,IIS 进程照样被挡在门外。这就是"我看得见、网站看不见"的全部原因。
记录里那句"要设置 Internet 来宾账户的权限",说的就是这一步;后面补的"今后网站建设中进行后台管理,要勾选修改权限",是因为后台要写文件、传附件,只给读权限会在上传环节再翻一次车。
2.3 解决:一条命令补齐读取和修改权限
图形界面路径是:右键站点目录 → 属性 → 安全 → 编辑 → 添加 → 输入 IUSR → 确定 → 勾选"读取和执行""列出文件夹内容""读取",需要后台写入再勾"修改"。界面操作容易漏勾,命令行更稳,可复制、可留痕,换台机器还能重放。
:: 授予 IIS 匿名访问账户对站点目录的读取执行权限 :: (OI) 对象继承,(CI) 容器继承,RX 为读取和执行 icacls "D:\web\site" /grant "IUSR:(OI)(CI)(RX)" /T :: 后台需要上传、写配置的,再补一条修改权限 icacls "D:\web\site" /grant "IUSR:(OI)(CI)(M)" /T :: 确认结果:列表里应能看到 IUSR 及它拿到的权限字母 icacls "D:\web\site"逻辑说明:/grant是追加允许权限,不会覆盖已有条目,两条命令可以叠加执行,不必担心把管理员组的权限冲掉。(OI)和(CI)这两个继承标志必须带,否则只对当前这一层目录生效,子目录里的 css、js、图片仍然读不到,页面会变成"能打开但样式全丢"的半残状态。/T表示递归应用到所有子目录和文件,项目刚拷进来时一定要带。
参数怎么改:目录换成你自己的站点根目录;如果服务器上用的是自定义匿名账户而不是内置 IUSR,把IUSR换成那个账户名;纯静态展示的项目不要加(M),写权限给多了是安全隐患,后台需要上传再单独给某个子目录开。
2.4 前置条件:磁盘不是 NTFS,安全选项卡根本不出现
记录里还有一句很容易被略过、但极其关键的话:只有在磁盘格式化为 NTFS 的前提下,才能在资源管理器里看到"安全"选项卡。FAT32 和 exFAT 没有 ACL 这一套东西,右键属性里压根没有安全页,你在网上抄的所有授权步骤都无从下手。
先确认盘的文件系统:
:: 查看指定盘符的文件系统类型 fsutil fsinfo volumeinfo D:输出里"文件系统名"一行如果是 NTFS,继续做权限;如果是 FAT32,要么换一个 NTFS 分区放项目,要么就地转换:
:: 把 FAT32 转为 NTFS,不可逆,执行前先备份并确认没有程序占用该盘 convert D: /fs:ntfs这条命令的逻辑说明:它只做单向转换,转完回不去,所以必须先备份;执行时要求独占该卷,有程序占用会提示稍后重试,重启后再跑即可。老机器上做本地站点,这一步踩坑率相当高,因为很多人图省事拿 U 盘或移动硬盘当站点目录,出厂就是 FAT32 或者 exFAT。
2.5 验证与预防:看返回码,别靠肉眼猜
改完权限别急着开浏览器,先用一条命令看服务端到底回了什么状态码:
# -I 只取响应头不下载正文;响应码比页面文字可信得多 curl -I http://127.0.0.1/index.html怎么读结果:返回HTTP/1.1 200 OK说明权限通了;返回403.14多半是目录浏览被禁且没有默认文档;返回401.3是授权失败,权限没生效或账户名写错;返回404是文件不在你指定的主目录下。再配合 IIS 日志目录里对应时间点的记录,能把"谁在什么时候用什么账户请求了什么、被拒了"看得一清二楚。
预防上我一般做两件事:一是给新站点单独建目录,不放在系统盘根目录,避免继承一堆来历不明的父级权限;二是每次迁环境后跑一遍上面这条 curl,把状态码当验收标准,而不是"我看着能打开就行"。
3. Tomcat 报 500 日志写着"数据加载失败":jar 包和数据库版本对不上
3.1 500 的第一动作是翻日志,不是改配置文件
现象:项目打成 war 丢进 Tomcat 的 webapps,启动看起来正常,浏览器一访问就 500。页面只给一句"服务器内部错误",什么有用信息都没有。这时候最耗时间的错误做法是去改 server.xml、去动 context 配置、去反复重启。
应该先找到日志。Tomcat 的日志分几路:catalina 系列记容器启动和全局异常,localhost 系列记应用层未捕获的堆栈,还有应用自己输出的业务日志。500 的根因几乎一定在这三类里。
记录里的原话是"到服务器日志查找原因,有一个数据加载失败的提示",这一行就是分水岭——它把问题范围从"Tomcat 配置"直接划到了"数据访问层"。
3.2 为什么"数据加载失败"会被误判成服务器配置问题
因为症状具有欺骗性:应用跑在 Tomcat 里,报错页面由 Tomcat 渲染,第一眼看上去当然是服务器的问题。但顺着日志往下看堆栈,会看到类似SQLException、No suitable driver、连接超时、认证失败这类由数据库驱动抛出的异常。根因在连接层,只是被容器包了一层壳。
版本不匹配是其中最常见的一种:项目 lib 里放的驱动 jar 是较新版本,数据库服务端是几年前的老版本,驱动连接时协商的协议、默认加密方式、默认参数老服务端不认识,于是连接建立失败,业务代码 catch 之后翻译成一句笼统的"数据加载失败"。反过来也一样,老驱动连新服务端会遇到认证插件不兼容。
还有一个隐蔽点:war 包里带了一份驱动,容器的 lib 目录里也有一份不同版本的驱动,类加载器先加载到哪一个不确定,表现就是"同一份代码,有时能连有时不能连"。这种情况单看代码永远找不到原因。
3.3 解决:先确认实际加载的是哪个版本,再对齐
第一步,把运行时的驱动版本打出来,不要靠文件名猜。
// 放在应用启动的监听器里执行一次,输出容器里真正注册的那个驱动 String url = "jdbc:mysql://127.0.0.1:3306/shop"; try { Class.forName("com.mysql.cj.jdbc.Driver"); java.sql.Driver d = java.sql.DriverManager.getDriver(url); System.out.println("driver=" + d.getMajorVersion() + "." + d.getMinorVersion()); } catch (Exception e) { e.printStackTrace(); // 这里的堆栈才是真实原因,别用空的 catch 吞掉 }逻辑说明:DriverManager.getDriver返回的是容器里真正注册成功的那一个实例,而不是你依赖里写的那一个。如果这一步直接抛异常,说明驱动压根没被加载,问题比版本不匹配更靠前,先查 lib 目录和类路径。catch 里一定要打堆栈,很多"数据加载失败"就是被空的 catch 块吃掉了原始异常。
第二步,把重复的驱动清掉。应用 lib 和容器 lib 只保留一份,版本跟服务端的大版本对齐,而不是一味求新。
# 看 war 包里到底带了哪些驱动 unzip -l target/shop.war | grep -iE "jdbc|driver" # 看容器 lib 里有没有同名不同版本 ls -l "$CATALINA_HOME/lib" | grep -iE "jdbc|driver"逻辑说明:unzip -l只列清单不解压,用来确认打包时有没有把测试用的驱动一起带进去;第二条用来发现"两份驱动共存"。两边都列出来对比版本号,就能定位到冲突来源。清理时删掉多余的那份,重新打包、清空 work 目录再启动,避免旧的编译缓存继续生效。
第三步,改完重启并用连接池做一次真实连接探测,不要只看启动日志有没有报错——连接池是懒加载的,启动时不连库,第一次请求才连。
3.4 版本对照与预防:把连接自检做成启动项
老服务端配新驱动、新服务端配老驱动,都会出问题,方向相反但排查方法一样:先定位版本,再对齐。记录里这次是 jar 包和数据库版本不匹配,改完即恢复。放到今天还要多考虑一项——连接串里的加密和时区参数,新驱动默认要求 TLS、默认要求显式时区,老服务端没有这些能力,就会报握手失败。常见做法是在连接串里显式声明参数,而不是直接降级驱动。
我一般在应用里保留一个健康检查接口,返回数据库连接状态和驱动版本号。上线后任何一次"页面 500",先打这个接口,几秒钟就能区分是应用挂了还是连接断了,比翻日志快得多。
4. 数据库装不上、中文存不进:安装选项和字符集最容易漏的两处
4.1 安装失败:安装类型选错,后面全乱
现象:安装向导一路下一步,到最后一步服务起不来,或者装完连不上、找不到客户端。记录里给出的原因很朴素——安装类型没有选"开发者"这一项。
为什么这个选项影响这么大:安装向导的几种类型对应不同的组件集合。只装服务端不装客户端工具,就没有命令行和图形客户端,你没法建库建表;只装客户端不装服务端,服务根本没起来,连接自然被拒;而开发环境需要的是一套能本地自测的完整组合,还包括示例库和驱动连接器。
判断方法很直接:服务列表里有没有数据库服务,装完目录里有没有客户端可执行文件,命令行敲mysql --version有没有反应。三者缺一,就回去重跑一遍安装程序补装组件,不用重装系统。
4.2 中文存不进:连接串少一个字符集参数
现象:英文数据能存能显示,中文一提交就变问号或者乱码。
原因链路有三段:页面用什么编码把表单内容编码成字节;请求到达服务端后用什么编码解码;JDBC 连接用什么字符集和数据库通信。再加上库和表自身的字符集,任何一段不一致,中文就会在中间某一段被错误转换。记录里最后的做法是"在连接数据库的代码处添加了限定字符集的代码",这是最直接的一段。
-- 建库建表时就把字符集显式定下来,不要依赖服务端默认值 CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 已经建好的表也能转,转换前先备份 ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;// 连接串里显式声明编码,避免驱动用平台默认值 String url = "jdbc:mysql://127.0.0.1:3306/shop" + "?useUnicode=true" + "&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai";参数说明:useUnicode打开 Unicode 转换;characterEncoding指定客户端与服务端通信使用的字符集;serverTimezone是较新驱动的强制要求,不写会直接抛时区异常,这个异常在老教程里很少提,遇到时容易一头雾水。
这里要交代一句:记录里当时的做法是把数据库和页面都改成 gbk,问题当天就消失了。gbk 能解决中文,但不是所有生僻字和符号都能表示,一旦有跨语言内容就得换。现在更稳的是全链路统一 utf8mb4:库、表、连接串、页面、响应头五处保持一致,不要一半 gbk 一半 utf8 混着用,混用迟早出问题。
4.3 页面这一端也要对齐
<%-- 页面声明编码和响应编码,两处都写,缺一个都可能出乱码 --%> <%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>参数说明:pageEncoding决定 JSP 源文件本身按什么编码被读取和编译;contentType里的 charset 决定响应头里告诉浏览器用什么编码渲染。这两个不是一回事,只写一个的典型症状是:源码里的中文在编译后变成乱码,或者页面内容正常但浏览器按错误编码猜出一堆方块。
另外,表单提交如果是 POST,还要确认请求体的解码编码,通常在过滤器里统一设置;GET 参数的编码取决于容器配置,较新的 Tomcat 默认已是 UTF-8,老版本需要在 Connector 上加URIEncoding,这一项漏了的表现是"表单能存中文、地址栏传参就乱"。
4.4 端到端验证:从插入到回显走一遍闭环
光看页面显示正常不算通过,因为有可能只是"页面显示对了、库里其实是错的"。验证要两边看:
-- 1. 直接查库里的真实字节,确认存进去的就是对的 SELECT id, name, HEX(name) FROM book WHERE id = 1; -- 2. 看库和表的字符集定义 SHOW CREATE TABLE book;逻辑说明:HEX()把字符串按字节打印出来,中文在 utf8mb4 下占三到四个字节,长度和内容都能对得上才算真存对了。如果字节和页面显示的字符对不上,说明写入阶段就错了,问题在连接串或请求解码;如果库里是对的、页面是错的,问题在读取或渲染阶段。这个二分法能省掉一半的试错时间。
5. 避坑:实习四周里五个最坑人的环境问题
这五条是记录里反复出现、每次都要重新查一遍的类型,按"现象、原因、解决"压缩成一条一段,方便对着现场排查。
5.1 启动成功但访问失败:端口被占用
现象:控制台输出"Server startup in xxx ms",看着一切正常,浏览器访问就是连不上。日志里其实有一行被前面的滚动刷掉了:Address already in use。
原因:端口被别的进程占了。常见的是上一次的容器没退干净、另一个服务用了同一个默认端口、或者开发工具内置容器还开着。记录里改成 89 端口后立刻恢复,就是这个原因。
解决:
# Windows 下查谁占了 8080,最后一列是 PID netstat -ano | findstr :8080 # 拿 PID 反查进程名 tasklist | findstr 12345<!-- conf/server.xml 里找到 Connector,改成没被占用的端口 --> <Connector port="89" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />参数说明:port改成空闲端口;如果还配了 AJP 连接器,那个 8009 也可能冲突,一起检查。改完要同步改访问地址,别改了配置还去敲旧端口。生产环境不建议用 89 这类常用预留端口,容易和别的服务撞车,选一个高位端口更稳。
5.2 查找只能查出固定目录:路径写死在代码里
现象:桌面端查找功能输入关键词,永远只返回某一个目录下的文件,换个盘符就查不到。记录里第二天就撞上这个问题,改完才支持任意目录。
原因:查找根路径在代码里被写成了常量字符串。
// 反例:根路径写死 File root = new File("D:/data"); // 正例:从输入框或配置读取,并做非空与合法性校验 String path = pathField.getText().trim(); if (path.isEmpty()) { JOptionPane.showMessageDialog(null, "请先指定查找目录"); return; } File root = new File(path); if (!root.isDirectory()) { JOptionPane.showMessageDialog(null, "目录不存在:" + path); return; }逻辑说明:把路径外部化之后还要处理两个边界——目录不存在、没有读权限。递归遍历时用队列或栈代替纯递归,目录层级深的时候不会栈溢出;模糊匹配用fileName.contains(keyword),是否忽略大小写取决于业务,不要默认加。查找状态要能中断,否则大目录一扫就卡死界面。
5.3 添加的信息在页面上重复显示
现象:点一次添加按钮,新数据出现在列表里,再点一次,同一条又出现一遍。
原因:只往容器里追加,没有先清空。记录里第三周前端那条写的就是这个,改法是"在添加信息前先删除原来的信息"。
// 每次重新渲染前先清空表格体,再按数组内容重建 function render(list) { const tbody = document.querySelector("#teacherTable tbody"); tbody.innerHTML = ""; // 关键:先清空,避免重复追加 list.forEach(item => { const tr = document.createElement("tr"); tr.innerHTML = `<td>${item.id}</td><td>${item.name}</td><td>${item.course}</td>`; tbody.appendChild(tr); }); }参数说明:innerHTML = ""是最快的清空方式;如果列表项绑定了事件监听,直接清空会留下悬挂引用,改用while (tbody.firstChild) tbody.removeChild(tbody.firstChild)更稳。渲染函数要设计成"输入什么状态就画出什么状态",不要依赖上一次的 DOM 残留,这是前端最常见的状态污染来源。
5.4 静态数组演示一切正常,连上数据库就白屏
现象:把数据写死在数组里,页面正常;换成从数据库读,页面空白,控制台报错。
原因:静态数组阶段绕过了真实链路的所有问题——连接、查询、结果集映射、序列化。任何一环出错都会白屏,而白屏本身不告诉你错在哪。记录里用一天搭响应请求、又用一天接数据库,断掉的地方全在中间这几环。
// 接口返回统一结构,前端才能区分"没数据"和"出错了" app.get('/api/books', async (req, res) => { try { const [rows] = await pool.query('SELECT id, name FROM book'); res.json({ code: 0, data: rows }); } catch (e) { console.error(e); // 服务端留完整堆栈 res.status(500).json({ code: 5001, msg: '查询失败' }); } });逻辑说明:前端拿到code !== 0就渲染错误态,而不是当成空列表处理,否则用户看到的永远是空白。服务端日志打完整堆栈,返回给前端的只给错误码和简短说明,不要把 SQL 语句和连接串暴露出去。数据库连接池要设超时和最大连接数,否则并发一上来就是排队等死。
5.5 改了编码还是乱码:漏了其中一段
现象:数据库字符集改了,页面也声明了,刷新还是乱码。
原因:链路里还有一段没对齐,最常见的是三处——JSP 源文件实际保存的编码和 pageEncoding 声明不一致;POST 请求体的解码没设置;响应头被别的过滤器覆盖成了别的 charset。
解决顺序:先用HEX()查库,判断错在写端还是读端;再抓一次响应头看Content-Type里的 charset 是什么;最后确认文件本身的存储编码是不是真的 UTF-8。三处都对齐,乱码基本无路可走。别一上来就全改一遍,那样改完还是不生效,你连是哪一步起了作用都不知道。
6. 快速定位:先日志、再端口、后编码的三段式自检
环境类故障最耗人的地方不是修,是找。顺序错了,就会在无关的层面反复试。
第一段看服务是否真的起来了,以及它自报了什么。容器看日志最后几行有没有启动完成、有没有异常堆栈;服务器看服务状态和日志目录。这里能过滤掉一半"其实没起来"的伪故障。
第二段看端口通不通。服务起来了但页面连不上,多半是端口或绑定地址的问题。用netstat -ano | findstr :端口确认监听状态,用curl -I http://127.0.0.1:端口/确认响应码。这一步能把"端口被占用""只绑了本机地址、外部访问不到"这类问题一次清掉。
第三段才看应用内部,按响应码分流:401 和 403 查权限与目录,404 查路径和默认文档,500 查应用日志,白屏或乱码查编码链路。
| 现象 | 第一动作 | 关键判据 | |---|---
本文还有配套的精品资源,点击获取