Java Web核心开发实战:基于年龄与姓名的信息处理案例
简介:Java Web是基于Java技术的动态Web应用开发框架,涵盖服务器端编程、数据库交互与前端展示。本课程以“年龄+姓名”为教学示例,系统讲解Servlet、JSP、MVC模式、EL表达式、JSTL标签库等核心技术,帮助学习者掌握用户数据的接收、处理、验证与会话管理,并结合JDBC实现数据持久化。通过该实例,学生可深入理解Java Web的开发流程与设计模式,为后续使用Spring、Hibernate等主流框架打下坚实基础。
Java Web全栈开发实战:从零构建用户信息管理系统
在今天的互联网世界里,几乎每一个网站背后都运行着一套复杂的Web应用系统。你有没有想过,当你登录一个网站、填写表单或者查看个人信息时,背后的“魔法”是如何发生的?🤔 这些看似简单的操作,其实依赖于一整套精密协作的技术体系——而今天我们要深入探索的,就是这套体系的核心: Java Web开发技术栈 。
想象一下,你要做一个能记住用户姓名和年龄的小系统。听起来不难对吧?但问题来了:HTTP协议天生是“健忘”的,它不会记得你是谁;浏览器只能显示静态内容,没法直接连数据库;而且你还得防止恶意输入破坏系统……这些问题该怎么解决?
别担心!我们即将踏上一段旅程,从最基础的Servlet讲起,一路打通JSP视图渲染、MVC分层架构、会话管理(Session),直到最后用JDBC连接MySQL完成数据持久化。最终我们会亲手打造一个完整的“用户信息管理系统”,让你真正理解现代Web应用到底是怎么跑起来的 💪。
准备好了吗?让我们开始吧!
技术基石:Java Web是怎么工作的?
先来点轻松的。你知道当你在浏览器地址栏敲下 http://localhost:8080/hello 并按下回车后,到底发生了什么吗?这短短一瞬间,其实上演了一场精密的“网络芭蕾”。
整个过程可以简化为这样一条链路:
flowchart LR
A[客户端浏览器] -->|HTTP请求| B(Tomcat服务器)
B -->|动态处理| C{Servlet/JSP}
C -->|数据库交互| D[(MySQL)]
B -->|HTTP响应| A
没错,这就是经典的 B/S架构 (Browser/Server)。你的浏览器发出请求 → Tomcat接收到并交给某个Java程序处理 → 程序可能去查数据库 → 最后再把结果包装成HTML发回给你。整个流程遵循“请求-响应”模型,简单却强大。
而支撑这一切的,正是Java语言跨平台的优势 + Servlet规范的强大生态。自1999年诞生以来,Java Web技术已经发展出一套成熟稳定的开发范式,尤其适合企业级应用。虽然现在前端框架百花齐放,但后端逻辑依然离不开这些底层机制。
说到Tomcat,它是Apache基金会出品的一个轻量级Web容器,实现了Servlet和JSP规范。你可以把它看作是一个“Java程序的管家”,负责启动、加载、调度我们的代码。安装也很简单:下载解压,配置环境变量 CATALINA_HOME ,然后运行 bin/startup.sh 就能看到熟悉的猫脸欢迎页啦 😺。
至于开发环境嘛,推荐组合是 JDK 8+ + Tomcat 9+ + IntelliJ IDEA 。IDEA对Java Web项目的支持非常友好,无论是调试还是部署都丝滑顺畅。当然Eclipse也可以,只是界面略显复古了些 😉
核心引擎:Servlet不只是个接口那么简单
如果说Java Web是一座大厦,那 Servlet 就是它的地基。很多人初学时觉得“不就是个处理请求的类吗”,但实际上它的设计哲学非常精妙。
它到底是啥?
严格来说, Servlet 是一个接口:
public interface Servlet {
void init(ServletConfig config) throws ServletException;
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
void destroy();
}
任何实现这个接口的Java类,都可以被称为Servlet。但现实中我们几乎不会直接实现它,而是继承更高级的抽象类——比如 HttpServlet 。
为什么?因为原始的 service() 方法太“裸”了,你需要自己判断是GET还是POST请求。而 HttpServlet 已经帮你做好了这件事:
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if ("GET".equals(method)) {
doGet(req, resp);
} else if ("POST".equals(method)) {
doPost(req, resp);
}
// 其他方法...
}
所以实际编码中,我们只需要重写 doGet() 或 doPost() 即可,省事多了!
生命周期的秘密
每个Servlet都有自己的“一生”,由Web容器全程监管。这个生命周期可以用三句话概括:
🌱 初始化一次:
init()
🚀 服务无数次:service()
💤 销毁仅一次:destroy()
来段代码感受一下:
public class LifecycleServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("我被初始化了!只会有这一次哦~");
// 比如在这里读取配置文件、建立数据库连接池
}
@Override
public void service(ServletRequest request, ServletResponse response)
throws IOException {
PrintWriter out = response.getWriter();
out.println("<h2>你好呀,我是活生生的Servlet!</h2>");
}
@Override
public void destroy() {
System.out.println("我要退休啦,释放资源...");
// 关闭数据库连接、注销监听器等
}
}
是不是有点像人的生命历程?出生(init)→ 工作(service)→ 老去(destroy)。这种设计让开发者可以在合适的时间点插入自定义逻辑,比如预热缓存、优雅关闭资源。
这里有个重要知识点: init() 和 destroy() 在整个应用生命周期内只会执行一次 ,而 service() 会被并发调用多次。这也意味着你不应该在成员变量里存请求相关的数据,否则会造成线程安全问题⚠️。
用一张图来总结它的状态流转:
graph TD
A[Web容器启动] --> B{是否首次请求?}
B -- 是 --> C[调用 init()]
B -- 否 --> D[直接进入 service()]
C --> D
D --> E[处理请求]
E --> F{容器关闭或卸载Servlet?}
F -- 是 --> G[调用 destroy()]
F -- 否 --> D
看到了吗?只有第一次请求才会触发初始化,之后所有请求都直接进入服务阶段。这也是为什么第一次访问页面总是特别慢的原因之一——它可能正在编译JSP或加载资源。
配置方式的进化史:XML vs 注解
早期Java Web项目必须通过 web.xml 注册Servlet:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.HelloHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
这种方式集中管理,便于审查,但在微服务时代显得冗长繁琐。于是从Servlet 3.0开始,注解登场了!
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().println("Hello World! 欢迎来到Java Web世界");
}
}
一行 @WebServlet 搞定路径映射,清爽多了有木有!而且还能玩更多花样:
@WebServlet(
urlPatterns = {"/api/user", "/user"},
loadOnStartup = 1,
initParams = {
@WebInitParam(name = "admin", value = "zhangsan")
}
)
支持多路径映射、指定启动优先级、设置初始化参数……简直是懒人福音。不过大型企业项目出于统一管控考虑,仍可能保留 web.xml 作为主配置文件。
两种风格各有千秋,就像火锅可以选择清汤也可以选择麻辣一样,关键看你喜欢哪种味道🍲。
数据通道:HTTP协议的那些事儿
既然谈到了请求与响应,那就不得不提它们的“交通工具”——HTTP协议。你每天都在用它,但它到底长什么样呢?
一个典型的HTTP请求报文长这样:
POST /submit HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=李四&age=25
拆开来看就是三大块:
- 请求行 :方法 + URI + 协议版本
- 请求头 :一堆键值对,传递元信息
- 请求体 :POST才有,装具体数据
而在Servlet里,我们可以轻松提取这些内容:
String method = req.getMethod(); // "POST"
String uri = req.getRequestURI(); // "/submit"
String contentType = req.getContentType(); // "application/x-www-form-urlencoded"
String name = req.getParameter("name"); // "李四"
注意啦!这里有两大坑要避开👇
中文乱码怎么办?
尤其是POST请求,如果你没设置编码,中文很可能会变成“????”这种乱码。原因在于: 浏览器用UTF-8发送,但服务器默认用ISO-8859-1解析 。
解决方案很简单,在读取参数前加一句:
req.setCharacterEncoding("UTF-8"); // 必须放在getParameter之前!
但对于GET请求,参数在URL里,这招就不灵了。得去改Tomcat的 server.xml :
<Connector port="8080" URIEncoding="UTF-8" />
统一设成UTF-8,从此告别乱码烦恼 ✅。
GET和POST到底有啥区别?
| 特性 | GET | POST |
|---|---|---|
| 数据位置 | URL参数 | 请求体 |
| 安全性 | 地址栏可见,不安全 | 相对安全 |
| 长度限制 | ~2KB | 几乎无限制 |
| 是否可缓存 | 可以 | 不可 |
| 幂等性 | 是 | 否 |
举个例子:搜索用GET,因为它只是获取数据;登录用POST,因为涉及敏感信息且会产生副作用(创建会话)。
千万记住一句话: GET用于查询,POST用于修改 。滥用GET提交表单不仅危险,还容易被爬虫盯上 😰。
视觉担当:JSP如何变身HTML战士
如果说Servlet是幕后英雄,那 JSP (JavaServer Pages)就是台前明星。它允许我们在HTML中嵌入Java代码,实现动态内容输出。
但你知道吗?JSP本质上并不是什么神秘技术,它最终也会被翻译成Servlet来执行!整个流程如下:
graph TD
A[JSP文件 index.jsp] --> B{容器检查是否已编译}
B -- 否 --> C[翻译为Java类]
C --> D[编译成.class]
D --> E[加载执行]
E --> F[生成HTML返回]
B -- 是 --> E
也就是说, hello.jsp 会被转成类似 _jspService() 的方法,里面全是 out.print() 语句。第一次访问慢是因为要走完整个编译流程,后面就快了。
那么JSP有哪些“武器”可用呢?主要有三种脚本元素:
| 类型 | 语法 | 用途 |
|---|---|---|
| 声明 | <%! %> |
定义类级变量或方法 |
| 表达式 | <%= %> |
输出表达式结果 |
| 脚本片段 | <% %> |
插入任意Java代码块 |
举个计数器的例子:
<%! int count = 0; %>
<% count++; %>
<p>访问次数:<%= count %></p>
每次刷新都会递增,因为 count 是类变量。但请注意⚠️:过度使用scriptlet会导致HTML和Java混杂,后期维护噩梦。现代开发推荐用EL表达式替代。
说到EL(Expression Language),这才是JSP的正确打开方式:
<p>姓名:${param.name}</p>
<p>年龄:${requestScope.age}</p>
<p>用户名:${sessionScope.user.username}</p>
简洁又安全,还能自动处理null值。配合JSTL标签库,基本可以告别Java代码嵌入:
<c:forEach items="${users}" var="user">
<li>${user.name} - ${user.age}</li>
</c:forEach>
干净利落,赏心悦目 ❤️。
架构灵魂:MVC模式让代码更有条理
如果你把所有逻辑都塞进一个Servlet里,很快就会陷入“意大利面条代码”的泥潭 spaghetti code 🍝。怎么办?答案就是引入 MVC设计模式 。
MVC把应用分成三层:
- Model(模型) :管数据和业务规则
- View(视图) :负责展示界面
- Controller(控制器) :协调两者之间的交互
它们的关系就像导演拍电影:
graph TD
A[Client Browser] -->|HTTP Request| B(Controller)
B -->|Call Service| C(Model)
C -->|Return Data| B
B -->|Forward to JSP| D(View)
D -->|Render HTML| A
用户发起请求 → 控制器接收 → 调用模型处理 → 拿到结果转发给视图 → 渲染返回。各司其职,井然有序。
举个实际例子,我们要做个“添加用户”功能:
第一步:定义实体类(Model)
public class Person {
private Integer id;
private String name;
private Integer age;
// getter/setter...
}
这是最基础的数据载体,将来会映射到数据库表。
第二步:编写Service层(业务逻辑)
public class PersonService {
private PersonDAO dao = new PersonDAO();
public boolean savePerson(Person person) {
// 校验逻辑
if (person.getName().length() < 2) {
throw new IllegalArgumentException("姓名太短!");
}
if (person.getAge() < 1 || person.getAge() > 150) {
throw new IllegalArgumentException("年龄不合理!");
}
return dao.insert(person) > 0;
}
}
这里集中处理业务规则,比如字段校验、事务控制等。
第三步:创建Servlet控制器
@WebServlet("/person")
public class PersonServlet extends HttpServlet {
private PersonService service = new PersonService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
Person p = new Person(name, age);
service.savePerson(p);
request.setAttribute("msg", "添加成功!");
request.getRequestDispatcher("/success.jsp").forward(request, response);
} catch (Exception e) {
request.setAttribute("error", e.getMessage());
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
}
控制器只做三件事:拿参数 → 调服务 → 决定跳哪。绝不掺和具体业务细节。
第四步:JSP展示结果(View)
<h3>${msg}</h3>
<ul>
<c:forEach items="${users}" var="u">
<li>${u.name} (${u.age})</li>
</c:forEach>
</ul>
纯粹的数据展示,没有任何Java代码污染。
你看,这样的结构是不是清晰多了?每层职责分明,改UI不影响逻辑,换数据库也不用动前端。这就是所谓“高内聚低耦合”的魅力所在啊!
状态记忆:Session如何记住你是谁
前面说过HTTP是无状态的,那淘宝怎么知道你是登录状态的?这就靠 Session机制 了。
原理其实很简单:服务器为每个用户创建一个独立的存储空间( HttpSession ),并通过Cookie里的 JSESSIONID 来识别身份。
// 获取或创建会话
HttpSession session = request.getSession();
// 存东西
session.setAttribute("user", userObj);
// 取东西
User u = (User) session.getAttribute("user");
这样一来,哪怕你刷新十次页面,只要Cookie还在,服务器就能认出你。
而且Session比Cookie安全得多,因为真实数据存在服务器内存里,客户端只能看到一个ID。不像Cookie那样容易被篡改。
不过要注意几点:
- Session占用服务器内存,不能存太多大对象
- 默认30分钟不活动就会过期
- 可以手动销毁:
session.invalidate()
为了提升安全性,建议开启 HttpOnly 标志,防止XSS攻击窃取Session ID:
cookie.setHttpOnly(true);
cookie.setSecure(true); // HTTPS专用
这样JavaScript就无法通过 document.cookie 读取了,安全性↑↑↑。
永久保存:JDBC连接MySQL实现数据落地
到现在为止,数据还停留在内存里,重启就没了。要想永久保存,必须上数据库!
我们选用经典组合: JDBC + MySQL 。
首先建张表:
CREATE TABLE user_info (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
age INT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
然后写DAO层代码:
public class UserDAO {
private static final String URL = "jdbc:mysql://localhost:3306/userdb";
private static final String USER = "root";
private static final String PASS = "your_password";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public int insert(Person person) {
String sql = "INSERT INTO user_info (name, age) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, person.getName());
ps.setInt(2, person.getAge());
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
}
看到 PreparedStatement 了吗?它是防SQL注入的关键!比起拼接字符串,预编译语句能把参数当作纯数据处理,从根本上杜绝黑客利用 ' OR '1'='1 这种手段入侵。
查询也一样简单:
public List<Person> selectAll() {
List<Person> list = new ArrayList<>();
String sql = "SELECT * FROM user_info ORDER BY create_time DESC";
try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
Person p = new Person();
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
list.add(p);
}
}
return list;
}
有了这套CRUD机制,我们的数据终于可以“永生”了!
终极实战:手把手搭建完整用户系统
好了,理论讲得差不多了,现在让我们动手做一个真正的应用吧!
目标:用户信息管理系统
功能:增删改查 + 分页展示 + 输入校验 + XSS防护
项目结构规划
src/
├── main/java/
│ ├── com/example/web/model ← 实体类
│ ├── com/example/web/service ← 业务逻辑
│ ├── com/example/web/dao ← 数据访问
│ └── com/example/web/servlet ← 控制器
└── webapp/
├── WEB-INF/jsp/ ← JSP页面
└── static/ ← CSS/JS
前端页面美化
不想让界面太丑,加点CSS:
<style>
.form-group { margin: 10px 0; }
input[type=text], input[type=number] {
padding: 5px; border: 1px solid #ccc; border-radius: 4px;
}
button { background: #007BFF; color: white; padding: 10px 20px; border: none; }
</style>
瞬间就有模有样了 👌。
控制器统一调度
@WebServlet("/UserServlet")
public class UserServlet extends HttpServlet {
private UserService userService = new UserService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String action = request.getParameter("action");
switch (action) {
case "add": handleAdd(request, response); break;
case "delete": handleDelete(request, response); break;
default: redirectHome(response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
if ("list".equals(request.getParameter("action"))) {
List<Person> users = userService.getAllUsers();
request.setAttribute("userList", users);
forwardTo("list.jsp", request, response);
}
}
}
一个Servlet搞定所有路由,干净整洁。
安全防护不能少
- 参数校验:长度、格式、范围
- SQL注入:使用PreparedStatement
- XSS攻击:输出时转义特殊字符
- 防重复提交:采用PRG模式(Post-Redirect-Get)
特别是最后一点,记得处理完POST请求后用 sendRedirect 跳转,避免用户刷新导致重复插入数据。
总结与思考:我们学到了什么?
回顾这一路走来,我们从最基本的“Hello World”Servlet出发,逐步构建起了一个完整的Java Web知识体系:
✅ 掌握了Servlet生命周期与请求处理机制
✅ 理解了HTTP协议的工作原理与常见问题
✅ 学会了用JSP+EL+JSTL打造动态视图
✅ 实践了MVC分层架构的设计思想
✅ 实现了Session会话管理与数据库持久化
更重要的是,我们亲手完成了一个具备生产雏形的Web应用。虽然它看起来简单,但其中蕴含的工程思维却是通用的—— 分层解耦、职责分离、安全防护、用户体验优化 。
未来如果你想继续深造,Spring Boot会是你的好朋友。它基于这些底层技术做了更高层次的封装,让你可以用更少的代码做更多的事。但请记住: 懂得底层原理的人,才能真正驾驭高级框架 。
所以,不要停下脚步!继续保持好奇心,去研究Filter过滤器、Listener监听器、国际化支持、文件上传下载……Java Web的世界远比你想象的精彩 ✨。
最后送大家一句话共勉:
“优秀的程序员不是写最多代码的那个,而是能让系统最稳定、最容易维护的那个。”
加油,未来的架构师们!🚀
简介:Java Web是基于Java技术的动态Web应用开发框架,涵盖服务器端编程、数据库交互与前端展示。本课程以“年龄+姓名”为教学示例,系统讲解Servlet、JSP、MVC模式、EL表达式、JSTL标签库等核心技术,帮助学习者掌握用户数据的接收、处理、验证与会话管理,并结合JDBC实现数据持久化。通过该实例,学生可深入理解Java Web的开发流程与设计模式,为后续使用Spring、Hibernate等主流框架打下坚实基础。
更多推荐

所有评论(0)