JSP服务器端脚本语言基础入门详解从HelloWorld到数据库连接常见坑点避坑与实战项目构建指南
你好呀!今天咱们来聊聊JSP这个有点年头、但至今仍在很多企业级项目中默默发光的技术。我知道你一听到”服务器端脚本语言”可能就觉得头大,别慌,我会用最接地气的方式带你入门,从最基础的HelloWorld一直讲到数据库连接,中间还会把那些容易踩的坑一个个指出来,最后咱们一起搭个实战小项目。准备好了吗?
先搞明白:JSP到底是个啥玩意儿
JSP全称JavaServer Pages,说白了就是在HTML里嵌Java代码的一种技术。你想啊,以前写静态网页,内容写死了,用户看到的是千篇一律的页面。JSP出现后,页面可以根据不同的请求、不同的用户、不同的数据库数据动态生成内容。
打个比方,你开了一家奶茶店。静态网页就像那种提前印好菜单的传单, everyone 看到的都一样。而JSP呢,就像你站在柜台后头,顾客说”我要一杯珍珠奶茶,少糖”,你当场给他现做——内容是根据顾客的需求动态生成的。
技术上来说,JSP文件(.jsp扩展名)会被Web容器(比如Tomcat)编译成Java Servlet,然后执行。所以你不需要额外安装什么JSP编译器,只要有一个支持Java的Web服务器就行。
环境准备:把舞台搭好
在写代码之前,你得先搞定运行环境。我用的是最常见的方案:
- JDK(Java Development Kit)1.8或以上版本
- Tomcat 9.x 或更高版本(Apache Tomcat是现在最主流的JSP容器)
- 一个IDE,推荐 IntelliJ IDEA(社区版就够用)或者 Eclipse
安装JDK后,打开命令行输入:
java -version
看到类似这样的输出就说明装好了:
java version "1.8.0_361"
Java(TM) SE Runtime Environment (build 1.8.0_361-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)
Tomcat的话,去官网下载Binary Distribution里的Core压缩包,解压就行。启动方式也很简单:
# Windows
catalina.bat run
# Linux/Mac
./catalina.sh run
启动成功后,浏览器访问 http://localhost:8080,看到那个默认的Tomcat页面就算成功啦。
避坑提醒:很多人装了JDK之后直接就去下载Tomcat,结果启动时报错说找不到JAVA_HOME。解决办法是在系统环境变量里加一个
JAVA_HOME,值指向你的JDK安装目录,比如C:\Program Files\Java\jdk1.8.0_361。
第一个程序:HelloWorld
好,环境搞定了,咱们来写第一个JSP程序。在你的Tomcat安装目录里找到webapps文件夹,在里面新建一个文件夹,比如叫myfirstjsp,然后在里面创建一个hello.jsp文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>我的第一个JSP</title>
</head>
<body>
<h1>Hello, World! 欢迎来到JSP的世界</h1>
<p>当前时间是:<%= new java.util.Date() %></p>
</body>
</html>
把这个文件放到webapps/myfirstjsp/目录下,启动Tomcat,浏览器访问:
http://localhost:8080/myfirstjsp/hello.jsp
是不是看到了”Hello, World! 欢迎来到JSP的世界”和当前时间?这就对了!
让我来给你拆解一下这几行代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>这是一行页指令(page directive),告诉容器这个页面是UTF-8编码的HTML,用的是Java语言。省略它也能跑,但中文可能会乱码,所以别偷懒。<%= new java.util.Date() %>这是表达式(expression),等号后面的Java代码会被直接输出到页面。注意这里不要加分号,容器会自动帮你处理。
JSP的三大脚本元素
JSP里能嵌Java代码,主要靠三个玩意儿:脚本片段、表达式、声明。
1. 脚本片段(Scriptlet)
用<% %>包裹,里面可以写任意合法的Java代码:
<%
String username = "小明";
int age = 20;
out.println("<p>姓名:" + username + ",年龄:" + age + "</p>");
%>
这里有个细节要注意:脚本片段里的变量是局部变量,出了这个<% %>块就不认识了。而且同一个JSP页面里可以写多个<% %>块,它们共享作用域。
2. 表达式(Expression)
用<%= %>包裹,用于直接输出一个值:
<p>计算结果是:<%= 3 + 5 %></p>
<p>用户名是:<%= username %></p>
表达式里不能加分号,这是新手最容易犯的错误之一。加了分号,编译会报错,Tomcat会给你返回一个500错误页面。
3. 声明(Declaration)
用<%! %>包裹,用于声明类级别的变量或方法:
<%!
private int counter = 0;
public String getGreeting(String name) {
return "你好," + name + "!";
}
%>
声明的东西是全局的,整个JSP类里都能用。但这里有个大坑——多线程问题。我后面会详细讲。
JSP的内置对象
JSP给你准备好了9个内置对象,不用new就能直接用。这几个是你接下来要最常打交道的:
| 对象 | 类型 | 用途 |
|---|---|---|
request |
HttpServletRequest | 获取请求信息(参数、头、Cookie等) |
response |
HttpServletResponse | 发送响应(重定向、写Cookie等) |
out |
JspWriter | 向页面输出内容 |
session |
HttpSession | 管理用户会话 |
application |
ServletContext | 应用级别的数据共享 |
config |
ServletConfig | 获取Servlet配置信息 |
pageContext |
PageContext | 页面上下文,可以获取其他8个对象 |
page |
Object | 指向当前JSP对应的Servlet实例(很少用) |
exception |
Throwable | 异常对象,只在错误页面可用 |
来,咱们写个例子看看它们怎么用的:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>内置对象示例</title>
</head>
<body>
<h2>请求信息</h2>
<p>请求方法:<%= request.getMethod() %></p>
<p>请求URL:<%= request.getRequestURL() %></p>
<p>客户端IP:<%= request.getRemoteAddr() %></p>
<h2>Session信息</h2>
<p>Session ID:<%= session.getId() %></p>
<p>创建时间:<%= new java.util.Date(session.getCreationTime()) %></p>
<h2>应用信息</h2>
<p>Servlet路径:<%= application.getServletContextName() %></p>
</body>
</html>
指令和动作标签
除了脚本元素,JSP还有指令(directive)和动作(action)两种标签。
三大指令
page指令:配置页面属性
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" errorPage="error.jsp" %>include指令:静态包含其他文件
<%@ include file="header.jsp" %>注意,这是静态包含,在编译时就合并了。如果你想运行时动态包含,用
<jsp:include>动作。taglib指令:引入标签库
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>这个等咱们讲到JSTL再说。
常用动作标签
<!-- 动态包含 -->
<jsp:include page="footer.jsp" />
<!-- 转发请求 -->
<jsp:forward page="nextPage.jsp" />
<!-- 设置Bean属性 -->
<jsp:useBean id="user" class="com.example.User" scope="page" />
<jsp:setProperty name="user" property="name" value="小明" />
<jsp:getProperty name="user" property="name" />
JSP与JavaBean的协作
JavaBean就是一个符合规范的Java类:私有属性、公有getter/setter、无参构造方法。JSP用<jsp:useBean>来创建和操作Bean,让页面和业务逻辑分离。
先写一个简单的User Bean:
package com.example;
public class User {
private String name;
private String email;
private int age;
public User() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
然后JSP页面这样用:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<jsp:useBean id="user" class="com.example.User" scope="session" />
<jsp:setProperty name="user" property="*" />
<html>
<head><title>用户信息</title></head>
<body>
<h2>填写用户信息</h2>
<form method="post">
姓名:<input type="text" name="name"><br>
邮箱:<input type="text" name="email"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<h2>当前用户信息</h2>
<p>姓名:<jsp:getProperty name="user" property="name" /></p>
<p>邮箱:<jsp:getProperty name="user" property="email" /></p>
<p>年龄:<jsp:getProperty name="user" property="age" /></p>
</body>
</html>
这里有个小技巧:<jsp:setProperty property="*">会自动把请求参数按名字匹配到Bean的属性上,省得你一个个去request.getParameter()了。
JSTL:告别脚本的终点站
说实话,早期的JSP页面经常到处是<% %>脚本,代码可读性差得要命,调试也头疼。后来大家引入了JSTL(JSP Standard Tag Library),用标签代替脚本,页面变得干净多了。
要使用JSTL,先得在WEB-INF/lib目录里放两个jar包:jstl.jar和standard.jar。如果用Maven的话,在pom.xml里加:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
JSTL分几个库,最常用的是Core库:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
条件判断
<c:if test="${user.age >= 18}">
<p>您是成年人,可以访问此页面。</p>
</c:if>
<c:choose>
<c:when test="${user.age < 18}">
<p>您是未成年人。</p>
</c:when>
<c:when test="${user.age < 60}">
<p>您是成年人。</p>
</c:when>
<c:otherwise>
<p>您是老年人。</p>
</c:otherwise>
</c:choose>
循环遍历
<c:forEach var="item" items="${userList}">
<li>${item.name} - ${item.email}</li>
</c:forEach>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>
格式化输出
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<fmt:formatDate value="${date}" pattern="yyyy年MM月dd日" />
<fmt:formatNumber value="${price}" pattern="#,##0.00" />
用JSTL之后,你的JSP页面几乎不会有<% %>脚本了,这算是JSP开发的最佳实践之一。
EL表达式:简洁的数据访问
EL(Expression Language)是JSP 2.0引入的,用${}语法来访问数据,比<%= %>简洁多了:
<!-- 访问Bean属性 -->
${user.name}
<!-- 访问请求参数 -->
${param.username}
<!-- 访问会话属性 -->
${sessionScope.user.name}
<!-- 访问应用属性 -->
${applicationScope.configName}
<!-- 访问集合 -->
${userList[0].name}
${userMap['key']}
<!-- 算术运算 -->
${10 + 5}
${price * quantity}
<!-- 逻辑运算 -->
${user.age >= 18 and user.vip eq true}
EL还有个好处:不会抛空指针异常。${user.name}如果user是null,它输出空字符串而不是报错,这在JSP页面里非常友好。
数据库连接:JSP实战的核心
好,现在来到最实用也最容易出问题的部分——数据库连接。
JDBC基础
JSP连接数据库用JDBC,标准流程就四步:加载驱动 → 获取连接 → 执行SQL → 关闭资源。
先写一个数据库工具类:
package com.example.db;
import java.sql.*;
public class DBUtil {
private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
private static final String USER = "root";
private static final String PASSWORD = "123456";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException("数据库驱动加载失败", e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
然后在JSP里用:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.sql.*" %>
<%@ page import="com.example.db.DBUtil" %>
<%
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
stmt = conn.prepareStatement("SELECT id, name, email FROM users WHERE age > ?");
stmt.setInt(1, 18);
rs = stmt.executeQuery();
while (rs.next()) {
out.println("<p>" + rs.getInt("id") + " - " + rs.getString("name") + " - " + rs.getString("email") + "</p>");
}
} catch (SQLException e) {
out.println("<p style='color:red;'>数据库查询失败:" + e.getMessage() + "</p>");
} finally {
DBUtil.close(rs, stmt, conn);
}
%>
使用连接池(生产环境必须)
上面的代码在开发环境跑跑没问题,但真正上线的话,每次请求都新建连接太消耗资源了。得用连接池。
如果用Tomcat,可以在context.xml里配置:
<Context>
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
maxTotal="30"
maxIdle="10"
maxWaitMillis="10000"
username="root"
password="123456"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC" />
</Context>
然后JSP里这样获取连接:
<%@ page import="javax.sql.DataSource" %>
<%@ page import="javax.naming.Context" %>
<%@ page import="javax.naming.InitialContext" %>
<%
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
Connection conn = ds.getConnection();
// ... 使用连接
conn.close(); // 注意:这里close()不是真正关闭,而是归还到连接池
%>
常见坑点与避坑指南
这部分我可是压箱底的干货,都是踩过的坑总结出来的。
坑一:中文乱码
这是新手遇到的第一大坑。解决方案是层层把关:
- JSP文件本身保存为UTF-8编码
- page指令里指定
charset=UTF-8 - 表单提交时用
method="post",并在JSP开头加:<% request.setCharacterEncoding("UTF-8"); %> - 数据库URL里加
characterEncoding=UTF-8 - MySQL数据库和表也设成UTF-8
坑二:JSP脚本里的全局变量线程安全问题
前面讲到<%! %>声明的变量是类级别的,多个请求会共享。如果修改这些变量,就会出现并发问题:
<%!
private int count = 0;
%>
<%
count++;
out.println("当前访问次数:" + count);
%>
两个用户同时访问,count的值会混乱。解决办法:不要在JSP里用脚本声明共享变量,需要共享数据用application作用域,或者用Session。
坑三:忘记关闭数据库资源
// 错误写法
Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// 然后页面就结束了,连接和结果集都没关
// 正确写法:用try-finally确保关闭
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM users");
// ... 处理结果
} finally {
DBUtil.close(rs, stmt, conn);
}
坑四:SQL注入
用字符串拼接SQL是万恶之源:
// 危险写法
String username = request.getParameter("username");
String sql = "SELECT * FROM users WHERE name = '" + username + "'";
如果用户输入' OR '1'='1,整个查询就变了。永远用PreparedStatement:
// 安全写法
String username = request.getParameter("username");
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
坑五:直接在JSP里写业务逻辑
JSP应该只负责展示,业务逻辑交给Servlet或Service层。如果你发现JSP里代码越来越长,到处都是<% %>,就该考虑重构了。
坑六:Session超时后仍认为用户已登录
// 错误写法
<%
String user = (String) session.getAttribute("username");
if (user == null) {
response.sendRedirect("login.jsp");
}
%>
这样写,如果Session超时了,每次请求都会重定向,体验很差。正确做法是在过滤器里统一处理,或者在前端配合做判断。
实战项目:用户管理系统
光说不练假把式,咱们搭一个完整的小项目——用户管理系统,涵盖前面讲的大部分知识点。
项目结构
usermanager/
├── WEB-INF/
│ ├── web.xml
│ └── lib/
│ └── jstl.jar
├── css/
│ └── style.css
├── pages/
│ ├── login.jsp
│ ├── register.jsp
│ ├── dashboard.jsp
│ ├── users.jsp
│ └── error.jsp
├── servlets/
│ ├── LoginServlet.java
│ ├── RegisterServlet.java
│ └── UserServlet.java
├── models/
│ └── User.java
├── dao/
│ ├── DBUtil.java
│ └── UserDao.java
└── index.jsp
数据库表
CREATE DATABASE IF NOT EXISTS usermanager DEFAULT CHARSET utf8mb4;
USE usermanager;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
用户模型
package com.example.model;
import java.util.Date;
public class User {
private int id;
private String username;
private String password;
private String email;
private Date createTime;
// getters and setters...
@Override
public String toString() {
return "User{id=" + id + ", username='" + username + "', email='" + email + "'}";
}
}
DAO层
package com.example.dao;
import com.example.model.User;
import java.sql.*;
import java.util.*;
public class UserDao {
public User findByUsername(String username) throws SQLException {
String sql = "SELECT * FROM users WHERE username = ?";
Connection conn = DBUtil.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
User user = null;
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("email"));
user.setCreateTime(rs.getTimestamp("create_time"));
}
DBUtil.close(rs, stmt, conn);
return user;
}
public List<User> findAll() throws SQLException {
String sql = "SELECT id, username, email, create_time FROM users ORDER BY create_time DESC";
Connection conn = DBUtil.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
List<User> users = new ArrayList<>();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
user.setCreateTime(rs.getTimestamp("create_time"));
users.add(user);
}
DBUtil.close(rs, stmt, conn);
return users;
}
public boolean register(User user) throws SQLException {
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
Connection conn = DBUtil.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getUsername());
stmt.setString(2, user.getPassword());
stmt.setString(3, user.getEmail());
int rows = stmt.executeUpdate();
DBUtil.close(null, stmt, conn);
return rows > 0;
}
}
LoginServlet
package com.example.servlet;
import com.example.dao.UserDao;
import com.example.model.User;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/servlet/login")
public class LoginServlet extends HttpServlet {
private UserDao userDao = new UserDao();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
try {
User user = userDao.findByUsername(username);
if (user != null && password.equals(user.getPassword())) {
HttpSession session = req.getSession();
session.setAttribute("user", user);
session.setAttribute("username", username);
resp.sendRedirect("pages/dashboard.jsp");
} else {
req.setAttribute("error", "用户名或密码错误");
req.getRequestDispatcher("pages/login.jsp").forward(req, resp);
}
} catch (Exception e) {
req.setAttribute("error", "登录失败:" + e.getMessage());
req.getRequestDispatcher("pages/login.jsp").forward(req, resp);
}
}
}
登录页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="container">
<h2>用户登录</h2>
<c:if test="${not empty error}">
<div class="error">${error}</div>
</c:if>
<form method="post" action="../servlet/login">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="password" required>
</div>
<button type="submit">登录</button>
</form>
<p>还没有账号?<a href="register.jsp">去注册</a></p>
</div>
</body>
</html>
用户列表页面 users.jsp(展示JSP和EL的用法)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ page import="com.example.dao.UserDao" %>
<%@ page import="java.util.List" %>
<%@ page import="com.example.model.User" %>
<%
// 检查登录状态
String username = (String) session.getAttribute("username");
if (username == null) {
response.sendRedirect("login.jsp");
return;
}
// 获取用户列表
UserDao userDao = new UserDao();
List<User> userList = userDao.findAll();
request.setAttribute("userList", userList);
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户管理</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="container">
<h2>用户列表</h2>
<p>欢迎,<strong>${username}</strong>!<a href="../servlet/logout">退出登录</a></p>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>注册时间</th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${userList}">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.email}</td>
<td>${user.createTime}</td>
</tr>
</c:forEach>
</tbody>
</table>
<a href="dashboard.jsp">返回首页</a>
</div>
</body>
</html>
错误处理页面 error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head>
<title>出错了</title>
</head>
<body>
<h2>哎呀,出了点问题</h2>
<p>错误信息:<%= exception.getMessage() %></p>
<a href="index.jsp">返回首页</a>
</body>
</html>
在web.xml里配置全局错误页:
<error-page>
<error-code>500</error-code>
<location>/pages/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/pages/error.jsp</location>
</error-page>
从JSP到现代技术的过渡
说句心里话,JSP这套东西现在确实有点老了。现代Java Web开发早就转向了Spring MVC + Thymeleaf、Spring Boot + 前后端分离这些方案。JSP的缺点很明显:业务逻辑和展示层混在一起、调试困难、性能不如模板引擎。
但了解JSP还是非常有价值的:
- 很多老项目还在用JSP,维护它们你得懂
- JSP的原理(Servlet、Filter、Listener)是现代框架的基础
- 理解JSP能帮你更好地理解Spring MVC的请求处理流程
- 面试时JSP依然是常考内容
如果你已经掌握了JSP,下一步可以看看:
- Servlet深入:过滤器、监听器、会话管理
- Spring MVC:现代MVC框架,比JSP + Servlet优雅得多
- Spring Boot:一键搞定环境配置,专注业务开发
- 前后端分离:前端用Vue/React,后端只做API
最后的小建议
学JSP的时候,别急着看大项目,先从能跑起来的HelloWorld开始,一步一步加功能。每学会一个概念,就写个小例子验证一下。遇到问题先自己看报错信息,Tomcat的日志在logs/目录下,很多时候错误原因写得清清楚楚。
还有,别怕看源码。Tomcat是怎么把JSP编译成Servlet的?去work目录下看看生成的.java和.class文件,你会对JSP的运行机制有质的理解。
希望这篇文章能帮你推开JSP的大门。技术这东西,上手了就好,慢慢来,不着急。有啥问题随时来问,咱们一起探讨!
