资讯动态

JavaWeb学习路线:从Servlet到MyBatis的完整实践笔记

发布时间:2026/10/3 11:31:32 来源:尧图企业网站定制
学完Java基础语法很多人的状态是“能看懂代码但不知道能用来干嘛”。到了黑马Java第二阶段JavaWeb这种悬浮感会迅速落地。我在这一阶段最大的感触是从写一个打印三角形的控制台程序到做出一套能在浏览器上访问、能从数据库读写数据的完整系统中间跨过的不是语法量级的难度而是思维层级的跃迁。这篇笔记不打算复述课程PPT而是把我在学习、复习、后来面试过程中反复验证过的关键点按我的理解重新整理一遍给正在走这条路或者准备走这条路的朋友做参考。这一章会涉及Tomcat、Servlet、JSP、MVC、MyBatis和MySQL的配合也包含大量真实项目中一定会踩的坑。不管你是在黑马学、在B站自学还是培训完在补基础只要你在JavaWeb这个阶段这篇笔记应该都能帮上忙。1. 学完Java基础后JavaWeb这一阶段到底在补什么1.1 从“会写单机程序”到“能独立做网站”的三道坎Java基础阶段你接触的程序大多是跑在main方法里的Scanner读键盘、控制台输出、集合存数据、IO读文件。这个阶段的核心矛盾是“语言本身”你只需要让程序在本机跑起来就够了。但JavaWeb阶段程序的运行场所从你的电脑变成了一台服务器使用方式从键盘输入变成浏览器请求数据存储从内存和文件变成数据库。这三件事对应着三道坎程序如何被远程访问需要一个Web容器接收HTTP请求并按规则返回响应这就是Tomcat做的事。请求如何被有组织的代码处理需要一套标准接口接收请求参数、调用业务逻辑、返回页面或数据这就是Servlet和JSP存在的意义。数据如何可靠地落地和读取需要一种规范化方式操作MySQL而不是每次手动拼SQL这就是JDBC到MyBatis的进化路径。第二阶段的课程表本质上就是逐个拆解这三道坎。你要做的不是把每个知识点当成独立名词背下来而是建立一条完整链条浏览器发请求Tomcat收到Servlet处理Service写业务Mapper查数据库结果再通过JSP或JSON返回页面。1.2 一张地图看明白JavaWeb涉及的全部知识点很多人在第二阶段学着学着迷失是因为没搞清楚各个知识点的位置关系。我按自己的理解画了一张逻辑地图你可以对照着确认自己在哪个位置Web基础层HTTP协议、请求/响应模型、WEB-INF目录结构、web.xml配置。容器层Tomcat的安装目录结构、端口配置、部署方式、常见启动报错排查。动态资源层Servlet生命周期、请求参数获取、响应输出、转发与重定向、Cookie与Session。底层组件层Filter过滤器、Listener监听器、文件上传下载、异步请求Ajax。视图层JSP脚本、EL表达式、JSTL标签库、MVC模式。数据层MySQL回顾、JDBC API、连接池Druid/C3P0、事务控制。框架过渡层MyBatis核心配置、Mapper映射、动态SQL。如果你在某个阶段卡住不要急着往后学。JavaWeb每个层次都依赖前一层Servlet都不熟后面学MyBatis时你连参数怎么传给后端都会懵。1.3 一次请求的完整生命周期浏览器输入URL后发生了什么这个问题是JavaWeb的核心主线也是面试官最爱问的“请描述一次HTTP请求的完整处理流程”。用我们做过的用户登录功能举例完整链条是这样的用户在浏览器地址栏输入http://localhost:8080/login并回车浏览器按HTTP协议把这段URL封装成一个请求对象发送到服务器8080端口。Tomcat作为容器接收请求根据URL中的/login去匹配它维护的Servlet映射表匹配到LoginServlet后Tomcat创建或从池中获取一个LoginServlet实例调用它的service方法。HttpServletRequest对象里装着浏览器传来的所有数据请求参数、请求头、SessionID等。LoginServlet调用业务层的UserServiceUserService内部通过UserDao访问MySQL拿到用户记录后校验用户名密码。校验通过后代码选择跳转方式forward跳转到首页JSP或sendRedirect重定向到首页URL校验失败则返回错误提示。响应沿原路返回浏览器浏览器解析HTML渲染成页面如果页面里引用了CSS/JS/图片浏览器还会再发起若干次静态资源请求。整个过程中Tomcat管理着请求线程和Servlet实例Servlet处理业务MySQL持久化数据。把这条链路讲清楚你的JavaWeb就算是入门了。2. Servlet不建议跳过的基础虽然日常开发已不太直接写它2.1 Servlet本质与生命周期原来你的代码是被“容器”调用的面向对象里我们习惯了“自己写代码自己new”但Servlet是典型的“被别人调用”的程序。你自己不写main方法Servlet实例的创建、初始化、调用、销毁全由Tomcat接管。理解这一点你就理解了整个Web开发最基本的控制反转思维。Servlet生命周期有五个阶段面试经常考用代码对应关系记忆会更牢public class LifecycleServlet extends HttpServlet { // 1. 构造器Tomcat实例化Servlet时调用只执行一次 public LifecycleServlet() { System.out.println(1. 构造); } // 2. init初始化方法只执行一次可读取配置参数 Override public void init() { System.out.println(2. init); } // 3. service每次请求都会执行responsible for分发 Override protected void service(HttpServletRequest req, HttpServletResponse resp) { System.out.println(3. service); } // 4. destroy服务器关闭/卸载应用时执行只执行一次 Override public void destroy() { System.out.println(4. destroy); } }Tomcat默认使用单实例多线程模型同一个Servlet只有一个实例但每次请求来临时Tomcat会从线程池分配一个线程去执行该实例的service方法。这就引发了编程中必须注意的问题——Servlet的成员变量是线程共享的如果你在成员位置定义了一个SimpleDateFormat或Connection并在多线程下读写就会出现并发安全问题。正确做法是尽量使用局部变量需要共享的功能类用ThreadLocal或线程安全类。2.2 核心对象request与response的分工搞懂它省一半调试时间HttpServletRequest和HttpServletResponse是Servlet处理请求的两大核心参数初学者最容易混淆的其实它们的职责非常清晰request代表“浏览器发来的请求”你能从它那里拿所有输入信息// 获取请求参数 String username req.getParameter(username); String[] hobbies req.getParameterValues(hobby); // 获取请求头 String userAgent req.getHeader(User-Agent); // 获取请求路径信息 String uri req.getRequestURI(); String contextPath req.getContextPath();response代表“将要返回浏览器的响应”你往它身上写什么浏览器就能收什么// 设置响应编码解决中文乱码的核心两行 resp.setContentType(text/html;charsetUTF-8); // 获取输出流并写入内容 PrintWriter writer resp.getWriter(); writer.write(h1登录成功/h1); // 重定向 resp.sendRedirect(/index.jsp);我见过很多初学者在处理乱码时反复在response那行加编码却始终没有意识到request接收数据也要设置编码。POST方式中文乱码处理为req.setCharacterEncoding(UTF-8)GET方式中文乱码则需要改Tomcat的server.xml或在代码中对ISO-8859-1转码。这两行编码设置的位置不同效果完全不同这是项目交付后最容易出现的线上问题。2.3 路径问题web.xml配置、注解配置、相对路径与绝对路径Servlet3.0之前配置Servlet映射必须写在web.xml里servlet servlet-nameLoginServlet/servlet-name servlet-classcom.example.web.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mappingServlet3.0之后可以用注解开发清爽很多WebServlet(/login) public class LoginServlet extends HttpServlet { // ... }注解配置适合开发和中小项目。大型项目如果每个接口都靠注解可能维护不便团队会采用统一路由或接口聚合。但考试和面试默认你掌握两种方式。路径问题要特别提一下/login这种以斜杠开头的路径在容器内是相对于“当前应用根路径”的不是相对于服务器根路径。如果应用部署名是myapp那么真正访问路径是http://localhost:8080/myapp/login。在JSP页面里写表单action时直接把action写成/login绝对会404。安全写法是动态获取上下文路径form action${pageContext.request.contextPath}/login methodpost这套细节看似不起眼实则是新手做整合项目时反复报404的最常见原因。3. Cookie/Session/Filter/ListenerWeb状态与控制的四大件3.1 Session和Cookie的协作机制以及我踩过的“登录失效”坑HTTP协议是天然无状态的。你登录一次第二次请求服务器并不知道你是谁于是在JavaWeb里引入了Cookie与Session两兄弟。Cookie是保存在浏览器端的小文本数据每次请求同域地址时浏览器会自动携带。Session是保存在服务器端的数据结构服务器在处理请求时可以用Session找到这个用户之前的状态。两者的桥梁是SYSTEM用户第一次访问时Tomcat生成一个Session对象并给浏览器下发一个名为JSESSIONID的Cookie。后续请求无论携带多少其他信息就靠这个ID找到对应的Session。打个不太严谨但好理解的比方Session是你在游泳馆里存放衣物的储物柜Cookie是柜门的钥匙。钥匙由你自己保管储物数据都在游泳馆这边。我踩过的一个经典坑把用户登录状态存在Session中然后在Session中存了较大的用户对象结果用户信息更新后旧登录态没有失效出现权限判断延迟的情况。后来的优化方案是Session里只存用户ID和必要角色信息业务数据每次实时查询。这样即使权限变更下一次请求就能生效。再提供一个真实的踩坑经验用sendRedirect完成登录后跳转如果跳转目标页面路径写错登录成功也到不了目标页更隐蔽的是开发时在多个标签页同时登录同一账号最后一个登录操作会把前一个Session覆盖失效。测试“记住我”功能时跨浏览器测试尤其要留意Cookie的域路径作用范围。3.2 Filter过滤器为什么说它是Web开发里的“拦截器前置课”Filter中文翻译叫过滤器它的核心用途就是在请求到达Servlet之前或者响应发回浏览器之前进行统一的逻辑处理。学习Filter时值得和后面的拦截器Interceptor对比理解但那是SSM框架阶段的事了。Filter本质是AOP面向切面编程思想的前奏把所有Servlet中“重复但必需的逻辑”抽离出来统一处理。最典型的使用场景有三个统一字符编码解决POST中文乱码WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; request.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); // 放行继续执行后续过滤器或Servlet } }登录校验如果没有登录直接重定向到login页面不进入任何业务Servlet。敏感词过滤、统一日志记录、接口耗时统计。Filter的多级链式执行是另一个容易糊涂的点。多个Filter按WebFilter配置的执行顺序在Servlet3.0中是不保证的取决于类名/路径等如果业务上对顺序敏感比如先编码过滤器、再登录过滤器最稳妥的做法是在web.xml中配置Filter顺序web.xml严格按照filter-mapping排列顺序执行。3.3 Listener监听器ServletContext、session、request三域监听Listener比Filter冷门但设计得很重要。它是在Web应用运行过程中一旦域对象发生了对应事件、Async逻辑总共涵盖生命周期监听、属性变更监听、Session绑定监听等分类时触发你预先定义的逻辑。三个作用域对象要清楚ServletContext整个应用只有一个应用启动时创建关闭时销毁。可以理解成应用的“公用黑板”。HttpSession每个会话一个创建与销毁都有事件。ServletRequest每个请求一个生命周期最短。最常用的监听器之一是ServletContextListener它会在应用启动或关闭时回调适合做初始化工作WebListener public class AppInitListener implements ServletContextListener { Override public void contextInitialized(ServletContextEvent sce) { // 加载配置文件、初始化线程池、预热缓存等 System.out.println(应用启动执行初始化任务); } Override public void contextDestroyed(ServletContextEvent sce) { System.out.println(应用关闭执行清理任务); } }用它的典型场景是项目需要启动时加载一个省份地区数据字典到内存或者初始化一个全局线程池。用Listener做这些事就省去了在Servlet里到处写冗余的初始化代码。4. 从JSP到MVC再到前后端分离JavaWeb的视图演进4.1 JSP本质是Servlet讲清楚它为什么会出现JSPJavaServer Pages是一种允许在HTML中嵌入Java代码的动态页面技术早期它简直是后端开发的“印钞机”因为业务开发只需要在页面里写% Java代码 %就能动态渲染。JSP的本质是一个被包装过的Servlet。第一次访问一个JSP文件时Tomcat会把它翻译成一个Java类文件并编译成Servlet执行。翻译规则大概是HTML内容变成out.write语句JSP脚本片段变成_jspService方法里的Java代码。JSP有九大内置对象request、response、session、application、out、pageContext、page、config、exception。很多初学者把它们当成魔法直接使用但不清楚它们的真实作用范围。比如request只在一次请求内有效session在一个会话内有效application在应用级别有效。选错作用域对象是JSP编程中常见的隐性问题。4.2 MVC思想在JavaWeb中的落地是连接“代码”与“工程”的桥梁黑马第二阶段最关键的思维转变发生在MVC引入的时机。之前写代码页面是页面、后台逻辑是后台逻辑各管各的。但一套真实系统不会这么简单于是分层的需求出现了。MVC在JavaWeb中的对应关系MModel业务数据和业务逻辑对应JavaBean、Service类、Dao类。VView展示页面对应JSP。CController接收请求、调用业务、控制转发对应Servlet。MVC不是Java独有的知识而是一种架构思想。它的核心价值是让代码职责单一Servlet只做路由转发不写SQLService只写业务逻辑不拼SQLJSP只做展示不写业务代码。这套思想在SSM、SpringBoot、微服务架构中一路延续学会了它之后学任何框架都会轻松很多。我在学习时习惯用一个例子复盘用户管理模块中UserServlet接收/user?actionlist请求调用UserService的list方法UserService返回ListUserServlet把list放入request作用域forward到userList.jspJSP用c:forEach标签遍历并渲染表格。整个流程下来每个类的代码量都很小改任何一层都不影响其他层工程性远强于把所有代码堆在一个Servlet里。4.3 前后端分离为什么说传统JSP模式逐渐被替代传统的Model2模型下JSP同时承担视图渲染和前端交互Java代码和HTML混在一起维护代价日益升高。后来出现了Ajax技术页面可以异步从后端拿JSON数据并局部刷新不再需要整个页面重新加载逐渐演化为前后端分离的架构。前后端分离后后端只用提供数据接口返回JSON或XML前端用Vue、React等框架渲染数据。前后端分离不是JavaWeb第二阶段必须深入的话题但知道它从何而来能帮你理解后续学SpringBoot、Restful接口的必然性。第二阶段用JSP做出来的东西虽然看起来有点“复古”但它对理解请求-响应模型、服务端渲染、会话管理有不可替代的训练意义。不要嫌技术老而跳过基础阶段的“笨办法”往往最锻炼思维。5. 数据持久化JDBC、连接池与MyBatis5.1 手写JDBC代码为什么让人崩溃又令人清醒无论SSM还是SpringBoot数据持久化底层都是JDBC规范。MyBatis是对JDBC的封装封装的前提是你自己知道JDBC完整流程。黑马课程里一定有让你手写JDBC代码的经历这一段千万别觉得是浪费时间。我曾经也一样觉得麻烦直到后续排查MyBatis问题时才庆幸当初没有偷懒跳过。JDBC完整六步流程// 1. 加载驱动 Class.forName(com.mysql.cj.jdbc.Driver); // 2. 获取连接 String url jdbc:mysql://localhost:3306/db_demo?useSSLfalseserverTimezoneAsia/Shanghai; String user root; String password 123456; Connection conn DriverManager.getConnection(url, user, password); // 3. 获取语句对象 String sql SELECT * FROM t_user WHERE username ? AND password ?; PreparedStatement ps conn.prepareStatement(sql); // 4. 设置参数 ps.setString(1, username); ps.setString(2, password); // 5. 执行查询并处理结果集 ResultSet rs ps.executeQuery(); ListMapString, Object list new ArrayList(); while (rs.next()) { MapString, Object map new HashMap(); map.put(id, rs.getInt(id)); map.put(username, rs.getString(username)); map.put(password, rs.getString(password)); list.add(map); } // 6. 释放资源 rs.close(); ps.close(); conn.close();手写JDBC的痛点很明显模板代码大量重复、连接管理繁杂、结果集手动封装为对象太痛苦、SQL与Java硬编码耦合。但这六步里每一项展开都是核心面试点为什么要用PreparedStatement而不是Statement答案是预编译与防SQL注入。为什么连接用完要close因为MySQL的默认连接数有限不释放会导致连接耗尽。这些底层理解直接能决定你后面能不能看懂框架的优化思路。5.2 连接池作用为什么每查一次数据库不该新开一个连接手写代码里每执行一条SQL就DriverManager.getConnection一次这在真实项目中是灾难性的。因为建立数据库连接的耗时通常达到几十到几百毫秒高频业务下会导致响应缓慢甚至把数据库打挂。连接池的思想是提前在内存中创建一批连接需要时从池里取出用完归还池中继续复用不真正关闭。JavaWeb阶段最常接触的Druid和C3P0都属于成熟连接池组件。Druid还自带监控页面开发联调时能清晰看到每条SQL的执行耗时和并发情况。Druid的典型配置参数DruidDataSource dataSource new DruidDataSource(); dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/db_demo?...); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); // 初始连接数 dataSource.setMaxActive(20); // 最大活跃连接数 dataSource.setMinIdle(5); // 最小空闲连接数 dataSource.setMaxWait(60000); // 获取连接最大等待时间毫秒连接池参数设计要结合业务量并发不大时initialSize设置5、maxActive设置20就够了并发高、数据库压力大时要谨慎增大maxActive同时配合监控判断慢SQL。盲目调大连接数通常并不会提升性能反而可能增加数据库负载。5.3 MyBatis的引入从“自动映射”理解为什么框架离不开动态代理MyBatis在整个课程中处于从“纯JDBC”到“Spring整合”的过渡地带。它的核心思路是你把SQL写在Mapper接口对应的XML文件或注解里MyBatis负责驱动管理、参数映射、结果集到对象的自动封装。这里藏着JavaWeb阶段最深的一个原理MyBatis的Mapper接口只是一个接口没有实现类为什么能直接注入并调用底层靠的是JDK动态代理。MyBatis在加载Mapper时会为每个接口生成一个代理对象当你调用接口方法时代理对象拿到方法名和参数找到对应XML中的SQL语句ID通过SqlSession执行SQL并完成结果映射。这个机制的完整理解是区分“会使用”和“真理解”的关键指标。下一篇往Spring阶段走之后你会发现AOP切面编程正是同一套底层机制。MyBatis的核心配置文件长这样configuration environments defaultdevelopment environment iddevelopment transactionManager typeJDBC/ dataSource typePOOLED property namedriver valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/db_demo?.../ property nameusername valueroot/ property namepassword value123456/ /dataSource /environment /environments mappers mapper resourcecom/example/mapper/UserMapper.xml/ /mappers /configurationMapper文件里最基础的查询与新增示例mapper namespacecom.example.mapper.UserMapper select idselectById parameterTypeint resultTypecom.example.pojo.User SELECT * FROM t_user WHERE id #{id} /select insert idinsertUser parameterTypecom.example.pojo.User INSERT INTO t_user (username, password, age) VALUES (#{username}, #{password}, #{age}) /insert /mapper#{id}是预编译占位符MyBatis先解析SQL模板再把参数通过PreparedStatement安全传入。这里和JDBC阶段学的PreparedStatement遥相呼应SQL注入防线就这么建立起来了。5.4 事务控制转账不丢钱这件事是怎么保证的数据库层的最后一块拼图是事务。我用一个经典场景演示用户A给用户B转账1000元假设第一步执行“A扣款”数据库写成功第二步执行“B收款”数据库写入失败或无响应。如果没有事务管理A的钱已经扣了B却没收到账就彻底平不了。事务的四要素是ACID原子性、一致性、隔离性、持久性。JavaWeb阶段需要掌握的核心就是把一组逻辑上的SQL操作包裹在同一Connection中多个操作要么全部成功提交commit要么全部失败回滚rollback。用JDBC手写事务控制的简单版本Connection conn dataSource.getConnection(); try { conn.setAutoCommit(false); // 关闭自动提交开启事务 // 第一步A扣款 // 第二步B加款 conn.commit(); // 全部成功则提交 } catch (Exception e) { conn.rollback(); // 任何一步失败则整体回滚 throw e; } finally { conn.setAutoCommit(true); conn.close(); // 归还连接到连接池 }MyBatis中事务默认由SqlSession管理或者交给Spring后续管理Transactional注解。如果你现在搞不明白Transactional不要急但要务必明白底层是“同一连接上的提交与回滚”这样才能在排查实际项目时知道为什么一个Service里多个Mapper操作要么一起成功要么一起失败。6. 把笔记变成作品一个带MySQL的JavaWeb后台管理系统的完整串联6.1 项目选型与表结构新手第一个作品怎么做设计取舍学习第二阶段强烈建议跟着课程完整实现一个项目但不要复制粘贴交差。我建议选“管理系统”这一类业务因为它结构规整、贴近真实工作能覆盖增删改查、分页模糊查询、登录权限等所有核心场景。以“用户角色管理后台”举例我当时的表设计是这样CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码建议存密文, real_name VARCHAR(50) COMMENT 真实姓名, age INT COMMENT 年龄, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ); CREATE TABLE t_user_role ( user_id INT NOT NULL, role_id INT NOT NULL, PRIMARY KEY (user_id, role_id) );设计取舍上有几个心得主键必须用自增int或bigint不要在业务代码里设置ID用户名字段要加唯一约束创建时间每次插入自动填充业务字段如果是预留字段可以暂时不加避免表结构快速膨胀。密码字段不管项目规模多大都不要存明文最少也要用MD5加盐或BCrypt处理。6.2 代码分层与核心逻辑完整链路到底是怎么跑通的包结构按照职责拆分这是JavaWeb阶段你应该建立起的工程习惯com.example.demo ├── controllerServlet控制层接收请求、调服务、转发/重定向 ├── service业务逻辑层事务、校验、流程控制 ├── dao数据访问层操作MySQL的具体SQL逻辑 ├── pojo实体类User、Role等JavaBean ├── filter过滤器编码处理、登录校验 └── util工具类连接池、MD5加密一个完整登录请求的调用链可以精确对应到每一层controller.LoginServlet收到表单参数username/password封装成User对象。调用service.UserService.login(user)。UserService里先判断参数是否空、密码是否正确假如后续涉及积分、vip等信息也会在这一层处理。UserService调用dao.UserDao.findByUsername(username)执行带参查询返回User实体。UserDao的实现类里用的是用JDBC连接池取连接、PreparedStatement查库、ResultSet映射成User。得到User对象后LoginServlet把用户信息写入sessionresp.sendRedirect(index.jsp)完成登录跳转。如果登录失败则req.setAttribute(error, 用户名或密码错误)再req.getRequestDispatcher(login.jsp).forward(req, resp)。注意forward和sendRedirect的区别这是经典面试题forward是服务器内部转发地址栏不变request作用域中的数据可以携带过去redirect是浏览器重新发起一次新请求地址栏变化request作用域中的数据无法跨请求携带需要借助session或通过URL重写传参。这个区别在实际项目中决定了你的表单重复提交问题、登录后页面刷新问题怎么解。6.3 部署与上线常见的坑端口、路径、环境差异项目做完后部署到Tomcat总会遇到本地跑得好好的打包到服务器就出问题的情况。最常见的原因有三个。第一个是路径问题。代码里如果写死了localhost:8080换环境必挂。正确做法是使用相对路径或者通过request.getContextPath()动态获取。第二个是端口和防火墙问题。Tomcat默认8080如果被占用改conf/server.xml后要重启服务器上的防火墙和安全组策略也需要把端口放通。否则从外部访问不通时优先排查第2点而不是项目代码。第三个是数据库环境差异。本地MySQL字符集是utf8mb4服务器上是latin1插入中文直接变乱码。部署后一定要重新检查数据库字符集配置而不仅是看Java代码里的编码设置。另外部署时建议在本地使用war包方式导出放到Tomcat的webapps目录下这种方式比直接复制整个项目更干净也更容易排查问题。7. 面试常考的JavaWeb八股哪些值得认真背7.1 高频考点从Session机制到转发重定向逐个过一遍结合我后来面试和带新人的经验JavaWeb阶段的面试题主要集中在以下几个点上GET和POST的区别GET参数拼接在URL后、有长度限制、会被浏览器缓存、不适合传敏感信息POST参数放在请求体、长度限制宽松、相对安全。细节还包括GET请求可回退刷新无害POST刷新可能产生重复提交。forward和redirect的区别见上文这是必问。Session和Cookie的区别与联系存储位置、生命周期、数据大小限制、安全性。记住那个储物柜钥匙比喻在面试中描述出来就合格了。Servlet是线程安全的吗默认非线程安全因为单实例多线程共享成员变量解决方案是尽量使用局部变量、避免在Servlet中保存可变状态。这个题黑马笔记里也反复强调属于绕不开的重点。JSP九大内置对象及作用域request、session、application、pageContext四个作用域的大小比较、生命周期、使用场景。Filter和Listener的使用场景区别Filter专注统一拦截、校验、编码Listener关注域对象生命周期与属性变更事件。MyBatis中#{}和${}的区别#{}是预编译占位符能防SQL注入${}是字符串拼接有注入风险一般只用于动态表名/列名等特殊场景。7.2 关联Java基础的知识很多人面试挂在这里JavaWeb面试不只是问Web框架面试官特别喜欢在JavaWeb题里穿插Java基础。热词里出现大量“线程等待、动态代理、运算符、集合类”相关搜索并非偶然。我踩过的被追问最多的问题包括动态代理与MyBatis的关系MyBatis的Mapper接口动态代理基于Proxy.newProxyInstance你要能说出InvocationHandler中invoke逻辑做了什么才算真正理解ORM框架底层。线程池与Tomcat的关系Tomcat本身默认使用线程池处理请求要能说出核心线程数、最大线程数、队列容量、拒绝策略对Tomcat高并发时的影响。API设计中为什么流式处理不起作用等等。这些看似基础但解决实际问题时都逃不开它们因此Java基础绝不能丢。8. 最后的分享JavaWeb阶段怎样学才不白费踩了一遍完整的JavaWeb学习路线之后我的感受是这个阶段最致命的问题不是难而是贪多嚼不烂。课程大纲从Servlet一路走到MyBatis节奏很快如果你每天只是跟着敲代码敲完了但不理解为什么那么等到学SpringBoot时旧账全都会找上来。我给自己定了一条规则每学一个新知识点就用自己的话解释一遍“解决什么问题、放在哪一层、底层原理是什么、和之前的知识有什么关联”这四个问题回答清楚了才算过。慢一点但扎实很多。如果你现在正卡在某个问题上比如Filter一直不生效、Session失效、JSP取值为空请对照你的路径配置和作用域对象类型多数问题出在这两个点。把项目完整地做一遍、部署一遍、总结一遍你的JavaWeb阶段就算真正过关了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑