JSP入门实战:从Servlet到动态网页开发,掌握JavaWeb核心技术
1. 从Servlet到JSP为什么我们需要一个“能写HTML的Java文件”如果你刚开始接触JavaWeb开发大概率是从Servlet入门的。Servlet确实强大它给了我们一个处理HTTP请求、生成动态响应的标准接口。但不知道你有没有过这样的经历在doGet或doPost方法里为了输出一个带表格的简单页面你不得不写下一连串令人头皮发麻的out.println(“trtd” data “/td/tr”)。代码里HTML标签和Java字符串拼接纠缠在一起不仅难以阅读后期想调整一个按钮的样式都像在玩“大家来找茬”。更别提那些复杂的页面布局和交互逻辑了用纯Servlet来生成简直就是一场灾难。这种开发模式我们戏称为“在Java里写作文”效率低下且极易出错。JSP的出现就是为了解决这个核心痛点。它的设计思想非常直观既然在Java里写HTML这么痛苦那为什么不反过来在HTML里写Java呢JSPJavaServer Pages本质上就是一个“披着HTML外衣的Servlet”。它允许你将Java代码片段称为Scriptlet、表达式和标签直接嵌入到标准的HTML页面中。当用户请求一个.jsp文件时Web容器如Tomcat会首先将其“翻译”成一个Servlet的Java源文件然后再编译成.class文件并执行。所以你最终在服务器上运行的依然是一个Servlet只不过这个Servlet是由容器自动为你生成的省去了你手动拼接HTML字符串的繁琐工作。对于初学者尤其是从Educoder/头歌这类实践平台开始学习的朋友理解JSP的这个定位至关重要。它不是一个全新的、独立的技术而是Servlet技术的补充和扩展旨在简化动态网页的表示层View开发。当你看到一个.jsp文件时你应该立刻明白这是一个主要用于展示数据的模板其中的Java代码是为了动态地填充这个模板。这种分离尽管在JSP早期版本中分离得并不彻底让前端页面的开发和后端逻辑的处理在物理文件上有了更清晰的界限是迈向更规范架构如MVC的第一步。2. 搭建你的第一个JSP战场环境、工具与“Hello World”理论说再多不如亲手跑通一个例子来得实在。这里我不会只给你一个孤零零的hello.jsp文件而是带你完整走一遍从零开始在IDEA中创建一个可运行的JavaWeb项目并集成JSP的全过程。这个过程里藏着很多新手容易踩坑的细节。2.1 核心环境准备JDK、Tomcat与IDEA首先确保你的“军火库”已经就位JDK这是Java开发的基石。我强烈建议使用JDK 8或JDK 11这两个长期支持LTS版本。对于JSP学习而言它们足够稳定且兼容性最好。避免使用过新如JDK 17或过旧的版本以减少不必要的环境配置问题。Apache Tomcat这是运行JSP/Servlet的Web容器。去官网下载一个Tomcat 9.x版本与Java EE 8规范对应兼容性广。下载ZIP包解压即可记住它的解压路径比如D:\apache-tomcat-9.0.xx。不需要运行startup.batIDEA会集成它。IntelliJ IDEA使用社区版Community就完全足够。确保你安装时勾选了必要的插件比如“Java EE”相关支持。2.2 在IDEA中创建并配置Web项目这是关键一步很多“项目跑不起来”的问题都源于此。新建项目打开IDEA选择“New Project”。左侧选择“Java Enterprise”确保“Project SDK”是你安装的JDK如1.8。在“Additional Libraries and Frameworks”中务必勾选“Web Application”。你会看到它自动勾选了“Java EE 8”和“Create web.xml”。这个web.xml是JavaWeb项目的部署描述符虽然现在很多配置可以用注解但初学阶段保留它有助于理解标准结构。点击“Next”命名你的项目例如JSP-Demo然后Finish。项目结构解析创建完成后关注左侧的Project视图。你会看到一个标准的目录结构src存放你的Java源代码Servlet、JavaBean等。web或webapp这是Web应用的根目录。WEB-INF这个目录很特殊里面的内容客户端浏览器无法直接访问。它通常存放web.xml、lib第三方JAR包和classes编译后的Java类。除了WEB-INF其他位置如根目录下就是存放可以被公开访问的资源的地方比如.html、.jsp、图片、CSS/JS文件。你的JSP文件通常就放在这里或者在这里面的子目录下。配置Tomcat点击IDEA右上角“Add Configuration…”点击“”号选择“Tomcat Server” - “Local”。在“Application server”右边点击“Configure…”找到你之前解压的Tomcat目录。配置好后在“Deployment”标签页点击“”号选择“Artifact”会把你当前的项目war包添加进去。关键点留意“Application context”一项它决定了你访问应用的根路径。默认可能是/JSP-Demo_war_exploded你可以把它简化为/这样访问地址就是http://localhost:8080/。2.3 编写并运行第一个JSP页面现在在webapp目录上右键New-JSP/JSPX创建一个名为hello.jsp的文件。IDEA可能会提示你“为JSP文件添加支持”确认即可。这解决了热词中“在idea如何支持jsp文件的高亮”的问题。在hello.jsp中输入以下代码% page contentTypetext/html;charsetUTF-8 languagejava % html head title我的第一个JSP/title /head body h1Hello JSP World!/h1 p现在时间是% new java.util.Date() %/p % String name Educoder学员; for(int i0; i3; i){ % p欢迎你% name %这是第% i1 %次问候。/p % } % /body /html点击绿色的运行按钮或Debug按钮IDEA会自动启动Tomcat并打开浏览器。如果一切顺利你会看到页面显示标题、当前时间以及三行欢迎语。这里发生了什么% page ... %这是JSP的page指令用于设置页面的全局属性比如内容类型、编码和使用的语言。% ... %这是JSP的表达式Expression用于输出一个Java表达式的结果到页面上。% new java.util.Date() %会输出当前日期时间。% ... %这是JSP的脚本片段Scriptlet里面可以写任何有效的Java代码。我们在这里声明变量、使用循环。注意脚本片段和HTML标签的混合使用% for(...){ %和% } %包裹着HTML段落p.../p这实现了循环生成HTML内容的效果。这个简单的例子展示了JSP的核心能力在HTML骨架中动态地插入由Java逻辑生成的内容。你不再需要手动拼接字符串视觉上清晰多了。3. 深入JSP语法核心指令、脚本元素与内置对象掌握了“Hello World”之后我们需要系统性地认识一下JSP的语法工具箱。它主要由指令Directives、脚本元素Scripting Elements和内置对象Implicit Objects构成。3.1 JSP指令给“翻译官”的说明书指令的作用是告知Web容器如何翻译和处理当前JSP页面。它以%开始以%结束。除了刚才用到的page指令还有两个非常重要的page指令最重要的指令属性繁多。contentType和pageEncoding解决中文乱码问题的关键。通常设置为contentType”text/html;charsetUTF-8″并确保你的JSP文件本身也用UTF-8编码保存在IDEA中检查文件右下角编码。import导入Java包就像在普通Java类里一样。% page import”java.util.List, com.example.User” %errorPage和isErrorPage用于错误处理。指定当前页面出错时跳转到的错误页面以及声明一个页面为错误页面可访问exception内置对象。include指令静态包含。在JSP页面被翻译成Servlet时就将指定文件的内容合并进来。语法% include file”header.jsp” %。它适合包含那些不经常变化的、公共的页面片段如页头、页脚、导航栏。因为是在编译期包含所以包含文件中不能有和主页面同名的变量否则会报错。taglib指令用于引入标签库如JSTL这是后话但它是进化到更优雅开发方式的关键。3.2 JSP脚本元素在页面中嵌入Java逻辑这是JSP早期最常用的部分但也因其容易导致“意大利面条式代码”HTML、CSS、JS、Java混作一团而备受诟病。我们了解它是为了更好地理解为什么需要替代方案。声明Declaration%! ... %。用于声明页面的成员变量或方法。这些变量和方法会成为翻译后Servlet类的成员是实例变量非线程安全需要谨慎使用。%! private int visitCount 0; // 实例变量所有用户共享不是每个Servlet实例 public String greet(String name) { return Hello, name; } %脚本片段Scriptlet% ... %。就是我们之前用的里面可以写任何Java代码。这些代码在翻译后会原封不动地放入Servlet的_jspService方法中。这是造成代码混乱的“元凶”。表达式Expression% ... %。用于输出相当于out.print(...)。注意表达式末尾没有分号。3.3 JSP内置对象开箱即用的强大工具这是JSP的一大便利。在脚本片段和表达式中你可以直接使用9个内置对象无需声明和初始化。它们对应着Servlet API中的关键对象request(HttpServletRequest)代表客户端的请求包含参数、头信息等。response(HttpServletResponse)代表对客户端的响应。out(JspWriter)用于向客户端输出内容的字符流类似于Servlet中的PrintWriter但略有不同。session(HttpSession)代表一次用户会话。用于跟踪用户状态。application(ServletContext)代表整个Web应用。用于存储全局共享数据。pageContext(PageContext)这是最重要的对象之一。它提供了访问其他所有内置对象的方法并且可以管理不同作用域page, request, session, application的属性。config(ServletConfig)对应此JSP页面的ServletConfig。page(Object)代表当前JSP页面实例本身相当于this。exception(Throwable)只有在isErrorPage”true”的页面中才可用代表抛出的异常。一个经典用例处理表单提交创建一个form.jsp和一个handle.jsp。form.jsp:form actionhandle.jsp methodpost 用户名input typetext nameusernamebr 密码input typepassword namepasswordbr input typesubmit value登录 /formhandle.jsp:% // 使用内置对象request获取表单参数 String user request.getParameter(username); String pwd request.getParameter(password); // 模拟一个简单的验证 if(admin.equals(user) 123456.equals(pwd)){ // 使用内置对象session记录登录状态 session.setAttribute(loginUser, user); // 使用内置对象response进行重定向 response.sendRedirect(welcome.jsp); } else { // 使用内置对象out输出信息 out.println(h3登录失败/h3); } %这个例子串联了request,session,response,out等多个内置对象展示了JSP如何处理一次完整的交互。4. 超越ScriptletJSP动作、EL表达式与JSTL的进化之路如果你写的JSP页面里充满了%和%并且夹杂着大量的Java业务逻辑和数据库操作代码那么你的项目正走在一条难以维护的“老路”上。这种模式将表示层View和业务逻辑层、数据访问层严重耦合违反了MVC的设计原则。现代JavaWeb开发中Scriptlet的使用被极力避免。那么我们用什么来替代它呢答案是JSP动作标签、EL表达式和JSTL标签库。4.1 JSP动作标签标准化的页面控制动作标签以jsp:为前缀在请求处理时动态执行。它们比Scriptlet更简洁、更面向标签。jsp:include动态包含。与% include%指令的静态包含不同它是在请求时才包含目标页面的输出结果。这意味着被包含的页面是一个独立的执行单元更适合包含动态内容。!-- 包含一个可能根据时间显示不同内容的页面 -- jsp:include page”dynamic_part.jsp”/jsp:forward请求转发。将当前请求转发给另一个资源JSP或Servlet处理浏览器地址栏不变。jsp:forward page”/anotherPage.jsp”/jsp:useBeanjsp:setPropertyjsp:getProperty用于操作JavaBean。这是早期MVC模式中JSP作为视图层与模型JavaBean交互的标准方式。虽然现在更常用EL和JSTL但了解其思想仍有必要。jsp:useBean id”user” class”com.example.User” scope”request”/ jsp:setProperty name”user” property”name” value”${param.username}”/ 用户名jsp:getProperty name”user” property”name”/4.2 EL表达式简化数据访问的语法糖ELExpression Language是JSP 2.0引入的革命性特性。它的目标是取代那些用于输出和简单逻辑的Scriptlet和表达式% %让JSP页面变得更干净。基本语法${expression}访问作用域属性这是EL最常用的功能。它自动在pageScope、requestScope、sessionScope、applicationScope四个作用域中查找属性。Scriptlet方式% request.getAttribute(“userName”) %EL方式${userName}或${requestScope.userName}更明确访问JavaBean属性EL支持使用点号.操作符访问对象的属性实际上是调用其getter方法。假设request中有一个User类型的属性user它有getName()方法。EL${user.name}自动调用user.getName()访问集合和数组使用方括号[]可以访问Map、List、数组。${userList[0].name}访问List第一个元素的name属性${userMap[‘admin’].email}访问Map中key为’admin’的元素的email属性执行简单运算和逻辑判断EL支持算术、关系、逻辑运算符以及empty操作符判断是否为null或空字符串/集合。${count 1}${age 18}${empty cartItems}判断购物车是否为空EL表达式极大地净化了JSP页面使得前端开发者即使不懂Java也能看懂页面中的数据展示逻辑。4.3 JSTL标签库彻底告别Scriptlet的终极武器JSTLJSP Standard Tag Library提供了一套标准化的、功能丰富的标签库用于处理常见的逻辑如循环、条件判断、格式化、数据库操作已废弃等。配合EL表达式几乎可以完全消除Scriptlet。引入JSTL在使用前需要在页面顶部通过taglib指令引入。% taglib prefix”c” uri”http://java.sun.com/jsp/jstl/core” % % taglib prefix”fmt” uri”http://java.sun.com/jsp/jstl/fmt” %核心标签库c:xxx条件判断c:if test”${user.role ‘admin’}” p欢迎管理员/p /c:if c:choose c:when test”${score 90}”优秀/c:when c:when test”${score 60}”及格/c:when c:otherwise不及格/c:otherwise /c:choose循环遍历c:forEach var”item” items”${productList}” varStatus”status” tr td${status.index 1}/td td${item.name}/td td${item.price}/td /tr /c:forEachURL管理c:url用于重写URL处理会话IDc:param用于添加参数。a href”c:url value’/productDetail’ c:param name’id’ value’${product.id}’/ /c:url”查看详情/a格式化标签库fmt:xxx用于格式化日期、数字、货币等。fmt:formatDate value”${order.createTime}” pattern”yyyy-MM-dd HH:mm:ss”/ fmt:formatNumber value”${product.price}” type”currency”/一个完整的现代JSP页面示例% page contentType”text/html;charsetUTF-8” language”java” % % taglib prefix”c” uri”http://java.sun.com/jsp/jstl/core” % html headtitle用户列表/title/head body h2用户列表/h2 c:if test”${empty userList}” p暂无用户数据。/p /c:if c:if test”${not empty userList}” table border”1” trth序号/thth姓名/thth邮箱/thth操作/th/tr c:forEach var”user” items”${userList}” varStatus”vs” tr td${vs.count}/td td${user.name}/td td${user.email}/td td a href”c:url value’/editUser’ c:param name’id’ value’${user.id}’/ /c:url”编辑/a /td /tr /c:forEach /table /c:if /body /html这个页面里你看不到任何% %逻辑清晰结构干净这才是现代JSP应该有的样子。它只负责展示由ServletController设置好的userList数据实现了良好的关注点分离。5. 实战避坑与性能优化从能跑到跑得好掌握了核心语法和现代写法并不意味着就能高枕无忧。在实际开发和部署中你会遇到各种各样的问题。下面我结合热词和常见坑点分享一些实战经验。5.1 中文乱码问题贯穿始终的“幽灵”乱码是JavaWeb开发中最常见的问题之一其根源在于编解码不一致。JSP生命周期中涉及多次编码转换任何一环出错都会导致乱码。JSP文件自身编码确保你的.jsp文件以UTF-8格式保存。在IDEA中检查文件右下角如果不是UTF-8点击并选择“Convert to UTF-8”。页面声明的编码在JSP页面顶部使用page指令明确指定。% page contentType”text/html;charsetUTF-8” pageEncoding”UTF-8”%contentType告诉浏览器用什么编码解析pageEncoding告诉JSP引擎用什么编码翻译本文件。请求参数乱码GET/POSTPOST请求在获取参数前设置请求体的编码。通常在一个过滤器中统一处理或在每个Servlet/JSP的顶部写% request.setCharacterEncoding(“UTF-8”); %GET请求参数在URL中Tomcat 8.5及以上版本默认使用UTF-8解码但旧版本可能需要修改Tomcat的server.xml中Connector的URIEncoding”UTF-8″属性。数据库连接编码确保数据库、表、连接字符串都使用UTF-8。5.2 常见错误与排查failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING这个错误在前端浏览器控制台出现表面是网络资源加载不完整。后端可能的原因包括JSP页面输出流被异常关闭或干扰比如在JSP中同时使用了response.getWriter()和JSP内置对象out导致流冲突。记住在JSP中尽量只使用内置对象out进行输出。服务器端处理异常JSP或Servlet在处理过程中抛出了未捕获的异常导致响应被截断。查看Tomcat的catalina.out或日志文件找到具体的异常堆栈信息。网络或代理问题偶尔也可能是网络不稳定导致。The superclass “javax.servlet.http.HttpServlet” was not found项目缺少Servlet API的JAR包。在传统的Web项目中你需要将Servlet API通常来自Tomcat的lib目录下的servlet-api.jar添加到项目的WEB-INF/lib下或者将其配置为“Provided”在Maven/Gradle项目中。在IDEA的普通JavaWeb项目中确保你创建项目时选择了正确的Java EE版本IDEA会自动关联库。JSP页面无法编译提示Java语法错误检查你的Scriptlet中的Java代码。常见的错误包括在% %表达式后加了分号在%! %声明中使用了不支持的类型或者引入了未定义的变量。JSP最终会被编译成Java类所以它必须遵守Java语法。5.3 性能考量与最佳实践避免在JSP中编写业务逻辑这是最重要的原则。JSP应该只负责展示。所有与数据库交互、复杂计算、业务规则判断的代码都应该放在后端的Java类Service, Dao或ServletController中。JSP通过EL表达式和JSTL来访问Controller准备好的数据模型。合理使用include指令与动作对于静态的、不变化的公共部分如版权信息、静态头部使用% include file”…” %静态包含效率更高。对于动态的、可能变化的模块如用户登录状态栏使用jsp:include page”…” /动态包含更灵活。谨慎使用%! %声明在JSP中声明的实例变量是线程不安全的因为多个请求会共享同一个Servlet实例。除非你非常清楚自己在做什么比如实现一个线程安全的计数器否则避免使用页面声明。及时关闭资源如果在Scriptlet中打开了数据库连接、文件流等资源务必在finally块中确保关闭。更好的做法是这些操作根本不应该出现在JSP里。预编译JSP在生产环境中可以考虑在应用启动时或使用工具预编译JSP将第一次请求时的翻译和编译开销消除提升首次访问速度。JSP作为JavaWeb开发历史中的一个重要里程碑其价值在于它提供了一种相对直观的方式来创建动态网页。尽管在现代前后端分离架构如Spring Boot Vue/React盛行的今天新的项目已经很少直接使用JSP作为视图层技术转而使用Thymeleaf、FreeMarker等模板引擎或者直接提供RESTful API。但理解JSP的工作原理对于深入理解Servlet容器、请求/响应生命周期、MVC模式的思想以及维护遗留项目仍然具有不可替代的意义。它就像学习编程语言中的C语言是构建更高级认知的基础。当你再看到那些“古老”的JSP代码时你不仅能看懂更能知道如何将它优化和现代化。

相关新闻

最新新闻

日新闻

周新闻

月新闻