拓冰建站拓冰建站
首页 / 资讯中心 / 正文

JSP入门到实践:从运行原理到EL/JSTL与常见问题排查

还记得你第一次用Servlet往浏览器里输出一整个HTML页面时的心情吗字符串拼接标签、转义引号、数据混在HTML里改来改去那时候我就想要是能直接在HTML里写Java代码就好了。JSP就是为解决这个痛点而生的。这篇博文是JavaWeb开发系列的第六篇聊的是JSP基础。我默认看这篇文章的朋友已经掌握了Servlet的基本用法知道请求响应模型也了解MVC的思想正准备进入视图层的开发。如果你常年用前后端分离那套架构那JSP确实用得不多但它并没有过时——国内大量存量系统、中小型项目的管理后台、模板渲染场景JSP依然是主力。这篇文章我会从JSP的运行原理讲起带你把JSP的基础语法、隐式对象、指令标签过一遍再用一个完整的个人信息展示页面把知识点串起来最后把“JSP改了不生效”“中文乱码”“JSON数据输出”这些高频问题一并解决掉。1. JSP到底是个什么东西先破除神秘感1.1 JSP的本质就是Servlet你可能会觉得JSP是个全新的技术其实不是。JSP的全称是Jakarta Server Pages以前叫JavaServer Pages本质上它就是一个被“包装过”的Servlet。我第一次接触JSP的时候老师直接在黑板上画了一张图浏览器发出请求服务器找到对应的JSP文件先把JSP翻译成Java文件再编译成class文件然后执行把结果返回给浏览器。我当时听得一头雾水直到自己动手把Tomcat的work目录翻出来看了一眼才恍然大悟。你在work/Catalina/localhost/项目名/org/apache/jsp/目录下能看到每个JSP文件对应的_jsp.java和_jsp.class文件。打开那个Java文件你会发现它继承了org.apache.jasper.runtime.HttpJspBase而这个类间接实现了HttpServlet接口。所以你可以把JSP理解成一个“反过来的Servlet”Servlet是Java代码里嵌HTML字符串JSP是HTML里嵌Java代码。1.2 JSP的生命周期和请求流程JSP的生命周期和Servlet类似但多了一个翻译阶段。完整流程是这样的浏览器发送请求Tomcat接收到后缀为.jsp的请求后交给JSP引擎处理。JSP引擎检查这个JSP文件有没有对应的Java文件或者JSP文件是否比已有的Java文件更新。如果是第一次访问或者JSP文件被修改过引擎会把JSP翻译成Java源文件然后编译成class文件。容器加载这个class创建实例调用jspInit()方法完成初始化只会执行一次。每次请求都会调用_jspService()方法方法内部执行你写在JSP里的脚本代码然后把生成的HTML响应回浏览器。容器销毁时调用jspDestroy()方法。这里有一个非常多新手踩的坑第一次访问JSP页面经常特别慢感觉像卡住了一样。原因就是第一次访问需要完成翻译加编译后面再访问就直接走class文件了。所以上线之前最好先“预热”一遍JSP页面把所有页面都访问一次这样用户看到的就不会是第一印象的“卡顿”。1.3 JSP和Servlet的分工边界JSP出现以后很多初学者容易走向另一个极端所有代码都写在JSP里数据库连接也写在JSP里业务逻辑也写在JSP里页面变成了一锅粥。我见到过把几百行Java代码写在JSP里的项目维护起来简直是一场灾难。按MVC的思想来划分JSP只负责视图View的渲染也就是数据的展示Servlet负责控制器Controller接收请求、调度业务逻辑JavaBean或者Service负责模型Model处理数据和业务规则。真实项目中你很少会看到Servlet直接输出HTML也很少会看到JSP直接操作数据库各司其职才是JSP正确的使用姿势。2. JSP的核心语法脚本、指令、动作与隐式对象2.1 三种脚本元素声明、表达式、脚本段JSP提供了三种脚本元素对应Java代码里的三种场景。声明用%! ... %包裹用来定义成员变量和方法。声明的内容会翻译成Servlet类的成员变量和成员方法所以多个请求之间共享一份数据这也意味着它是线程不安全的非必要不要用。表达式用% ... %包裹用来向页面输出内容翻译后相当于out.print(...)注意表达式结尾不能加分号。脚本段用% ... %包裹可以写任意Java语句翻译后直接嵌在_jspService()方法里。看个具体例子%! private String siteName 会员中心; private String formatTime(Date date) { SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); return sdf.format(date); } % % String username (String) request.getSession().getAttribute(loginUser); if (username null) { username 游客; } % html body h1% siteName %/h1 p欢迎你% username %/p p当前时间% formatTime(new Date()) %/p /body /html看到没有脚本段里写逻辑表达式负责输出声明提供公共方法。但我要强调的是这三种元素都是早期JSP的写法现在的主流思路是尽量在Servlet里把数据准备好在JSP里用EL表达式和JSTL标签来展示。脚本元素能不用就不用实在要用了也别把业务逻辑往里塞。2.2 page指令配置页面行为的关键page指令是JSP里最常用的指令用来声明页面的属性。常见属性有这些属性作用常用值import导入Java类和Java里的import一样java.util.*contentType设置响应类型和字符集text/html; charsetUTF-8pageEncoding指定JSP文件本身的编码UTF-8session页面是否参与会话true/falseisELIgnored是否忽略EL表达式true/falseerrorPage页面出错时跳转的页面error.jspisErrorPage声明当前页面是错误处理页true/false很多中文乱码问题的根源就是pageEncoding和contentType里的字符集没有设置正确。我一般建议在JSP文件第一行就写上% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %contentType的charset影响的是响应内容告诉浏览器用什么编码来解析pageEncoding影响的是JSP文件本身的读取告诉服务器用什么编码来读这个文件。两个都写UTF-8再配合request.setCharacterEncoding(UTF-8)和response.setCharacterEncoding(UTF-8)乱码问题能解决掉八成。2.3 include指令与include动作的区别JSP里有两张“包含”的玩法一个是% include file... %指令一个是jsp:include page... /动作。两者的区别非常重要。include指令是静态包含翻译阶段就把被包含的文件内容“复制粘贴”到当前页面里最终只生成一个Java文件。这种方式的好处是性能好因为是直接把内容嵌进去的坏处是被包含的文件如果有变量容易和当前页面的变量冲突而且被包含文件改了以后当前页面也需要重新编译。include动作是动态包含它会在运行时把被包含页面的执行结果“拿过来”嵌入当前页面。每次请求都会单独去执行被包含的页面所以被包含页面改了以后只要重新编译它自己就够了。这种方式更灵活但性能略差一些。实际项目里页头、页脚、导航栏这些公共部分我习惯用include指令来做因为它们是静态内容嵌进去以后效率最高。如果是有独立逻辑的动态模块比如登录状态栏、天气信息这种就用include动作这样每个模块可以独立维护。2.4 九大隐式对象页面里的“免费劳力”JSP为我们准备好了九大对象不需要自己创建直接在脚本段或表达式里就能用。这九大对象对应Servlet里的不同角色对象类型作用域requestHttpServletRequest一次请求responseHttpServletResponse一次请求outJspWriter页面输出sessionHttpSession一次会话applicationServletContext整个应用configServletConfig配置信息pageContextPageContext当前页面pageObject当前Servlet实例exceptionThrowable错误页面request、response、session这些你在Servlet里已经很熟了重点说说pageContext。它是九大对象里比较“万能”的一个可以获取其他八个对象还可以操作各种作用域的数据。比如pageContext.setAttribute(key, value, PageContext.REQUEST_SCOPE)一行代码就能往指定作用域存数据。pageContext.findAttribute(key)会按照page - request - session - application的顺序查找属性这个特性在后面讲EL表达式时会很有用。2.5 标准动作标签JSP自带的组件化方案JSP标准动作标签以jsp:前缀开头是JSP规范里自带的一组标签不需要引入任何外部依赖。常用的有这些jsp:include动态包含页面。jsp:forward转发请求到另一个页面或Servlet。jsp:useBean创建或获取一个JavaBean实例。jsp:setProperty设置JavaBean的属性。jsp:getProperty获取JavaBean的属性。jsp:param在include或forward时传递参数。比如转发并带参数jsp:forward page/login.jsp jsp:param namemsg value请先登录 / /jsp:forward使用jsp:useBean时默认会先查找指定作用域里有没有这个Bean有就直接用没有才会新建。这个先查找后创建的特性在页面里展示数据时候很实用。不过现在的开发中useBean系列基本被EL表达式和JSTL取代了我优先推荐大家学EL和JSTL这些动作标签了解即可。3. EL表达式与JSTL让JSP回归模板的本分3.1 EL表达式访问数据的便捷通道ELExpression Language表达式是JSP 2.0引入的用${...}包裹目的是替代脚本段的取值操作。它的核心作用就是“取数据”语法非常简洁。EL内置了一套“点号”规则来访问数据。如果域对象中存了一个User对象其中有username属性那${user.username}就能取到这个属性。你没有看错不需要调用getUsername()方法EL会自己找到标准的getter或者JavaBean属性映射。EL将数据存储空间分成了四个作用域用点的形式依次查找pageContext、requestScope、sessionScope、applicationScope。如果直接用${user}EL会按照这四个作用域的顺序依次查找叫user的属性找到哪个用哪个。这就带来一个问题如果四个作用域都有同名属性你可能取到的是作用域里最小那个。所以当有同名属性时建议显式指定作用域比如${sessionScope.user}。EL还支持简单的运算例如算术运算${1 2}、比较运算${score 60}、逻辑运算${empty list}。还有一个超级好用的empty运算符它既能判断是否为null又能判断字符串是否为空串还能判断集合是否为null或者没有元素。我写页面时经常这样做c:if test${empty userList} trtd colspan4暂无数据/td/tr /c:ifEL还提供了很多内置对象比如${pageContext.request.contextPath}可以拿到项目根路径这个在页面里写绝对路径时太重要了。用相对路径的话一旦当前URL层级发生变化资源文件路径就全乱了所以我都是统一用这个表达式来拼路径。3.2 JSTL核心标签库逻辑控制不再写JavaJSTLJSP Standard Tag Library是JSP的标准标签库提供了很多现成标签来处理常见的页面逻辑。使用之前需要先引入核心标签库的指令% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %prefixc是给这个标签库起的简称后面的uri是这个库的唯一标识。引入之后就可以用了。最常用的是c:if、c:choose和c:forEach这三个。c:if是条件判断但注意它没有else分支。需要if-else效果时用c:choose搭配c:when和c:otherwisec:choose c:when test${user.level 1} span classbadge badge-primary普通会员/span /c:when c:when test${user.level 2} span classbadge badge-success高级会员/span /c:when c:otherwise span classbadge badge-dangerVIP会员/span /c:otherwise /c:choosec:forEach是循环遍历支持集合、数组和Map。比如遍历用户列表c:forEach items${userList} varuser varStatusstatus tr td${status.index 1}/td td${user.username}/td td${user.email}/td /tr /c:forEachvarStatus属性非常好用它暴露了循环的状态比如index当前索引从0开始、count循环计数从1开始、first是否是第一条和last是否是最后一条。列表表格里做序号列、条纹背景就靠它了。JSTL还有格式化标签库fmt做日期和数字格式化很好用% taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % fmt:formatDate value${user.createTime} patternyyyy-MM-dd HH:mm:ss / fmt:formatNumber value${user.balance} pattern#,##0.00 /视图层尽量只做展示和轻逻辑这一点一定要记住。有了EL和JSTL你几乎可以完全告别JSP里的Java脚本代码了页面干净不少可维护性也高了不止一个档次。3.3 MVC中的JSP最佳实践把前面这些内容串起来一个合理的MVC数据流长这样Servlet接收请求调用Service层获取数据把数据封装成JavaBean或List然后用request.setAttribute()存到请求域中最后转发到JSP。JSP里用EL取数据用JSTL控制结构完全不写Java代码。这个模式的好处是前端改页面样式时不需要懂Java后端改逻辑时也不会碰坏页面。分工清晰每个人改自己擅长的部分。4. 实战案例从零搭建一个会员信息展示页面4.1 准备工作2023版IDEA创建JavaWeb项目先说创建项目。现在大家用的IDEA基本都是2021以后的版本新建JavaWeb项目和以前不太一样。打开IDEA选择新建项目New Project左侧选Jakarta EE这个选项在较新的IDEA版本里默认就带。然后在Application Server那边选你本地的Tomcat比如Tomcat 9或Tomcat 10。如果下拉框里没有点New手动选择Tomcat的安装目录IDEA会识别到Tomcat的版本信息。接下来配置项里要注意两个关键选择Template那里可以选Web Application这样会自带一个标准的webapp目录结构。如果不用模板直接建普通Java项目之后再手工添加webapp目录也行但比较啰嗦建议直接用模板。项目建好以后默认的webapp/WEB-INF/下会有web.xml文件。如果你的Tomcat版本比较高web.xml可能不在项目里因为Servlet 3.0以后支持注解配置web.xml不是必须的了。但JSP项目我建议保留一个web.xml后面设置欢迎页和URL映射时方便。还要检查一下项目依赖里有没有Servlet API和JSP API的jar包。很多新手项目跑起来报ClassNotFoundException: javax.servlet.*通常就是这个原因。IDEA新建的Jakarta EE项目一般会自动带但要注意版本匹配问题Tomcat 9及之前用javax.servlet包名Tomcat 10开始改成jakarta.servlet包名这俩不能混用会直接编译报错。4.2 构建一个带分页和条件渲染的列表页光说理论没用直接做一个例子。假设我有一个旅游管理系统的后台需要展示会员列表。这个场景非常典型各家后台管理系统的列表页都长差不多。先写一个Member实体类public class Member { private int id; private String username; private String email; private String phone; private int level; // 1普通 2高级 3VIP private double balance; private Date createTime; // 省略getter/setter }然后是Servlet负责查数据并转发WebServlet(/member/list) public class MemberListServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { MemberService service new MemberService(); int page 1; int pageSize 10; String pageStr request.getParameter(page); if (pageStr ! null !pageStr.isEmpty()) { page Integer.parseInt(pageStr); } PageResultMember pageResult service.queryPage(page, pageSize); request.setAttribute(pageResult, pageResult); request.getRequestDispatcher(/member/list.jsp).forward(request, response); } }接下来是JSP页面。这里的核心是分页按钮和表格数据展示。分页按钮加上上一页、下一页的链接上一页的URL拼上pagepage-1下一页拼上pagepage1% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % c:set varctx value${pageContext.request.contextPath} / html head title会员列表/title link relstylesheet href${ctx}/css/style.css /head body h2会员列表/h2 table border1 cellpadding8 cellspacing0 tr th序号/th th用户名/th th邮箱/th th等级/th th余额/th th注册时间/th /tr c:forEach items${pageResult.list} varm varStatusst tr td${(pageResult.currentPage - 1) * pageResult.pageSize st.count}/td td${m.username}/td td${m.email}/td td c:choose c:when test${m.level 1}普通会员/c:when c:when test${m.level 2}高级会员/c:when c:otherwiseVIP会员/c:otherwise /c:choose /td tdfmt:formatNumber value${m.balance} pattern¥#,##0.00 //td tdfmt:formatDate value${m.createTime} patternyyyy-MM-dd //td /tr /c:forEach /table div classpagination c:if test${pageResult.currentPage 1} a href${ctx}/member/list?page${pageResult.currentPage - 1}上一页/a /c:if 第 ${pageResult.currentPage} / ${pageResult.totalPages} 页 c:if test${pageResult.currentPage pageResult.totalPages} a href${ctx}/member/list?page${pageResult.currentPage 1}下一页/a /c:if /div /body /html这个例子虽然短但把EL取数、JSTL条件判断、循环遍历、格式化标签、动态路径都串起来了算是JSP基础语法的综合应用。有一个细节要提醒String拼接URL时注意page参数的类型转换如果用户直接在浏览器地址栏把page改成非数字Integer.parseInt会抛异常。实际项目里一定要做参数校验和异常处理这里为了演示简洁先不写全。4.3 JSP里输出JSON数据和导出Excel有人会觉得JSP只能输出HTML其实response的ContentType一变它能输出的类型多着呢。最常见的一个场景是页面里用jQuery发AJAX请求后端需要返回JSON数据。JSP页面里输出JSON的办法是这样的% page contentTypeapplication/json; charsetUTF-8 pageEncodingUTF-8 % % // 实际项目中从Service获取数据然后用Fastjson或Jackson序列化 User user new User(); user.setId(1); user.setUsername(zhangsan); // 这里演示直接用字符串真实场景建议用ObjectMapper String json {\id\:1,\username\:\zhangsan\}; out.print(json); %这段代码能跑但我不推荐用脚本段这种写法。更好的做法是在Servlet里把数据序列化好然后设置response.setContentType(application/json; charsetUTF-8)直接用response.getWriter().write(json)输出。是否需要通过JSP输出JSON实际意义不大还会多一次视图层跳转的开销。这个场景更适合在Servlet里完成大家千万别学我上面那堆脚本式的写法我只是演示JSP的能力边界。导出Excel也是类似的思路。把ContentType设成application/vnd.ms-excel浏览器就会识别为Excel文件然后下载。你可以在JSP里用循环把表格内容输出为Excel兼容的HTML格式用response.setHeader(Content-Disposition, attachment;filenamemember.xls)设置下载文件名。这个办法不需要任何第三方库原理是利用Excel能打开HTML表格的特性实用性很强。但数据量大的场景比如上万行建议直接用POI等专业库来生成真正的Excel文件性能和格式控制好得多。4.4 文件上传和MP4视频播放的JSP页面搜索词里还有两个高频场景jsp实现mp4视频播放和文件上传下载。我在这里简单讲一下。视频文件通常不要放进项目目录里更不要放到WEB-INF下因为WEB-INF下的资源对浏览器是受保护的外部访问不到。常规做法是把文件存到一个独立的上传目录比如D:/upload/或者服务器上的/data/upload/然后在Tomcat里配置虚拟目录映射把/upload/**的访问指向物理目录。如果你用的是IDEA内置的Tomcat配置虚拟目录有点麻烦我一般直接用项目根目录下的static/upload/来存然后页面里的src指向${ctx}/static/upload/video.mp4。播放视频的JSP核心就一个video标签video src${ctx}/static/upload/sample.mp4 controls width640 height360 您的浏览器不支持HTML5视频播放。 /video这里最容易出问题的是MIME类型。Tomcat默认配置里对.mp4的Content-Type是video/mp4大多数情况能正常播放但如果浏览器不认这个类型播放器就会黑屏。你可以在web.xml里显式配置一下mime-mapping extensionmp4/extension mime-typevideo/mp4/mime-type /mime-mapping文件上传的JSP页面就两件事表单加enctypemultipart/form-data然后用Servlet 3.0的Part接口接收文件。JSP本身不负责处理上传它只是提供一个带文件选择框的表单而已真正的处理逻辑还是在Servlet里。5. 高频问题排查实录改了不生效、乱码与路径问题5.1 “JSP改了不生效”的五种常见原因这个拿去搜索命中率绝对高。每个JSP开发者都经历过“我明明改了代码刷新页面还是老样子”的绝望时刻。我总结一下常见的五个原因按排查优先级排第一个浏览器缓存。JSP页面在浏览器侧可能会被缓存尤其是用GET请求访问时。第一次加载后浏览器直接把换回的东西存在本地缓存里你改了服务器端代码浏览器还在吃老本。解决办法很简单开发的时候按CtrlF5强制刷新或者在response头里加禁用缓存的配置。第二个IDEA的JSP引擎没有重新部署。改完JSP以后IDEA不一定会自动把新文件同步到Tomcat的部署目录里。你需要看看IDEA右上角的Build菜单确认已勾选自动构建或者每次改完手动执行一次Build - Rebuild Project。第三个Tomcat的work目录里有旧的编译产物。JSP翻译成的Java文件和class文件都存在Tomcat的work目录里。如果Tomcat没检测到JSP文件被修改文件时间戳没变它会直接用旧的class。这种情况我一般直接删掉work/Catalina/localhost/项目名目录然后重启Tomcat让一切重新来。这个方法屡试不爽。第四个JSP页面被include到了其他页面。如果你修改的是一个被静态包含的公共文件比如header.jsp那么主页面不会自动感知到它的变化。因为静态包含是翻译阶段就把内容复制进去了主页面重新编译之前被包含文件的修改不会生效。解决办法是修改主页面或者把公共部分改成动态包含。第五个部署位置不对。IDEA有个Deployment的设置决定了项目的资源部署到哪里。如果这里配置错了你改的文件根本不会发布到Tomcat的运行目录里。检查路径的方法在IDEA底部打开Server面板看它的Deployed资源路径确认改动同步到了目标目录。5.2 中文乱码的根源和一次性解决方案JSP乱码问题根源就是编码不一致。整个链路上有四个环节需要统一编码JSP文件本身的编码pageEncoding。服务器发送给浏览器的编码contentType的charset。浏览器解析页面的编码取决于响应头里的Content-Type。如果涉及表单提交或AJAX请求还有请求参数的编码。排查思路如下如果页面上中文显示成???那是JSP文件本身编码不对把pageEncoding改成UTF-8并且确认文件确实是以UTF-8格式保存的。IDEA右下角能看到文件编码格式确保是UTF-8。如果页面上的DB数据中文乱码多半是数据库连接串没有指定编码。JDBC连接URL后面加?useUnicodetruecharacterEncodingUTF-8MySQL里表字段的字符集也要保证是utf8mb4MySQL 5.5以后推荐utf8mb4能存emoji字符。如果是表单提交成功后页面数据中文乱码GET请求修改Tomcat的server.xml里的URIEncodingUTF-8POST请求在Servlet或过滤器中调用request.setCharacterEncoding(UTF-8)。Servlet 4.0之后还可以在web.xml里配置一个编码过滤器一次配置全局生效filter filter-nameencodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping用了这个过滤器以后整个项目的POST请求编码都会被统一成UTF-8。实测下来很省心。5.3 import和依赖瘦身JSP里导包的两个技巧热搜词里有个jsp jsonarray import这其实是两个独立的问题JSP里怎么导入JSON相关的包。第一个问题怎么在JSP里导入类。在% page %指令的import属性里写逗号分隔的类路径就行% page importcom.alibaba.fastjson.JSONArray % % page importcom.alibaba.fastjson.JSONObject %注意import可以分多条写也可以一条里用逗号分隔多个类。第二个问题怎么把依赖的jar包部署到项目里。很多人把jar包下载好了也加到IDEA的Library里了代码里import也不报错但一运行就报ClassNotFoundException。原因很简单jar包只出现在了编译期的ClassPath里没有出现在Tomcat运行时的WEB-INF/lib目录下。解决办法把jar包复制到webapp/WEB-INF/lib目录下或者在IDEA里对jar包右键选择Add as Library然后确认部署时勾选了Include in project build。用Maven或者Gradle项目的同学直接声明依赖就够了构建工具会自动把jar包打进去。我建议从前期就统一用Maven依赖管理会省很多事。5.4 页面路径那些事contextPath的正确用法绝对路径还是相对路径这个问题不搞清楚页面里引CSS、JS、图片、跳转链接时容易出各种“找不到资源”的错。根本原因是浏览器地址栏里的URL和应用的实际根路径不一定一致。举个例子项目部署名是travel你的JSP页面在/member/list.jsp如果页面里写死href/css/style.css浏览器会去请求http://localhost:8080/css/style.css显然找不到正确路径是http://localhost:8080/travel/css/style.css。所以写路径时优先用${pageContext.request.contextPath}来动态拼。比如link relstylesheet href${ctx}/css/style.css a href${ctx}/member/list?page1会员列表/a form action${ctx}/member/add methodpost.../form页面顶部先用c:set把这个变量存起来后面直接用${ctx}页面会清爽很多。还有一个小细节项目部署名不能频繁改一旦改了部署名所有绝对路径都要跟着变。最稳妥的做法是用根路径部署也就是contextPath为空这样项目访问地址为http://localhost:8080/相对路径的写法就跟Standalone的应用差不多了。6. 关于JSP后续演进从JSP到模板引擎的迁移思路学了JSP基础以后很多人会有一个疑惑现在新项目还该不该用JSP我的看法是JSP的语法和思想值得学因为你会遇到大量存量代码用JSP写的项目接手别人的项目连JSP都看不懂才是真麻烦。但如果从零开始一个新项目又不需要依赖Java服务端渲染的特殊能力我更推荐用Thymeleaf、Freemarker这类现代模板引擎它们语法更干净和前端协作更顺滑。迁移的过程也简单。JSP里的% xxx %和${xxx}Thymeleaf里用th:text${xxx}代替JSP的c:forEachThymeleaf里用th:each代替JSP的% include %Thymeleaf里用th:insert和th:replace。核心的思想完全相通——数据放域中模板负责展示渲染逻辑和业务逻辑分离。你把JSP的阶段学好以后切到其他模板引擎基本上半天就能上手。但有一件事必须强调无论你用什么模板引擎“把页面当视图、把逻辑留在后端”这个MVC原则是不变的。JSP曾经被滥用不是因为JSP技术本身有问题而是因为用的人没有边界感。任何技术都禁不起“无脑往里塞逻辑”这种用法。7. 写在最后的一点经验这一系列文章写下来JSP基础算是告一段落了。我自己带新人的时候最常说的话就是如果JSP页面里出现了大段的Java脚本代码你第一件事不是把代码跑通而是先停下来想想架构上哪里出了问题。JSP的定位永远是视图层是给前端展示数据用的不是让你写业务逻辑的。你在动手练习的时候先不要急着上框架用原生的Servlet加JSP把一个登录注册、列表分页、增删改查完整做一遍。这个过程看起来“土”但它能把HTTP协议、请求响应、作用域、JSP翻译机制这些底层的原理彻底打通。多年以后你回头看这一步走得值。JSP的技术栈并不复杂语法标签就那么几个真正的难点在于理解它的定位学会在各种技术之间做权衡。下一篇我会接着讲JavaWeb里另一个躲不掉的话题Session与Cookie的进阶应用。到时候我们再来聊聊登录状态保持、跨域会话共享的问题。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门