尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

JavaWeb实战:从Maven配置到MyBatis整合的完整开发流程解析

JavaWeb实战:从Maven配置到MyBatis整合的完整开发流程解析 1. 从零到一JavaWeb技术栈的实战脉络梳理如果你刚学完Java基础准备一头扎进JavaWeb的世界或者正在跟着黑马的视频做笔记感觉知识点又多又杂理不清头绪那你来对地方了。我当年也是这么过来的看视频时感觉都懂了一关上播放器脑子里就只剩下Servlet、JDBC、Maven这些名词在打架完全不知道它们之间是怎么串起来的更别提自己动手搭一个项目了。这篇文章就是帮你把这些散落的知识点像拼图一样按照一个真实项目从无到有的构建顺序重新组装起来。我们不谈空泛的理论就从一个最简单的需求开始做一个能通过浏览器访问并且能连接数据库、展示数据的Web应用。你会发现Servlet、JDBC、Maven、MyBatis这些技术不再是孤立的“知识点”而是你工具箱里解决特定问题的“螺丝刀”和“扳手”。2. 项目基石Maven的环境搭建与核心思想在真正写一行Web代码之前我们得先把“工地”平整好。这个工地就是我们的开发环境而Maven就是管理这个工地的“项目经理”。很多新手会直接去搜“Maven下载安装与配置”然后照着步骤一通操作但往往知其然不知其所以然后面遇到依赖冲突、仓库地址不对等问题就懵了。2.1 为什么是Maven它解决了什么痛点在没有Maven的年代或者现在你手动管理jar包开发一个JavaWeb项目是相当痛苦的。你需要去各个官网下载servlet-api.jar、mysql-connector-java.jar等等手动复制到项目的lib目录下。如果A.jar依赖B.jar你还得自己去把B.jar也找来。更麻烦的是当你把项目发给别人或者换一台电脑时你必须确保对方也有完全相同的jar包版本还不能错。这种管理方式效率低下且极易出错。Maven的核心价值就在于依赖管理和项目构建。它通过一个中心化的仓库Maven Central Repository来管理全世界绝大部分的Java开源库。你只需要在项目根目录的pom.xml文件中声明你需要什么库即依赖DependencyMaven就会自动从仓库下载这些库并且会递归下载这些库所依赖的其他库。pom.xml就是这个项目的“物料清单”。2.2 手把手配置避开那些“坑爹”的默认设置安装Maven本身很简单去官网下载二进制包解压然后配置环境变量MAVEN_HOME和PATH。这个步骤网上一搜一大把。我想强调的是安装后的关键配置这往往是视频里一笔带过但实际开发中影响巨大的部分。首先必须修改Maven的本地仓库位置。默认的仓库路径在C盘用户目录下的.m2/repository随着项目增多这个文件夹会变得非常庞大挤占C盘空间。我习惯在D盘或其它非系统盘创建一个目录比如D:\maven-repo。然后打开Maven安装目录下conf/settings.xml文件找到localRepository标签修改为你的自定义路径localRepositoryD:\maven-repo/localRepository其次必须配置国内镜像仓库。直接从中央仓库下载依赖速度慢如蜗牛还经常失败。我们需要将仓库地址指向国内的镜像站最常用的就是阿里云镜像。在settings.xml的mirrors标签内添加mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror这个配置意味着所有对中央仓库的请求都会被重定向到阿里云的服务器下载速度会有质的飞跃。有些教程会教你配置多个镜像但对于新手一个稳定快速的阿里云镜像足够了。最后在IDE中关联Maven。以IntelliJ IDEA为例打开File - Settings - Build, Execution, Deployment - Build Tools - Maven将Maven home path指向你的Maven安装目录User settings file指向你刚才修改过的settings.xmlLocal repository会自动识别为你上面配置的路径。完成这三步你的Maven环境才算真正可用。注意settings.xml的修改必须在你创建或导入Maven项目之前生效。如果你已经有一个项目修改后需要重启IDEA并执行Maven - Reload Project或者点击右侧Maven工具栏的刷新按钮让配置重新加载。2.3 理解pom.xml你的项目“身份证”创建一个Maven项目后你会得到一个pom.xml。别被它一开始的简单样子骗了这是项目的核心配置文件。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion !-- 项目坐标全球唯一标识符 -- groupIdcom.yourcompany/groupId !-- 组织或公司域名倒序 -- artifactIdmy-first-webapp/artifactId !-- 项目名 -- version1.0-SNAPSHOT/version !-- 版本号SNAPSHOT表示开发中版本 -- packagingwar/packaging !-- 打包方式Web项目必须是war -- properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies !-- 依赖声明将在这里添加 -- /dependencies /project重点看packagingwar/packaging这指定了项目最终打包成Web应用归档格式这是部署到Tomcat等Servlet容器的标准格式。dependencies标签内就是我们声明所有第三方库的地方。随着项目进行这里会逐渐丰富起来。3. Web入口Servlet的本质与生命周期探秘环境搭好了我们可以开始写Web代码了。而所有JavaWeb应用的起点就是Servlet。很多人学Servlet就记住了要继承HttpServlet重写doGet和doPost方法但为什么要这么做Servlet到底是个什么东西3.1 Servlet不是“类”而是一种“规范”首先得澄清一个概念我们常说的“写一个Servlet”指的是编写一个实现了Servlet接口通常继承HttpServlet的Java类。但Servlet本身是一套标准一套由Java Servlet规范定义的、用于处理Web请求和响应的API。Tomcat、Jetty这些我们称之为Servlet容器的软件才是这套规范的实现者。你的Servlet类是运行在容器里的。你可以把Servlet容器想象成一个餐厅的后厨系统而Servlet就是后厨里的一道道标准化工序比如炒菜、蒸饭。客户浏览器的点单请求HTTP Request通过服务员Tomcat的连接器传到后厨后厨系统根据点单内容决定由哪道工序哪个Servlet来处理处理完后把菜品HTTP Response传回给客户。你的工作就是按照工序的标准实现Servlet接口编写具体的处理逻辑。3.2 手写第一个Servlet理解配置与注解两种方式假设我们要处理一个/hello的请求返回“Hello World”。传统方式web.xml配置和现代方式注解你都需要了解。方式一web.xml配置理解原理创建一个类继承HttpServlet重写doGet方法。package com.demo.web; import javax.servlet.http.*; import javax.servlet.*; import java.io.IOException; import java.io.PrintWriter; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetUTF-8); PrintWriter out resp.getWriter(); out.println(h1Hello World from web.xml!/h1); } }在WEB-INF/web.xml中配置这个Servlet建立URL到Servlet类的映射。web-app ... servlet servlet-namehello/servlet-name servlet-classcom.demo.web.HelloServlet/servlet-class /servlet servlet-mapping servlet-namehello/servlet-name url-pattern/hello/url-pattern /servlet-mapping /web-app这个配置告诉容器当访问/hello路径时请实例化com.demo.web.HelloServlet这个类来处理请求。这种方式很直观但配置繁琐且集中在一个文件里不利于管理。方式二使用WebServlet注解推荐从Servlet 3.0规范开始支持使用注解来配置Servlet省去了web.xml的配置。package com.demo.web; import javax.servlet.annotation.WebServlet; // ... 其他import WebServlet(/hello) // 关键注解一行搞定映射 public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().println(h1Hello World from Annotation!/h1); } }使用注解后容器在启动时会自动扫描类路径发现带有WebServlet注解的类就自动完成注册。这大大简化了配置。现在主流开发基本都使用注解方式。3.3 深入Servlet生命周期它何时生何时死理解生命周期对于写出高效、稳定的Servlet至关重要。Servlet的生命周期由容器全权管理主要包括三个阶段初始化 (init)当容器启动后或第一次有请求访问某个Servlet时容器会调用其无参构造函数创建Servlet实例然后调用一次init(ServletConfig config)方法。这个方法通常用于加载一次性资源比如数据库连接池、读取配置文件等。切记一个Servlet在容器中通常只有一个实例单例所有请求共享这个实例。所以不要在Servlet中定义可变的成员变量否则会导致线程安全问题。服务 (service)对于每次请求容器会调用service方法。HttpServlet的service方法会根据请求类型GET、POST等自动分派到对应的doGet、doPost等方法。你的业务逻辑主要写在这里。这个方法会被多线程并发调用因此必须保证线程安全。销毁 (destroy)当容器关闭或应用被卸载时容器会调用destroy方法。你可以在这里释放init方法中申请的资源如关闭数据库连接、清理临时文件等。一个常见的误区是认为每次请求都会创建一个新的Servlet对象。实际上为了性能容器采用了单例多线程的模式。这解释了为什么我们通常把数据库连接等对象作为方法内的局部变量或者使用ThreadLocal等技术来保证线程安全而不是作为Servlet的成员变量。4. 数据桥梁JDBC的原始操作与连接池化思想我们的Servlet能处理请求了但一个动态网站离不开数据。Java连接数据库的标准API就是JDBC。很多教程只教了怎么用却没讲清楚为什么每一步要那么做以及最关键的——如何管理宝贵的数据库连接。4.1 最原始的JDBC操作流程我们以连接MySQL为例回顾一下最基础的JDBC操作步骤这能帮你理解底层发生了什么加载驱动Class.forName(com.mysql.cj.jdbc.Driver);。这一步是告诉JDBC“我要用MySQL的驱动实现”。高版本JDBC可以省略通过SPI机制自动发现但显式加载更稳妥。获取连接Connection conn DriverManager.getConnection(url, user, password);。这是最昂贵的一步因为建立真实的TCP连接、进行数据库握手、验证身份等开销非常大。创建语句对象Statement stmt conn.createStatement();或PreparedStatement pstmt conn.prepareStatement(sql);。强烈建议永远使用PreparedStatement因为它可以预编译SQL提高效率更重要的是能有效防止SQL注入攻击。执行SQLResultSet rs stmt.executeQuery(sql);查询或int rows stmt.executeUpdate(sql);增删改。处理结果集遍历ResultSet对象获取数据。释放资源严格按照rs.close()-stmt.close()-conn.close()的顺序在finally块中关闭资源确保即使发生异常连接也能被释放。一段典型的查询代码如下Connection conn null; PreparedStatement pstmt null; ResultSet rs null; try { Class.forName(com.mysql.cj.jdbc.Driver); conn DriverManager.getConnection(jdbc:mysql://localhost:3306/test?useSSLfalseserverTimezoneUTC, root, password); String sql SELECT id, name FROM user WHERE id ?; pstmt conn.prepareStatement(sql); pstmt.setInt(1, 10); // 设置参数防止SQL注入 rs pstmt.executeQuery(); while (rs.next()) { int id rs.getInt(id); String name rs.getString(name); System.out.println(id : name); } } catch (Exception e) { e.printStackTrace(); } finally { // 逆序关闭资源 if (rs ! null) try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } if (pstmt ! null) try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } if (conn ! null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }这段代码的问题显而易见模板代码太多每个数据库操作都要写一遍try-catch-finally资源管理繁琐容易忘记关闭最关键的是频繁创建和关闭连接性能极差。4.2 连接池解决性能瓶颈的必由之路想象一下每次有用户请求需要查数据库你都现场打一口井创建连接用完再把井填上关闭连接。这无疑是灾难性的。连接池就是预先打好一批井创建一批连接放在一个“池子”里管理。当程序需要连接时就从池子里借一个用用完了不是真的关闭而是还回池子里供下一个请求使用。常见的连接池有DBCP、C3P0以及目前最主流的HikariCP以高性能著称。在Spring Boot项目中默认就使用了HikariCP。我们如何在非Spring的普通Web项目中使用呢通常我们会配合像Apache Commons DBCP2这样的库或者直接使用HikariCP。以HikariCP为例我们不再使用DriverManager.getConnection而是配置一个HikariDataSource// 通常在应用启动时如Servlet的init方法或监听器初始化连接池 HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/test); config.setUsername(root); config.setPassword(password); config.addDataSourceProperty(cachePrepStmts, true); config.addDataSourceProperty(prepStmtCacheSize, 250); config.addDataSourceProperty(prepStmtCacheSqlLimit, 2048); HikariDataSource dataSource new HikariDataSource(config); // 在需要连接的地方 try (Connection conn dataSource.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { // ... 执行操作 // 注意这里不需要手动关闭conntry-with-resources语句会自动将其“还回”连接池 }使用连接池后获取连接dataSource.getConnection()的成本变得极低因为大部分时候它只是从池里取出一个现成的、可用的连接。conn.close()也变成了“归还连接”的操作。这极大地提升了应用性能和高并发能力。实操心得连接池的参数配置如最大连接数、最小空闲数、连接超时时间需要根据实际应用压力和数据库性能来调整不是越大越好。过大的连接数会拖垮数据库。一个经验是最大连接数可以设置为(核心数 * 2) 有效磁盘数作为起点进行测试。5. 持久层进化MyBatis如何优雅地替代JDBC即使使用了连接池和工具类封装原始的JDBC操作依然面临几个顽疾SQL与Java代码混杂、手动设置参数和结果集映射非常繁琐、数据库变更时代码修改量大。这时ORM框架就登场了而MyBatis是JavaWeb领域最流行、最灵活的持久层框架之一。它不像Hibernate那样试图完全屏蔽SQL而是让开发者自己编写SQL它负责将SQL执行结果映射成Java对象。5.1 核心配置文件与映射文件MyBatis的“双轮驱动”MyBatis的配置围绕两个核心XML文件展开mybatis-config.xml全局配置和XxxMapper.xmlSQL映射文件。全局配置 (mybatis-config.xml)通常放在资源目录下它定义了MyBatis的行为。?xml version1.0 encodingUTF-8? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd configuration settings !-- 开启下划线到驼峰命名的自动映射 -- setting namemapUnderscoreToCamelCase valuetrue/ !-- 配置日志实现方便调试 -- setting namelogImpl valueSTDOUT_LOGGING/ /settings typeAliases !-- 给实体类起别名在Mapper.xml中就不用写全限定名了 -- typeAlias typecom.demo.entity.User aliasUser/ /typeAliases environments defaultdevelopment environment iddevelopment transactionManager typeJDBC/ dataSource typePOOLED !-- 这里使用了MyBatis内置的连接池 -- property namedriver valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/test/ property nameusername valueroot/ property namepassword valuepassword/ /dataSource /environment /environments mappers !-- 注册Mapper.xml文件 -- mapper resourcecom/demo/mapper/UserMapper.xml/ /mappers /configuration在实际项目中我们更倾向于用Java代码配置SqlSessionFactory或者直接集成到Spring中用Spring来管理数据源和事务这样更灵活。但理解这个XML结构有助于你明白MyBatis的组成部分。SQL映射文件 (UserMapper.xml)这是MyBatis的灵魂所在SQL写在这里。?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.demo.mapper.UserMapper !-- 命名空间通常对应Mapper接口的全限定名 -- !-- 结果映射定义数据库字段如何映射到Java对象的属性 -- resultMap iduserResultMap typeUser id propertyid columnid/ result propertyuserName columnuser_name/ !-- 驼峰映射 -- result propertyemail columnemail/ /resultMap !-- 查询语句 -- select idselectUserById resultMapuserResultMap SELECT id, user_name, email FROM user WHERE id #{id} /select !-- 插入语句useGeneratedKeys获取自增主键 -- insert idinsertUser parameterTypeUser useGeneratedKeystrue keyPropertyid INSERT INTO user (user_name, email) VALUES (#{userName}, #{email}) /insert !-- 动态SQL根据条件查询 -- select idselectUsersByCondition resultMapuserResultMap SELECT * FROM user where if testuserName ! null and userName ! AND user_name LIKE CONCAT(%, #{userName}, %) /if if testemail ! null AND email #{email} /if /where /select /mapper#{id}是MyBatis的参数占位符它会被预处理防止SQL注入等同于JDBC中的?。${}是字符串替换有注入风险除非必要如动态表名、列名否则不要使用。5.2 Mapper接口与动态代理为什么接口不用实现这是MyBatis最精妙的设计之一。你定义了一个接口package com.demo.mapper; import com.demo.entity.User; import java.util.List; public interface UserMapper { User selectUserById(Integer id); int insertUser(User user); ListUser selectUsersByCondition(User user); }然后你并没有编写这个接口的实现类。但是在代码中你可以这样使用SqlSession sqlSession sqlSessionFactory.openSession(); try { UserMapper mapper sqlSession.getMapper(UserMapper.class); // 关键 User user mapper.selectUserById(1); System.out.println(user.getUserName()); } finally { sqlSession.close(); }sqlSession.getMapper(UserMapper.class)返回了一个动态代理对象。MyBatis在运行时会根据UserMapper.xml中namespace的值必须是接口的全限定名和SQL语句的id值必须与接口方法名一致将接口方法的调用动态绑定到对应的SQL语句上并执行。这实现了接口与SQL的完全解耦让数据访问层的代码变得异常简洁。5.3 高级特性与实战避坑动态SQLMyBatis提供了强大的动态SQL标签if,where,choose,foreach等可以灵活地拼接SQL避免在Java代码中写丑陋的字符串拼接。上面例子中的where和if就是经典用法。结果集映射除了简单的自动映射开启mapUnderscoreToCamelCase后user_name能自动映射到userName属性复杂场景如一对一、一对多关联查询需要定义详细的resultMap。这是MyBatis学习的重点和难点但也是其灵活性的体现。关于MyBatis-Plus这是MyBatis的增强工具在MyBatis的基础上只做增强不做改变。它提供了通用的Mapper和Service内置了分页、性能分析、代码生成器等插件可以极大地简化单表的CRUD操作。例如你甚至可以不写任何XML通过继承BaseMapperUser就拥有了selectById、insert、update、delete、selectPage等常用方法。但对于复杂的多表关联查询和定制化SQL依然需要回归到MyBatis原生的XML或注解方式。避坑指南MyBatis的SQL写在XML里调试不方便。强烈推荐安装MyBatis Log Plugin这类插件IDEA有对应插件它可以将MyBatis执行的SQL语句及其参数完美地拼接成完整的、可以直接在数据库客户端执行的SQL对于排查问题至关重要。另外关于分页MyBatis本身不提供物理分页需要借助PageHelper这样的插件或自己写LIMIT语句。MyBatis-Plus则内置了性能很好的分页插件。6. 整合与部署将一切组装成可运行的应用现在我们有了处理请求的Servlet控制器有了操作数据库的MyBatis持久层还差一个将它们组织起来的“粘合剂”以及最终打包部署的步骤。6.1 原始的MVC模式与分层架构在引入Spring MVC等高级框架之前我们可以用最原始的方式实现MVCM (Model模型)实体类如User和业务逻辑/数据访问对象如UserService,UserDao/UserMapper。V (View视图)JSP页面负责展示数据。Servlet将处理好的数据Model通过request.setAttribute放入请求域然后转发RequestDispatcher.forward到JSP页面JSP使用EL表达式和JSTL标签取出数据并渲染成HTML。C (Controller控制器)Servlet负责接收请求、调用Service处理业务、组装数据、跳转视图。一个典型的Servlet处理流程如下WebServlet(/user/list) public class UserListServlet extends HttpServlet { private UserService userService new UserServiceImpl(); // 通常通过工厂或IoC容器获取 Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 接收参数如果有 String page req.getParameter(page); // 2. 调用业务层处理 ListUser userList userService.getAllUsers(Integer.parseInt(page)); // 3. 将数据存入请求域传递给JSP req.setAttribute(userList, userList); // 4. 请求转发到JSP页面 req.getRequestDispatcher(/WEB-INF/jsp/userList.jsp).forward(req, resp); } }对应的JSP页面 (userList.jsp)% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle用户列表/title/head body table c:forEach items${userList} varuser tr td${user.id}/td td${user.userName}/td td${user.email}/td /tr /c:forEach /table /body /html这就是最经典的JavaWeb MVC模式。它的缺点是Servlet作为控制器过于笨重每个请求都要写一个Servlet配置和映射繁琐。这催生了像Spring MVC这样更优雅的框架它通过一个DispatcherServlet作为总入口利用注解Controller,RequestMapping来灵活定义处理器大大简化了开发。6.2 使用Maven打包与Tomcat部署开发完成后我们需要将项目部署到Servlet容器如Tomcat中运行。Maven的packaging类型为war就是为此准备的。首先确保pom.xml中包含了Servlet API等必要的依赖并且作用域scope为provided因为Tomcat容器本身会提供这些库。dependencies !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope !-- 关键 -- /dependency !-- JSP API -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- JSTL -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- MyBatis -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.13/version /dependency /dependencies然后在项目根目录下执行Maven命令进行打包mvn clean packageMaven会执行清理、编译、测试如果有、打包等一系列操作。最终在target目录下生成一个项目名-版本.war文件例如my-first-webapp-1.0-SNAPSHOT.war。部署到Tomcat将生成的.war文件复制到Tomcat的webapps目录下。启动Tomcat运行bin/startup.bat或startup.sh。Tomcat会自动解压war包并加载应用。此时你可以通过浏览器访问http://localhost:8080/my-first-webapp/hello来测试你的Servlet。在开发阶段我们更常用的是在IDE如IDEA中配置Tomcat进行热部署这样修改代码后无需重启整个Tomcat只需重新加载应用即可极大提升开发效率。6.3 项目结构标准化一个清晰的Maven Web项目结构如下my-first-webapp ├── src │ ├── main │ │ ├── java // Java源代码 │ │ │ └── com │ │ │ └── demo │ │ │ ├── entity // 实体类 │ │ │ ├── mapper // MyBatis Mapper接口 │ │ │ ├── service // 业务层接口和实现 │ │ │ └── web // Servlet控制器 │ │ ├── resources // 资源文件 │ │ │ ├── mybatis-config.xml // MyBatis全局配置可选 │ │ │ └── com/demo/mapper // MyBatis Mapper XML文件 │ │ └── webapp // Web资源 │ │ ├── WEB-INF │ │ │ ├── web.xml // 部署描述符Servlet 3.0可省略 │ │ │ └── lib // 依赖jar包Maven项目通常为空 │ │ └── index.jsp // 首页 │ └── test // 测试代码 ├── pom.xml // Maven项目对象模型 └── target // 编译输出目录由Maven生成遵循这样的结构能让你的项目更易于理解和维护。走完这一整套流程从环境搭建(Maven)到请求处理(Servlet)再到数据存取(JDBC/MyBatis)最后到打包部署你对JavaWeb的核心技术栈就有了一个贯通的理解。这不再是分散的知识点而是一条清晰的、可实践的技能链路。接下来无论你是学习更高级的Spring框架还是应对实际项目开发都有了坚实的地基。记住理解原理和动手实践永远是学习技术最快的方式。
返回列表