很多人学完 JavaSE 之后,会突然陷入一段迷茫期:面向对象、集合、IO、多线程好像都学了,但打开招聘网站一看,岗位要求上写的全是 Spring、SpringMVC、MyBatis 这些名词,根目录里一堆 Maven 依赖和 web.xml 配置,完全对不上号。
我当年就是从这个阶段过来的。当时我手头能找到的、体系比较完整的,就是“狂神说Java系列”里那套 JavaEE 相关的教程。说真的,这套系列帮我把“JavaSE 之后到底学什么”这个问题彻底捋清楚了。它不是单纯给你念 PPT,而是从 JavaWeb 基础一路讲到 SSM 整合,把 Servlet、过滤器和 Spring 容器这些抽象的概念,用能跑起来的代码一步步带出来。如果你现在也在自学 JavaEE,或者上完了学校的 Java 课但还不会做项目,这篇文章就是把我基于这套学习框架整理的路线、核心原理和踩过的坑,一次性写出来分享给你。
先放个整体认知:JavaEE 学习不是去背一堆框架的 API,而是要理解一套 Web 应用的底层运行逻辑。你在浏览器里输入一个网址,回车,数据到底经过了哪些过程才显示在页面上?请求怎么被服务器接收、怎么被处理、怎么返回?数据存在哪里、怎么保证安全?还有,代码如何组织、依赖如何管理、怎么和数据库交互、事务怎么控制?把这些问题的答案串起来,你再去学 Spring、SpringBoot,会发现它们只是把底层过程封装得更好看、更好用而已。
1. 为什么需要一套系统化的 JavaEE 学习路线
网上 Java 学习的零散资料太多了,今天看一个 Servlet 教程,明天看一段 Spring 视频,后天翻一翻 MyBatis 的官方文档,结果就是每个技术名词都见过,但脑子里是一盘散沙。这套系列最大的价值,是给了你一条主线,让你知道每个知识点在这个链路里属于哪一环。
1.1 JavaSE 到 JavaEE 之间那道坎
很多人卡住不是因为 JavaSE 没学好,而是因为不知道 JavaEE 阶段的学习目标和之前完全不同。
JavaSE 阶段你写的代码,运行入口是 main 方法,你自己控制程序的流程。JavaEE 阶段写的是 Web 应用,运行在容器里面,由容器来管理组件的生命周期。你不再主动 new 一个 Servlet 出来调用,而是把 Servlet 配置好,让 Tomcat 在收到 HTTP 请求时帮你创建实例、调 init、调 service、再调 destroy。这个思维的转换,是入门 Web 开发的第一道门槛。
当时我花了很多时间想明白一件事:你写的类什么时候被实例化,谁调用了它,数据是怎么传进来的。这些问题的答案不在代码本身,而在容器规范里。所以学习 JavaEE,一定要带着“容器思维”去看问题,而不是像写单机程序一样只关注自己的类怎么实现。
1.2 主线内容到底覆盖了什么
这套学习体系基本覆盖了 JavaEE 开发的核心链路:
- JavaWeb 基础:HTTP 协议、Servlet、请求响应、Session 与 Cookie、过滤器、监听器
- 数据库访问:JDBC、数据库连接池、MyBatis
- 项目管理:Maven
- 企业级框架:Spring 的 IOC 和 AOP、SpringMVC
- 整合实战:SSM 整合,做一个完整的增删改查项目
R如果你把这几个点串起来看,其实就是一条完整的请求链路:浏览器发送 HTTP 请求 → Tomcat 容器接收 → Servlet 或 SpringMVC 的 DispatcherServlet 分发 → Controller 处理业务 → Service 层调用 Mapper → MyBatis 操作数据库 → 返回结果渲染到页面。每一个环节都在这个链路里有明确的位置。
1.3 这套路线适合哪些人
如果你是零基础转行自学,或者科班刚上完 JavaSE 课程但对框架一脸懵,又或者准备面试但发现自己的项目只是“照着视频敲了一遍”,这条路线都值得完整走一遍。
不过要说明白的是,它适合的是“建立整体认知”的阶段,而不是“追新”的阶段。它讲的主要是传统 JavaEE 技术栈,SpringBoot 和微服务只是顺带提一下。但反过来讲,如果你连 Servlet 的生命周期、Maven 的依赖机制、Spring 容器的运行原理都没想过,直接去啃 SpringBoot 的自动装配,很容易变成只会写注解的“配置搬运工”。
2. JavaWeb 阶段的理解与实操要点:从 HTTP 到 Servlet
如果让我只选一个阶段来夯实基础,我会把时间优先砸在 JavaWeb 这里。因为这个阶段学的东西,决定了你后面理解框架时是“顿悟”还是“硬背”。
2.1 先彻底搞懂 B/S 结构和 HTTP 协议
很多教程一上来就写 Servlet 代码,结果初学者连浏览器和服务器之间发生了什么都不知道。我建议你在写任何代码之前,先用抓包工具(或者浏览器开发者工具里的 Network 面板)观察一次完整的 HTTP 请求。
看什么?看请求行、请求头、请求体:请求方式是 GET 还是 POST,路径是什么,带了多少参数,Cookie 在哪个字段里,Content-Type 是什么。再看响应状态码 200、302、404、500 分别代表什么,响应头里 Set-Cookie 做了什么,响应体结构如何。
我之前学习的时候,专门用开发者工具访问了一个最简单的登录页面,把请求和响应的每个字段抄下来,挨个对照着看。这一步比单纯看视频有用得多。理解了这些,你后面再看 Session 怎么维系状态、过滤器怎么拦截请求、SpringMVC 怎么绑定参数,全都是水到渠成的事。
HTTP 协议本身不复杂,但它是 Web 开发的“普通话”。你不需要背下所有状态码,但常用的那几十个,尤其是 301/302、304、400、401、403、404、500、502,要能说出来什么场景下会出现。
2.2 Servlet 到底是什么,为什么框架里还在用
有时候你会听人说“现在谁还写 Servlet”,这话其实是有误导性的。SpringMVC 的 DispatcherServlet 本身就是一个 Servlet,只不过框架帮你把“分发到哪个 Controller、调用哪个方法”这层逻辑封装掉了。
所以,Servlet 是 JavaEE 的地基。你至少要手写过一次 Servlet,并且搞清楚生命周期里那四个方法的调用顺序和调用者:
- init:Servlet 实例创建后,容器调用一次,完成初始化
- service:每次请求都会进入这个方法,根据 HTTP 方法类型转给 doGet 或 doPost
- destroy:容器关闭或应用卸载时调用一次,释放资源
我在第一次学的时候有个体会:不要只停留在“知道”层面,要打开 Tomcat 的源码看一眼 HttpServlet 里的 service 方法是怎么把请求转发给 doGet、doPost 的。看完你就明白,为什么继承 HttpServlet 之后只需要重写 doGet/doPost,也明白为什么 doGet 里写逻辑的话,发 POST 请求会得到 405。
另外,学 Servlet 一定要配合 web.xml 或者注解配置去理解映射关系。一个 URL 到了服务器,容器怎么知道交给哪个 Servlet?这就是 web.xml 里的 servlet-mapping 在做的事。哪怕后来你用 SpringMVC 的 @RequestMapping,本质上也还是在一个东西:请求路径和处理器之间的映射。
2.3 过滤器、监听器、Cookie/Session 的底层作用
这三个概念如果只看定义,很容易相互混淆。我用自己的话给你梳理一遍:
- Cookie:服务器放在浏览器本地的小数据,每次请求都会自动携带。作用是用来标识客户端,比如记住登录状态、记录偏好设置。
- Session:服务器端为每个“会话”保存的数据结构。你第一次访问时,服务器创建 Session,给浏览器返回一个 JSESSIONID 的 Cookie,之后浏览器每次请求带上这个 ID,服务器就能找到对应的 Session。
- 过滤器:在请求进入 Servlet 之前做的拦截处理。登录校验、编码设置、请求日志都是典型场景。
- 监听器:监听 application、session、request 这几个作用域对象的创建和销毁,或者属性的变化。比如在应用启动时初始化数据、在会话销毁时清理资源。
这里有一个很经典的理解方法:把一次请求想象成快递寄送。过滤器是快递分拣站(检查包装、贴标签),Servlet 是收件人(真正处理包裹内容),Session 是你在快递公司的会员档案,Cookie 是你的会员卡。这么一对比,谁在什么阶段干什么事,就清楚多了。
2.4 实操建议:手动配置、源码追踪、调试走一遍
JavaWeb 阶段我给的实操建议是三个:
第一,不要一开始就全部用注解。手动在 web.xml 里配置一次 Servlet 和 Filter,路径、初始化参数、加载顺序分别是什么,配置一遍就懂。
第二,在 Servlet 的 init、service、destroy 方法里打日志,启动 Tomcat,发几个请求,看日志打印顺序,体会容器调用的顺序和时机。
第三,在过滤器中打印请求路径,然后把 Chain 放行与不放行的结果都试一遍,感受一下请求怎么被拦截、怎么回到页面。
这套动作做完,你对 Web 容器如何工作的理解,会比背十遍概念都深刻得多。
3. Maven 与项目环境管理:别把时间浪费在“找 jar 包”上
很多人学 JavaEE 新框架的时候,最烦躁的其实不是代码,而是环境。jar 包去哪里下载?版本冲突了怎么办?项目为什么在这台电脑上能跑、换一台就报错?这些问题,遇过一次之后你就明白,Maven 不是可学可不学的工具,而是 JavaEE 开发的基础设施。
3.1 Maven 解决的核心问题:依赖、构建、约定
Maven 最核心的作用有三个:
- 依赖管理:你只需要在 pom.xml 里声明依赖的坐标,也就是 groupId、artifactId、version,Maven 就会自动从仓库下载相应的 jar 包,以及它依赖的其他 jar 包,也就是传递依赖。
- 构建流程统一:编译、测试、打包、部署这些步骤,Maven 用生命周期给你统一管理起来。你不需要手动敲 javac、jar,直接执行 mvn clean package 就行。
- 目录约定:源代码放在哪里、配置文件放在哪里、打包输出到哪里,Maven 有约定好的结构。你只需要照着它的约定去创建目录,团队协作时就不会出现“项目结构只有我自己懂”的情况。
3.2 坐标、仓库与依赖冲突排查
Maven 仓库分三级:本地仓库、中央仓库(以及配置的镜像)、远程私服。依赖查找顺序也是先本地,再镜像,再远程。你在 pom.xml 里引入一个新依赖,如果本地仓库没有,它会去镜像下载。
实际开发中,依赖冲突是最常见的坑。比如直接用 JUnit 4 还是 JUnit 5,Spring 和某个第三方库传递下来的 Spring 版本不一致,就会导致运行时报 NoSuchMethodError 或者 ClassNotFoundException。最常见的排查方式,是在 idea 的 Maven 面板里看依赖树,找到冲突,然后用排除依赖的方式解决。
我以前解决过一个印象深刻的问题:项目里有两个库都传递依赖了旧版的 commons-logging,结果启动时报日志相关的异常。查了半天发现是依赖树里出现了两个版本。解决方式就是在 pom.xml 里用 exclusion 排除掉较旧的那个传递依赖,显式声明需要的版本。这个排查过程本身,就是对 Maven 机制的实战检验。
3.3 新手必踩的几个坑与解决方案
- 本地仓库默认在 C 盘,一旦下载的依赖多了,C 盘空间不够,或者权限问题导致部分 jar 包损坏。建议改到自定义目录,在 settings.xml 里配置 localRepository。
- 依赖下载慢,大概率是镜像没配好。在 settings.xml 里配置国内镜像仓库,下载速度会有明显提升。
- idea 里改了 pom.xml 但项目没生效,多半是没刷新 Maven 项目。IDEA 有 Maven 面板,点 reload 按钮,或者命令行 mvn clean compile。
- 有时候明明在仓库里能看到 jar 包,但项目就是报红。最笨但有效的办法是:删除本地仓库里对应的目录,重新 mvn -U 强制刷新下载。
提示:Maven 不是 JavaEE 的某个框架,它更像你的“项目管家”。任何新加入的依赖、任何打包部署动作,都优先经过 Maven 来做,长期来看能避免很多环境问题。
4. Spring 与 SpringMVC:从“对象管理”到“请求处理”
到了 Spring 这个阶段,很多人会感觉前面学的东西突然“沉”下去了。因为 Spring 不是一个单一功能,而是一整套容器和生态。你只有理解了容器怎么管理对象、AOP 怎么解决横切逻辑、SpringMVC 怎么接收请求,才算是真正迈进了企业级开发的层次。
4.1 IOC 与 DI 如何改变对象管理方式
IOC(控制反转)听起来很玄,其实核心就是一个概念:对象的创建和管理不再由自己代码 new 来负责,而是交给容器。你在配置文件里或者用注解定义好 Bean,容器启动时帮你创建对象、维护它们之间的依赖关系。你需要用的时候,直接告诉容器我要哪个 Bean,容器就把装配好的对象给你。
DI(依赖注入)就是 IOC 的具体实现方式:容器在创建对象的时候,把依赖的属性主动塞进对象里,而不是让对象自己去找。
我第一次理解这个思想的时候,用了这么个类比:自己造房子是传统的做法,你要自己联系水泥、砖头、工人,还要管施工进度。IOC 的模型是什么呢?你只跟装修公司说出你的需求,装修公司帮你把材料准备好、把工人安排好,你只管住进去或者只管使用这个空间。对象之间的关系不由代码决定,而是由容器统一装配。
这个思想带来最直接的好处是代码解耦。你换一个实现类,只需要改配置或者改注解标注的类,而不用改业务逻辑里的 new。
4.2 AOP 到底在解决什么问题
AOP(面向切面编程)解决的是横切逻辑问题。业务代码要关心的事务、日志、权限校验,本质上和“计算订单金额”这种核心逻辑关系不大。如果你在每个业务方法里都手动写一遍事务开启和提交,代码会重复得没法看。
AOP 的做法是把这些横切逻辑抽取成切面,在方法调用前、后、异常抛出时插入指定逻辑。你只需要在配置里声明“哪些方法需要被拦截、拦截时要执行什么逻辑”,Spring 就用动态代理或 CGLIB 帮你在运行时把增强逻辑织入目标方法。
我当时是在实现一个转账功能时才真正理解 AOP 的价值的。两个账户之间转钱,必须保证同一个事务,中途任何一个 SQL 出错都要全部回滚。如果不用声明式事务,你得手动在 Service 层 try-catch 来回写 commit 和 rollback。用 Spring 的 @Transactional 注解,容器会自动在方法执行前开启事务、执行后提交、异常时回滚。这背后就是 AOP 在起作用。
4.3 SpringMVC 请求流转与 DispatcherServlet
SpringMVC 的学习核心只有一个:请求从进入到返回,经过了哪些组件。
一条典型链路是这样的:浏览器发送请求,Tomcat 收到后转发给 DispatcherServlet。DispatcherServlet 根据请求 URL 匹配 HandlerMapping,找到对应的 Controller 方法。然后通过 HandlerAdapter 调用 Controller 方法,方法返回 ModelAndView 或者直接返回 JSON。如果是视图解析,ViewResolver 会解析出具体的 JSP 或 HTML 页面。最后 DispatcherServlet 把结果写回浏览器。
我把这个流程背了大概五遍,才真正画得出流程图。画出来的那一瞬间就明白,为什么 Controller 方法可以只写业务逻辑,不用关心 Servlet API。因为从请求进入到返回,所有 Servlet 层级的处理细节,都被 DispatcherServlet 和它的组件封装好了。
4.4 SSM 整合的思路与配置细节
SSM 整合是很多 JavaEE 学习路线最后一个实战大项目。整合的意义,是让你把所有组件放在同一个项目里跑通。核心就三件事:Spring 管好 Service 层对象,SpringMVC 管好 Controller 层,MyBatis 管好数据库映射层。
配置方式分两种:1)用 XML 配置,手写 applicationContext.xml、springmvc.xml、mybatis-config.xml;2)用全注解和配置类。我建议第一遍老老实实用 XML 配置一次,因为整个过程中你会把扫描包、数据源、事务管理器、Mapper 扫描、视图解析器这些配置逐个弄明白,而不是复制粘贴一个自动配置类。
举一个常见的整合细节:Spring 容器和 SpringMVC 容器是两个容器,Spring 管 Service、Mapper,SpringMVC 管 Controller。如果包扫描配置错了,要么注入不到 Service,要么报重复 Bean 的错。很多整合失败的案例,问题都出在扫描范围上:spring 的扫描配置该写 service、dao 包,而不该把 controller 包也扫进去;SpringMVC 的扫描配置则只负责 controller 和相关 Web 组件。
注意:不要把所有包一股脑让两个容器都扫描,不然很容易出现事务不生效或者 Bean 覆盖的问题。这个坑,几乎每个做过 SSM 整合的人都会遇到一次。
5. 数据库访问:JDBC、连接池与 MyBatis
如果你不想只能做“展示数据的网页”,数据库这块必须学扎实。JavaEE 里操作数据库的方式是一步步演进的:从 JDBC 原生操作,到用连接池,到 MyBatis 半自动化框架,再到 Spring 封装好的 MyBatis。理解这条演进路径,你就知道框架到底帮你省了什么。
5.1 JDBC 原生操作与连接池的意义
JDBC 是 Java 操作数据库的标准接口。你自己手写的时候,步骤大概是:加载驱动 → 获取连接 → 创建 Statement/PreparedStatement → 执行 SQL → 处理 ResultSet → 关闭连接。
这里最明显的问题是:连接每次使用时都要创建,用完要关闭,频繁创建销毁开销很大。所以连接池就出现了,比如 HikariCP、Druid、C3P0。连接池的本质是预先创建一批数据库连接,用完放回池里复用,需要的时候从池里取,不关闭连接而是归还连接。你用代码看,获取和关闭连接的写法好像没变,但底层管理方式完全不同。
我建议你至少在初学阶段手动写一遍完整的 JDBC 增删改查,再封装一次 JDBCUtils 工具类。在封装的过程中,你对“为什么需要连接池”这个问题的理解会非常深。
5.2 MyBatis 的 Mapper、参数映射与动态 SQL
MyBatis 做的事情,是把 JDBC 的重复样板代码封装掉,让你只关注 SQL 和参数映射。它有两个关键概念:
- Mapper 接口:定义数据库操作方法,方法名对应 XML 里的 SQL id。
- Mapper XML:写 SQL 的地方,参数用 #{} 方式占位。
第一个要注意的点是 #{} 和 ${} 的区别。#{} 是预编译,传进去的值会作为参数值设置到 PreparedStatement 中,防 SQL 注入;${} 是字符串拼接,很多东西不能用它,比如表名、排序字段,但输入不可信时不要直接用 ${} 拼接 SQL。
第二个要掌握的是动态 SQL。MyBatis 的 if、choose、foreach 等标签是为了解决“查询条件不确定”的常见场景。我在实际项目里体会最深的是这种场景:列表页筛选条件可能有,也可能没有,你会写 according to 条件拼 SQL。用动态 SQL 之后,就不用在代码里手动拼接一堆 if else 字符串。
5.3 事务控制与数据库隔离级别
写数据库应用,事务肯定绕不开。事务的 ACID 特性,以及隔离级别,算是面试常考的重点,也是实际开发中会踩坑的地方。
隔离级别有四个:读未提交、读已提交、可重复读、串行化。MySQL 默认是可重复读,这意味着在一次事务里,多次读取同一数据的结果应该是一样的。但这也会带来潜在问题,比如幻读,MySQL 用多版本并发控制(MVCC)和行锁/间隙锁配合解决。
实际工作中,事务的配置粒度也很关键。Spring 的事务默认在 RuntimeException 上回滚,如果业务方法里把异常 catch 掉了,事务不会回滚,数据可能就写错了。我当时为了排查一个“数据明明报错但还是保存了”的 bug,看了好久代码,最后发现是 catch 了异常并做了日志输出。理解了事务代理的机制之后,这个问题一眼就能定位。
6. 学习过程中最常见的坑和排查技巧实录
这部分我想把自己实际踩过、以及身边很多初学者都遇到过的坑,集中列成一张速查表。每一项都是真实的调试经历,建议你遇到类似问题的时候优先对照这里。
6.1 环境启动类问题
- Tomcat 启动时端口被占用:8080 被其他进程占用了。排查方式:确认占用进程,要么改端口,要么结束占用进程。
- 部署时项目显示没有 Artifact:IDEA 里部署 Web 项目需要手动配置 war exploded 的部署方式,很多人忘记这一步。
- 启动后访问 404:先确认 URL 路径和项目 context path 对不对,再确认文件放在了 webapp/WEB-INF 里,以及 web.xml 是否加载到了 Servlet。
- ClassNotFoundException:多半是依赖没有打包到 WEB-INF/lib,需要在 IDEA 的 Artifacts 里把依赖加进去。
6.2 依赖与编译类问题
- 出现两个 Spring 版本:使用 mvn dependency:tree 查看依赖树,找出传递依赖冲突。
- Spring XML 文件开头报 schema 相关错误,一般是 applicationContext.xml 里的 xsd 网址访问不到,检查是否联网,或者本地有没有对应版本。
- 明明改了数据库配置,但启动还是连接的旧库:检查编译后的 target 目录里 applicationContext 相关的配置文件是否更新,有时候只是没 clean 导致旧配置还在。
6.3 设计与配置类问题
- Service 层加了 @Transactional 但事务不生效:可能原因:1)类没有被 Spring 管理,没有加 @Service;2)自调用方法,Spring 代理不会拦截同一个类内部的方法;3)异常被 catch 了,回滚失效。
- SpringMVC 请求转发到 JSP 时路径不对:检查视图解析器配置里的 prefix 和 suffix,以及 JSP 文件所在的目录是不是在 WEB-INF 下面。
- MyBatis 的 Mapper 接口和 XML 绑定不上:检查 namespace 是否等于接口全限定名,SQL id 是否等于方法名,以及 XML 文件有没有放进 target/classes。
6.4 这些问题为什么值得专门记录
很多初学者遇到这些问题会觉得很挫败,觉得“是我太笨了”。实际上,这些坑几乎所有人都会踩一遍,踩过一次、记录一次、排查一次,后面的速度会快很多。
我自己的习惯是,每次排查完一个问题,就整理一个短小的记录,包含现象、排查命令、最终原因、解决方案。一周之后回头看,你会发现自己解决问题的能力增长非常快。
7. 建议的学习节奏与配套练习方式
如果不需要赶时间,我的建议是用大约 6 到 8 周来走完上面这条主线。不需要每天高强度学习,但一定要保证每个知识点都有代码和实践支撑。
7.1 一套可执行的时间安排
- 第 1 周:JavaWeb 基础。重点掌握 HTTP、Servlet、Cookie/Session、Filter。手动写一个简单的登录注册页面,数据可以暂时存在内存集合理。
- 第 2 周:Maven。先用三天搞清楚 pom、依赖、生命周期,再用一周时间把一个之前手写的 Web 项目改造成 Maven 项目。
- 第 3 到 4 周:Spring 和 SpringMVC。用 XML 配置一个 IOC 容器,定义一个切面,再分别练习 SpringMVC 的表单提交、参数绑定、JSON 返回。完成 Spring 管业务、SpringMVC 管控制层的小整合。
- 第 5 周:MyBatis。用 XML 方式完成单表增删改查、多表查询、动态 SQL。再把 MyBatis 接入 Spring。
- 第 6 到 8 周:SSM 整合做项目。建议做一个有用户登录、列表展示、新增编辑、条件查询的小管理后台,每个功能都用一套完整链路:前端页面 → Controller → Service → Mapper → 数据库。
7.2 练习项目怎么选、怎么做
很多人做项目时喜欢找那种功能非常复杂的大项目,但我觉得第一个 SSM 项目,功能在精不在多。哪怕只是一个用户管理系统,只要你能自己独立把登录、增删改查、分页、条件查询、事务这些环节全部打通,你对 JavaEE 的理解就已经超过不少工作了几个月但只会照抄代码的人了。
练习过程中要注意:不要一上来就打开视频跟着敲。建议先自己想想功能流程、数据库表怎么建、接口怎么分,然后再看参考实现。遇到 bug 时先打印日志、断点调试,别急着看下一节视频。
做项目时我还推荐一个做法:给自己加一个额外的需求,比如给用户表加个状态字段,实现禁用用户不能登录的逻辑。这种超出原教程的小需求,最能锻炼你独立排查和设计的能力。
7.3 面试常问的串联问题
学完这一整套之后,你其实已经可以回答不少高频面试题了。试着把这些题目按主线回答一遍:
- 输入一个 URL 到页面展示出来,中间发生了什么?
- Servlet 和 Filter 的区别?
- 为什么用 Maven,依赖冲突怎么解决?
- Spring 的 IOC 是什么?AOP 用在什么场景?
- SpringMVC 的请求流转过程?
- MyBatis 的 #{} 和 ${} 区别,为什么说 #{} 能防 SQL 注入?
- 事务失效的常见原因有哪些?
把这几个问题讲清楚,你不但能建立起知识之间的联系,面试的时候也会更有底气。
最后再分享一个小经验:学习 JavaEE 千万别只看不写。看视频的时候什么都明白,关掉视频一上手就各种报错,这种感觉太正常了。报错不是坏事,它是在逼你去看底层机制。我刚学 Spring 的时候,光是容器初始化报错就调了两天,但就是在反复排查的过程中,把 Bean 的生命周期和依赖注入真正理解透了。框架可以越出越新,但底层这套“请求-处理-数据”的逻辑,永远是 JavaWeb 开发的基本功。后面就算你切换到 SpringBoot、SpringCloud,这段基础也会一直陪着你。