前阵子帮人把一套基于JSP的旅行体验交流平台完整跑通了,从开发环境初始化到数据库导入、从源码调试到最终部署上线,整个过程踩了不少坑,也把很多细节真正弄明白了。这套东西说白了一个典型的Java Web课程设计项目,用JSP+Servlet+JDBC实现,核心功能包括用户注册登录、旅行攻略发布、图片和视频上传展示、评论点赞、个人中心管理等。对于正在做JSP课设、毕业设计或者想搞明白Java Web底层原理的同学来说,确实是个很值得参考的完整案例。这篇文章我就把这个项目从设计到部署的全过程详细拆一遍,包括数据库表的构建思路、开发工具版本选择、关键代码实现、调试部署的具体操作,以及我在实践中遇到的各种问题和解决办法,希望能帮你少走弯路。
1. 项目定位与功能拆分
开始动手之前,得先弄明白这套JSP旅行体验交流平台到底要做一个什么东西。很多同学拿到项目第一反应是找代码、跑起来,结果跑起来之后发现这里报错那里白屏,根本原因就是没把项目的功能边界和业务流程搞清楚。这个项目表面上是个“交流平台”,本质上是一个内容管理系统加用户系统的组合体,核心逻辑就是:用户注册登录后可以发布旅行游记,上传旅途中的照片甚至短视频,其他用户可以对游记进行评论和点赞,同时每个用户有一个个人信息展示页面。
1.1 核心业务需求解析
从实际使用场景去理解,这类平台要做的事情无非三件:内容生产、内容展示、内容互动。内容生产对应游记发布模块,用户填写标题、选择目的地分类、写正文、上传图片或视频;内容展示对应首页信息流、游记详情页和个人的作品列表;内容互动则是评论、点赞、收藏这些社交动作。
我用一个实际的用户行为路径来说明:一个新用户访问平台首页,可以看到最新发布的游记列表,每篇游记显示封面图、标题、作者昵称和大致的发布时间。点击进去之后是详情页,左边是游记正文和视频,右边是作者信息和评论区。如果是注册用户,可以点赞、发表评论,也可以点击“写游记”按钮进入发布页面。整个链条下来,涉及的数据表有用户表、游记表、评论表、点赞表,以及相关的图片和视频资源记录,这就是最基础的数据模型来源。
1.2 技术选型与架构思路
技术栈方面,这套项目走的是最朴素的JSP+Servlet+JDBC路线,没有用Spring、MyBatis这些框架。很多人一开始不理解,为什么都2025年了还在用JSP?这里面的逻辑其实很实在:课程设计和毕业设计考察的是对Java Web基础原理的掌握程度,脱离框架手写一遍Servlet、自己封装JDBC工具类、自己处理请求转发和重定向,才能真正理解HTTP请求到数据库响应这整条链路上发生了什么。而且JSP作为视图层,可以直接内嵌Java代码,配合JSTL标签库做数据展示非常直观,非常适合中小型教学项目的体量。
架构上采用经典的MVC分层思想,Model层对应JavaBean和DAO类,View层是JSP页面,Controller层是Servlet。请求的处理流程是:浏览器发起HTTP请求到Tomcat容器,web.xml或注解配置的Servlet根据URL路由接收请求,调用Service层或直接调用DAO层访问MySQL数据库,拿到结果后通过request对象setAttribute传递数据,再forward到对应的JSP页面渲染输出。这套链路虽然简单,但是对于理解Web开发的请求生命周期非常有帮助。
项目目录结构也按照这个思路来组织:src目录下面分com.xxx.entity(实体类)、com.xxx.dao(数据访问层)、com.xxx.servlet(控制层)、com.xxx.util(工具类),web目录下面是JSP页面、CSS和JS资源、上传文件目录,另外还有数据库脚本文件。拿到源码后先看目录结构,就能快速定位每一部分代码的作用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库设计与核心数据表构建
数据库设计是整个项目的地基。我见过太多人代码写得挺顺,结果数据库表设计得一塌糊涂,后面联调的时候各种关联查不出来、字段对不上,改起来比重新写还痛苦。这套JSP旅行体验交流平台的数据模型,核心是围绕“用户—内容—互动”这三层关系来展开的。
2.1 核心数据表结构详解
先看用户表,这是整个系统的基础,字段设计如下:
sql复制CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
`password` VARCHAR(64) NOT NULL COMMENT '密码(MD5加密)',
`nickname` VARCHAR(50) COMMENT '昵称',
`avatar` VARCHAR(255) COMMENT '头像图片路径',
`signature` VARCHAR(255) COMMENT '个性签名',
`role` TINYINT DEFAULT 0 COMMENT '角色:0普通用户,1管理员',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
设计用户表的时候有两个地方需要注意。第一,username字段必须加UNIQUE约束,这是注册接口的查重依据;第二,密码字段长度设为64,存的不是明文而是MD5加密后的哈希值,这样即使数据库泄露也不会直接暴露用户密码。这些细节看似简单,但恰恰是实际项目中考核的重点。
然后是游记表和图片表。游记表存储核心内容信息,图片表独立出来是因为一篇游记可以对应多张图片,如果不拆表就会造成字段冗余,而且不方便后期做图片列表轮播。
sql复制CREATE TABLE `travel_note` (
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '游记ID',
`user_id` INT NOT NULL COMMENT '发布者ID',
`title` VARCHAR(100) NOT NULL COMMENT '标题',
`content` TEXT COMMENT '正文内容',
`cover_image` VARCHAR(255) COMMENT '封面图路径',
`video_url` VARCHAR(255) COMMENT '视频路径',
`location` VARCHAR(100) COMMENT '目的地',
`view_count` INT DEFAULT 0 COMMENT '浏览量',
`like_count` INT DEFAULT 0 COMMENT '点赞数',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
这里有一个容易踩的坑:FOREIGN KEY外键约束。在课程设计中把外键加上是加分项,但实际开发中如果操作顺序不对,容易出现插入子表记录时外键关联不上的报错。解决方案是保证先有用户记录,再插入该用户发布的游记,或者干脆在应用层控制关联关系,不在数据库层面加物理外键。
2.2 评论与点赞表的设计逻辑
评论表和点赞表这两个表的设计逻辑也比较典型。评论表是一对多关系,一篇游记对应多条评论;点赞表有点特殊,这里用了联合唯一索引来防止用户重复点赞。
sql复制CREATE TABLE `comment` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`note_id` INT NOT NULL COMMENT '游记ID',
`user_id` INT NOT NULL COMMENT '评论者ID',
`content` VARCHAR(500) NOT NULL COMMENT '评论内容',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`note_id`) REFERENCES `travel_note`(`id`),
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `like_record` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`note_id` INT NOT NULL,
`user_id` INT NOT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `uk_note_user` (`note_id`, `user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
联合唯一索引uk_note_user这个设计是必须的,它保证同一用户对同一篇游记只能点赞一次。后端在执行业务逻辑时,先尝试INSERT这条点赞记录,如果报Duplicate entry错误,说明用户已经点过赞了,这时就应该执行取消点赞操作。这种利用数据库约束来保证业务规则的做法,在真实项目中非常常见,比单纯用Java代码判断要可靠得多。
建完表之后建议顺手把测试数据也插进去,不然页面空空荡荡的,调试起来也不方便。插入测试数据时注意user_id要和user表已有的id对应上,否则会报外键错误。
3. 开发环境搭建与工程初始化
说实话,环境搭建这一块劝退的人比写代码还多。很多人拿到源码之后第一个问题就是“我这跑不起来”,结果一排查,要么是JDK版本不对,要么是Tomcat配置错了,要么是数据库驱动没引入。这套JSP项目对开发环境其实有明确的要求,版本之间是相互匹配的,不能随意换。
3.1 JDK、IDEA、Tomcat、Maven版本搭配
我实际测试下来比较稳的一套组合是:JDK 1.8 + IntelliJ IDEA 2022.x + Apache Tomcat 8.5.x + Maven 3.6.3 + MySQL 5.7。这个搭配的兼容性很好,网上资料也最多,遇到问题容易查到解决方案。如果非要用JDK 11甚至17,JSP项目会面临一个很尴尬的问题:Tomcat版本得跟着升级到9或者10,而Tomcat 10之后包名从javax.servlet变成了jakarta.servlet,代码里的Servlet导入语句全部要改,非常折腾。
IDEA 2022初始化的时候要做几件事。首先是配置JDK,在Project Structure里选择Project SDK为1.8;然后配置Maven,Settings里找到Maven选项,指定本地仓库路径和settings.xml文件位置,第一次运行时会自动下载大量依赖。这里有个技巧:如果Maven下载依赖特别慢,可以在settings.xml里配置阿里云镜像源,速度能提升一个量级。
项目的依赖管理如果是Maven方式,核心的pom.xml需要引入这几个关键依赖:
xml复制<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSTL 标签库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
特别注意Servlet API的scope要设置为provided,因为Tomcat容器自带了Servlet实现,如果打进了war包反而会冲突。MySQL驱动的版本要和数据库版本对应,5.7对应5.1.x驱动,8.0对应8.0.x驱动,版本不对会报通讯链路异常。
3.2 数据库初始化与连接配置
数据库的导入操作不复杂,但很多人卡在Navicat和命令行到底用哪个这个问题上。其实两种方式都行:如果项目根目录有.sql文件,直接用Navicat打开然后运行即可;如果只有数据库创建语句没有数据,那就手动在MySQL里执行CREATE DATABASE和建表语句。执行的时候要注意,字符集千万别选错了,一定要选择utf8mb4,不然插入中文数据会变成乱码。
然后是数据库连接配置,通常项目里会有一个JDBC工具类,比如DBUtil.java,里面维护连接参数。拿到的源码可能是这样:
java复制private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/travel_db?useSSL=false&characterEncoding=utf8";
private static final String USERNAME = "root";
private static final String PASSWORD = "123456";
这里极大概率需要改成你自己本机的数据库密码。我见过太多人跑不起来,最后发现是密码没改,连接被拒绝。除了密码,URL里还有一个容易忽略的坑:useSSL=false这个参数一定要加,否则MySQL 5.7以上版本会提示SSL连接警告;characterEncoding=utf8参数一定要加,否则中文写入数据库会乱码。
3.3 Tomcat运行配置与项目启动
在IDEA里配置Tomcat运行环境,步骤是:Run菜单下选Edit Configurations,点加号选择Tomcat Server下面的Local,然后配置Tomcat安装目录。这里有一个很关键的细节:Deployment选项卡里要添加Artifact,选择项目打包好的war exploded格式,Application context建议设置为/travel或者直接根路径/,这样访问URL就不会带着一长串项目名。
配置好之后点启动,IDEA会自动把项目部署到Tomcat并启动服务,控制台输出“Connected to server”之后,浏览器输入http://localhost:8080就能看到项目首页了。如果控制台出现乱码或者中文变问号,需要去IDEA的Help菜单里找到Edit Custom VM Options,添加-Dfile.encoding=UTF-8参数,然后重启IDEA。
4. 核心功能实现与关键代码解析
项目能跑起来之后,重点就是研究功能代码是怎么实现的。这套平台的用户注册登录模块、游记发布模块、文件上传模块和交互模块,每一块都有值得拆解的细节,也是面试或答辩时最喜欢问的地方。
4.1 用户注册登录与会话管理
用户注册登录是几乎所有Web项目的第一个功能。注册模块的逻辑是:用户在register.jsp填写用户名和密码,提交后由RegisterServlet接收请求,调用UserDao的findByUsername方法检查用户名是否已存在,如果存在就在页面上提示“用户名已被注册”,否则将密码做MD5加密后存入数据库,并跳转到登录页。
登录模块的核心是Session管理。LoginServlet验证用户名密码成功后,通过request.getSession().setAttribute("loginUser", user)把用户对象存到Session中,后续所有需要登录才能访问的功能都通过判断Session里有没有loginUser来实现。这里我强烈建议写一个LoginFilter过滤器统一做登录校验,而不是在每个Servlet里复制粘贴判断代码。过滤器的配置可以用注解方式:
java复制@WebFilter("/*")
public class LoginFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
Object loginUser = session.getAttribute("loginUser");
String uri = req.getRequestURI();
// 放行登录页、注册页和静态资源
if (uri.contains("login.jsp") || uri.contains("register.jsp")
|| uri.contains("css") || uri.contains("js") || uri.contains("images")
|| uri.contains("LoginServlet") || uri.contains("RegisterServlet")) {
chain.doFilter(request, response);
return;
}
if (loginUser == null) {
response.sendRedirect("login.jsp");
return;
}
chain.doFilter(request, response);
}
}
用过滤器统一处理的好处很明显,新加功能页面的时候不用想着有没有漏掉登录校验,代码安全性和可维护性都大大提升。
4.2 个人信息展示页面与数据回显
个人信息展示页面其实就是当前登录用户的个人主页,展示头像、昵称、签名、个人发布的游记列表和互动数据。这个模块的实现核心是数据回显,也就是说Servlet从数据库查到的数据,怎么在JSP页面上正确地展示出来。
实现思路是这样的:个人中心Servlet接收用户ID参数,调用UserDao查出用户信息,调用TravelNoteDao查出该用户发布的游记列表,然后把这两个对象放到request域中,转发到personal.jsp页面。页面上配合JSTL的c:forEach标签循环输出游记列表,这样可以避免在JSP中写大量的Java代码,页面整体也更干净。
这里有一个细节值得注意:头像上传之后的路径怎么保存和读取。我见过不少项目直接把上传的文件路径存数据库,但在JSP页面上图片不显示,原因是路径写错了。正确做法是数据库里存相对路径,比如存/upload/avatar/123.jpg,然后JSP里通过${pageContext.request.contextPath}拼接项目路径,变成/项目名/upload/avatar/123.jpg,浏览器才能正确请求到这张图片。
4.3 旅行游记发布与图片上传
游记发布这个模块涉及两个技术点:表单数据接收和文件上传。HTML表单要加上enctype="multipart/form-data"属性,否则文件上传功能无法正常工作。后端接收时,如果沿用request.getParameter这种方式获取表单字段,会发现所有字段都是null,因为此时请求体已经变成了二进制流,必须用文件上传组件来解析。
推荐使用Apache Commons FileUpload组件,在pom.xml中引入commons-fileupload和commons-io依赖。上传的代码逻辑比较固定:先用DiskFileItemFactory创建工厂,再创建ServletFileUpload对象解析请求,遍历解析结果里的FileItem,如果是普通表单字段就getString获取值,如果是文件类型就调用write方法保存到指定目录。
上传文件的保存目录需要注意,不能直接写到IDEA项目源码目录的webapp下,因为IDEA部署项目时是用war包解压到Tomcat的临时目录,源码目录里的文件不会实时同步。稳妥的做法是在项目部署目录下创建一个upload文件夹,或者使用系统盘的某个固定目录,然后在Tomcat的server.xml里配置虚拟路径映射。这个细节虽然麻烦,但属于实战中必须掌握的部署技能。
4.4 视频播放与数据导出Excel
关于视频播放,JSP页面实现MP4视频播放其实比很多人想得简单。HTTP协议本身对视频播放是支持的,只要内容类型返回的是video/mp4,浏览器就能直接播放。所以后端不需要做任何特殊处理,只需要保证视频文件能通过URL访问到,前端用HTML5的video标签即可:
html复制<video width="100%" controls>
<source src="${pageContext.request.contextPath}${note.videoUrl}" type="video/mp4">
</video>
如果视频播放不了,大概率是上传的视频编码浏览器不支持,建议使用H.264编码的MP4格式。另外文件大小有限制的话,Tomcat默认的POST请求大小是2MB,需要在这个配置里调整:
code复制在Tomcat的 conf/context.xml 中为Context添加 maxPostSize="104857600" 属性
后台管理模块可能有人需要把游记列表导出成Excel表格,这也是课程设计里的常见功能点。封装一个简单的方法:设置响应的ContentType为application/vnd.ms-excel,设置Content-Disposition为attachment,filename指定为xxx.xls,然后通过Java POI库创建Workbook,填充数据后输出到response.getOutputStream()。这样用户在浏览器里点击导出按钮,就能直接下载Excel文件。
5. 调试部署与运行验证
功能代码研究完之后,很多人以为万事大吉,结果一部署到服务器就各种问题。实际上本地跑通和部署上线之间还有一道坎,那就是调试技巧和部署配置。这里我完整说一下从本地调试到最终部署的整个流程。
5.1 本地调试与热部署技巧
本地调试JSP项目,最常遇到的问题就是“改了JSP不生效”。出现这个情况,第一反应不用怀疑代码,先去检查IDEA的Tomcat配置里On frame deactivation选项是不是设为Update resources,同时Deployment选项卡里勾选Build on frame deactivation。这样设置之后,修改JSP保存时IDEA会自动把文件同步到Tomcat部署目录,浏览器刷新就能看到变化,不用反复重启Tomcat,调试效率能提高不少。
如果改了Java代码又不想重启Tomcat,可以把IDEA的Hot Swap功能用起来。Debug模式下,IDEA支持对方法体内部的代码修改进行热替换,但要注意新增方法和新增字段之类的结构性变更不支持热替换,只能重启。另外还有一个容易忽略的地方:JSP页面编译错误不会在控制台直接显示完整报错,要打开浏览器的开发者工具F12,查看网络请求的响应内容,Tomcat的报错信息往往就藏在响应体里。
关于Java代码的调试,最好还是学会打断点。在代码行号的右侧单击就能打上断点,用Debug模式启动Tomcat,访问对应页面之后程序会在断点处暂停,这时候可以逐行执行,查看每个变量的值。有些同学习惯性地到处用System.out.println输出日志排查问题,遇到复杂逻辑时效率很低,断点调试才是真正解决问题的利器。
5.2 项目打包及Tomcat部署步骤
本地调试完就要考虑部署到服务器上。这套JSP项目打包发布的大致流程是:先用Maven执行package命令或者直接IDEA里的Build Artifact打出war包,然后把war包拷贝到Tomcat的webapps目录下,启动Tomcat会自动完成解压和部署。
这里有一个很多人不知道的坑:war包的名字就是访问路径的根路径。比如你打出来的包叫travel.war,部署后访问地址就是http://IP:8080/travel/;如果你希望用户直接访问根路径,就把war包改名为ROOT.war,部署后访问地址就是http://IP:8080/。这个小技巧在正式上线时非常实用。
数据库脚本在服务器上同样要执行一遍,注意确保服务器上的MySQL配置允许远程连接,以及数据库密码要和项目配置一致。如果部署的是Linux服务器,数据库连接URL里的localhost改成服务器的内网IP,同时检查Tomcat的端口是否在防火墙规则里放行,不然外部访问不了。
5.3 部署后的功能验证路径
部署完成后,不要急着宣布完工,要跑一遍完整的冒烟测试流程。我习惯的验证路径是:先访问首页看列表能否正常加载;然后注册一个新用户,看能否登录;登录后发布一篇带图片的游记,刷新首页看是否出现在列表里;进入该游记详情页,测试评论和点赞功能,以及重复点赞是否会被阻止;最后访问个人中心,确认个人信息和游记列表能正确显示。整个流程走通了,这个项目才算真正交付。
6. 常见问题与排查技巧实录
做这个项目的时候,我断断续续收集了一堆异常报错和解决记录,这些都是课程设计和实际部署中最容易遇到的典型问题。整理成速查表,方便你对号入座:
| 问题表现 | 可能原因 | 解决方案 |
|---|---|---|
| Tomcat启动失败,提示端口占用 | 8080端口被其他进程占用 | 修改Tomcat的server.xml端口,或杀掉占用进程 |
| 连接数据库报Access denied for user | 数据库用户名或密码错误 | 检查DBUtil中的账号密码与MySQL实际一致 |
| 插入中文数据后显示问号 | JDBC连接未指定字符集或表字符集不对 | URL加characterEncoding=utf8,表字段用utf8mb4 |
| JSP页面显示源码不渲染 | 部署结构错误,JSP文件未放入webapp目录 | 检查Artifact配置,重新构建war包 |
| 上传图片后页面无法显示 | 图片保存路径与访问路径不一致 | 统一使用相对路径,用el表达式拼接项目名 |
| 改了JSP刷新没变化 | IDEA未设置更新资源 | Tomcat配置里On frame deactivation设为Update resources |
| 视频上传了但播放不了 | 视频编码格式不兼容 | 转换视频为H.264编码的MP4格式 |
| 注册时提示用户名重复但仍能注册 | 后端未做唯一性校验 | 在RegisterServlet中调用findByUsername先查一次 |
除了表格里的常规问题,再重点说三个我实际踩过的比较隐蔽的坑。
第一个是JDBC驱动版本引起的通讯异常。我用MySQL 8.0数据库,但pom.xml里引入的驱动是5.1.49,启动后一执行查询就报Communications link failure。排查很久换了8.0版本的驱动就好了,同时URL中的驱动类名也要从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver。如果你用的是MySQL 5.7,老老实实用5.1.x驱动就好,版本不是越高越好,匹配才最重要。
第二个是文件上传时Tomcat临时目录权限不足导致的Java.io.IOException。这个问题在Windows本地不常见,但在Linux服务器上很典型。Tomcat运行用户没有文件上传目录的写权限,导致write方法执行失败。解决办法是给上传目录执行chmod 777授权,或者把Tomcat运行用户切换为root(不建议)。
第三个是部署后静态资源404。很多项目在本地开发时CSS和JS都能正常加载,部署到服务器就全丢了。原因通常是打包时没有把静态资源打进war包里。检查IDEA的Artifact配置,确认web目录下的css、js、images文件夹都被纳入打包范围,同时JSP页面引用静态资源时用${pageContext.request.contextPath}拼接路径,不要写死绝对路径。
再补充一个小技巧,排查问题时的通用思路是分段定位。比如页面空白了,先用浏览器F12看Network请求,确认JSP请求是否返回200,再确认Servlet请求是否正常,再确认数据库查询是否有数据,按这个链路逐级排查,问题一般都能锁定到某一层。不要一上来就怀疑某个类的代码有问题,很多时候只是数据库连接断了或者路径写错了。
这个项目从头到尾跑完,我个人最大的体会是:JSP技术虽然老,但它把Web开发最核心的东西都展示了——HTTP请求怎么被处理、数据怎么从数据库流到页面、用户状态怎么保持。弄懂这些东西,后面学Spring Boot、MyBatis等框架会轻松很多,因为你已经知道它在底层替你做的是什么事了。最后再分享一个小经验:拿到任何源码项目,第一件事不是运行,而是先花半小时看目录结构和数据库脚本,把数据流转的链路在脑子里过一遍,这样后面遇到任何报错你都能掐着位置找问题,而不是漫无目的地乱试。
