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.jarmysql-connector-java.jar等等,手动复制到项目的lib目录下。如果A.jar依赖B.jar,你还得自己去把B.jar也找来。更麻烦的是,当你把项目发给别人,或者换一台电脑时,你必须确保对方也有完全相同的jar包,版本还不能错。这种管理方式,效率低下且极易出错。

Maven的核心价值就在于依赖管理项目构建。它通过一个中心化的仓库(Maven Central Repository)来管理全世界绝大部分的Java开源库。你只需要在项目根目录的pom.xml文件中,声明你需要什么库(即依赖,Dependency),Maven就会自动从仓库下载这些库,并且会递归下载这些库所依赖的其他库。pom.xml就是这个项目的“物料清单”。

2.2 手把手配置:避开那些“坑爹”的默认设置

安装Maven本身很简单,去官网下载二进制包,解压,然后配置环境变量MAVEN_HOMEPATH。这个步骤网上一搜一大把。我想强调的是安装后的关键配置,这往往是视频里一笔带过,但实际开发中影响巨大的部分。

首先,必须修改Maven的本地仓库位置。默认的仓库路径在C盘用户目录下的.m2/repository,随着项目增多,这个文件夹会变得非常庞大,挤占C盘空间。我习惯在D盘或其它非系统盘创建一个目录,比如D:\maven-repo。然后打开Maven安装目录下conf/settings.xml文件,找到<localRepository>标签,修改为你的自定义路径:

<localRepository>D:\maven-repo</localRepository>

其次,必须配置国内镜像仓库。直接从中央仓库下载依赖,速度慢如蜗牛,还经常失败。我们需要将仓库地址指向国内的镜像站,最常用的就是阿里云镜像。在settings.xml<mirrors>标签内添加:

<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://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 version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- 项目坐标:全球唯一标识符 --> <groupId>com.yourcompany</groupId> <!-- 组织或公司域名倒序 --> <artifactId>my-first-webapp</artifactId> <!-- 项目名 --> <version>1.0-SNAPSHOT</version> <!-- 版本号,SNAPSHOT表示开发中版本 --> <packaging>war</packaging> <!-- 打包方式,Web项目必须是war --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- 依赖声明将在这里添加 --> </dependencies> </project>

重点看<packaging>war</packaging>,这指定了项目最终打包成Web应用归档格式,这是部署到Tomcat等Servlet容器的标准格式。<dependencies>标签内,就是我们声明所有第三方库的地方。随着项目进行,这里会逐渐丰富起来。

3. Web入口:Servlet的本质与生命周期探秘

环境搭好了,我们可以开始写Web代码了。而所有JavaWeb应用的起点,就是Servlet。很多人学Servlet,就记住了要继承HttpServlet,重写doGetdoPost方法,但为什么要这么做?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配置(理解原理)

  1. 创建一个类,继承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;charset=UTF-8"); PrintWriter out = resp.getWriter(); out.println("<h1>Hello World from web.xml!</h1>"); } }
  2. WEB-INF/web.xml中配置这个Servlet,建立URL到Servlet类的映射。
    <web-app ...> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.demo.web.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</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;charset=UTF-8"); resp.getWriter().println("<h1>Hello World from Annotation!</h1>"); } }

使用注解后,容器在启动时会自动扫描类路径,发现带有@WebServlet注解的类,就自动完成注册。这大大简化了配置。现在主流开发基本都使用注解方式。

3.3 深入Servlet生命周期:它何时生,何时死?

理解生命周期,对于写出高效、稳定的Servlet至关重要。Servlet的生命周期由容器全权管理,主要包括三个阶段:

  1. 初始化 (init):当容器启动后,或第一次有请求访问某个Servlet时,容器会调用其无参构造函数创建Servlet实例,然后调用一次init(ServletConfig config)方法。这个方法通常用于加载一次性资源,比如数据库连接池、读取配置文件等。切记,一个Servlet在容器中通常只有一个实例(单例),所有请求共享这个实例。所以不要在Servlet中定义可变的成员变量,否则会导致线程安全问题。
  2. 服务 (service):对于每次请求,容器会调用service方法。HttpServletservice方法会根据请求类型(GET、POST等),自动分派到对应的doGetdoPost等方法。你的业务逻辑主要写在这里。这个方法会被多线程并发调用,因此必须保证线程安全。
  3. 销毁 (destroy):当容器关闭或应用被卸载时,容器会调用destroy方法。你可以在这里释放init方法中申请的资源,如关闭数据库连接、清理临时文件等。

一个常见的误区是,认为每次请求都会创建一个新的Servlet对象。实际上,为了性能,容器采用了单例多线程的模式。这解释了为什么我们通常把数据库连接等对象作为方法内的局部变量,或者使用ThreadLocal等技术来保证线程安全,而不是作为Servlet的成员变量。

4. 数据桥梁:JDBC的原始操作与连接池化思想

我们的Servlet能处理请求了,但一个动态网站离不开数据。Java连接数据库的标准API就是JDBC。很多教程只教了怎么用,却没讲清楚为什么每一步要那么做,以及最关键的——如何管理宝贵的数据库连接。

4.1 最原始的JDBC操作流程

我们以连接MySQL为例,回顾一下最基础的JDBC操作步骤,这能帮你理解底层发生了什么:

  1. 加载驱动Class.forName("com.mysql.cj.jdbc.Driver");。这一步是告诉JDBC:“我要用MySQL的驱动实现”。高版本JDBC可以省略,通过SPI机制自动发现,但显式加载更稳妥。
  2. 获取连接Connection conn = DriverManager.getConnection(url, user, password);。这是最昂贵的一步,因为建立真实的TCP连接、进行数据库握手、验证身份等,开销非常大。
  3. 创建语句对象Statement stmt = conn.createStatement();PreparedStatement pstmt = conn.prepareStatement(sql);强烈建议永远使用PreparedStatement,因为它可以预编译SQL,提高效率,更重要的是能有效防止SQL注入攻击。
  4. 执行SQLResultSet rs = stmt.executeQuery(sql);(查询)或int rows = stmt.executeUpdate(sql);(增删改)。
  5. 处理结果集:遍历ResultSet对象,获取数据。
  6. 释放资源:严格按照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?useSSL=false&serverTimezone=UTC", "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)) { // ... 执行操作 // 注意:这里不需要手动关闭conn,try-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.xml(SQL映射文件)。

全局配置 (mybatis-config.xml):通常放在资源目录下,它定义了MyBatis的行为。

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 开启下划线到驼峰命名的自动映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 配置日志实现,方便调试 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <typeAliases> <!-- 给实体类起别名,在Mapper.xml中就不用写全限定名了 --> <typeAlias type="com.demo.entity.User" alias="User"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 这里使用了MyBatis内置的连接池 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <mappers> <!-- 注册Mapper.xml文件 --> <mapper resource="com/demo/mapper/UserMapper.xml"/> </mappers> </configuration>

在实际项目中,我们更倾向于用Java代码配置SqlSessionFactory,或者直接集成到Spring中,用Spring来管理数据源和事务,这样更灵活。但理解这个XML结构有助于你明白MyBatis的组成部分。

SQL映射文件 (UserMapper.xml):这是MyBatis的灵魂所在,SQL写在这里。

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.demo.mapper.UserMapper"> <!-- 命名空间,通常对应Mapper接口的全限定名 --> <!-- 结果映射:定义数据库字段如何映射到Java对象的属性 --> <resultMap id="userResultMap" type="User"> <id property="id" column="id"/> <result property="userName" column="user_name"/> <!-- 驼峰映射 --> <result property="email" column="email"/> </resultMap> <!-- 查询语句 --> <select id="selectUserById" resultMap="userResultMap"> SELECT id, user_name, email FROM user WHERE id = #{id} </select> <!-- 插入语句,useGeneratedKeys获取自增主键 --> <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (user_name, email) VALUES (#{userName}, #{email}) </insert> <!-- 动态SQL:根据条件查询 --> <select id="selectUsersByCondition" resultMap="userResultMap"> SELECT * FROM user <where> <if test="userName != null and userName != ''"> AND user_name LIKE CONCAT('%', #{userName}, '%') </if> <if test="email != 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); List<User> 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.xmlnamespace的值(必须是接口的全限定名)和SQL语句的id值(必须与接口方法名一致),将接口方法的调用,动态绑定到对应的SQL语句上并执行。这实现了接口与SQL的完全解耦,让数据访问层的代码变得异常简洁。

5.3 高级特性与实战避坑

动态SQL:MyBatis提供了强大的动态SQL标签(<if>,<where>,<choose>,<foreach>等),可以灵活地拼接SQL,避免在Java代码中写丑陋的字符串拼接。上面例子中的<where><if>就是经典用法。

结果集映射:除了简单的自动映射(开启mapUnderscoreToCamelCase后,user_name能自动映射到userName属性),复杂场景(如一对一、一对多关联查询)需要定义详细的<resultMap>。这是MyBatis学习的重点和难点,但也是其灵活性的体现。

关于MyBatis-Plus:这是MyBatis的增强工具,在MyBatis的基础上只做增强不做改变。它提供了通用的MapperService,内置了分页、性能分析、代码生成器等插件,可以极大地简化单表的CRUD操作。例如,你甚至可以不写任何XML,通过继承BaseMapper<User>,就拥有了selectByIdinsertupdatedeleteselectPage等常用方法。但对于复杂的多表关联查询和定制化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等高级框架之前,我们可以用最原始的方式实现MVC:

  • M (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. 调用业务层处理 List<User> 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 contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>用户列表</title></head> <body> <table> <c:forEach items="${userList}" var="user"> <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> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> <!-- 关键! --> </dependency> <!-- JSP API --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</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>8.0.33</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.13</version> </dependency> </dependencies>

然后,在项目根目录下执行Maven命令进行打包:

mvn clean package

Maven会执行清理、编译、测试(如果有)、打包等一系列操作。最终在target目录下生成一个项目名-版本.war文件(例如my-first-webapp-1.0-SNAPSHOT.war)。

部署到Tomcat

  1. 将生成的.war文件复制到Tomcat的webapps目录下。
  2. 启动Tomcat(运行bin/startup.batstartup.sh)。
  3. 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框架,还是应对实际项目开发,都有了坚实的地基。记住,理解原理和动手实践,永远是学习技术最快的方式。

返回列表