JavaWeb 学习笔记 5:JSP

2023-09-21 09:00:00

JavaWeb 学习笔记 5:JSP

简单的说,JSP 就是 Java + Html,JSP 的出现是为了让 Java Web 应用生成动态页面更容易。

1.快速开始

1.1.依赖

添加 JSP 依赖:

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
</dependency>

servlet-api 的依赖类似,jsp-api 依赖同样存在于 Tomcat 中,所以这里范围要设置为provided

1.2.JSP

/rwebapp 目录中添加一个简单的 JSP 文件 hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <h1>hello</h1>
    <% System.out.println("hello"); %>
</head>
<body>

</body>
</html>

<% ... %>标签中的是 Java 脚本。

访问 http://localhost:8080/login-demo/hello.jsp 就能看到h1标签中的内容,同时服务端控制台也会输出信息。

1.3.原理

JSP 本质上就是一个 Servlet,在项目运行后,/target/tomcat/work目录下会生成一个hello_jsp.java文件:

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {
    // ...
}

这个文件实际上就是 Tomcat 在收到hello.jsp的请求后,将hello.jsp”翻译“为一个 Servlet 类。

查阅 Tomcat 的 API 文档 可以知道,这个类实际上就是HttpServlet的子类:

public abstract class HttpJspBase
extends HttpServlet
implements HttpJspPage{
    // ...
}

它有一个_jspService()方法,负责处理 HTTP 请求,其主要内容是输出 JSP 中的 HTML 内容以及执行其中的 Java 脚本:

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
    throws java.io.IOException, javax.servlet.ServletException {
    // ...
    out.write("\r\n");
    out.write("\r\n");
    out.write("<html>\r\n");
    out.write("<head>\r\n");
    out.write("    <title>Title</title>\r\n");
    out.write("    <h1>hello</h1>\r\n");
    out.write("    ");
    System.out.println("hello"); 
    out.write("\r\n");
    out.write("</head>\r\n");
    out.write("<body>\r\n");
    out.write("\r\n");
    out.write("</body>\r\n");
    out.write("</html>\r\n");
    // ...
}

当然,Tomcat 生成的 Java 文件同样要编译为字节码(.class)才能执行。

整个过程可以表示为:

image-20210818111039350

2.JSP 脚本

JSP 脚本有三种类型:

  • <% ... %>,脚本中的 Java 代码将在_jspService方法中执行。
  • <%= ... %>,脚本中的 java 表达式的运行结果将作为out.print()方法的参数输出到 Html 页面中。
  • <%! ... %>,脚本中的 Java 代码将作为 Tomcat 生成的 Servlet 的成员变量或成员方法。

第一种之前已经看到过,这里看后两种。

在 JSP 中添加:

<div>
    当前时间:
    <%= LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) %>
</div>

请求 JSP 页面就能看到显示的时间,这个时间是服务端的 JSP 脚本生成并写入 Html 中的。

/target/Tomcat/.../hello_jsp.java_jspService方法中可以看到:

out.print( LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) );

还可以用<% ... %>定义的局部变量进行输出,比如:

<div>
    <% LocalDateTime now = LocalDateTime.now(); %>
    当前时间:
    <%= now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) %>
</div>

可以用<%! ... %>定义成员方法,并由<% ... %><%= ... %>中的 Java 代码调用:

<%!
    String getNowTimeStr(){
    return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
%>
<div>
    当前时间:
    <%= this.getNowTimeStr() %>
</div>

3.案例:品牌列表页

在下面的案例中,用 JSP 实现一个简单的品牌列表展示页面。

先导入表结构和测试数据 tb_brand.sql

用 MyBatis 实现持久层和服务层。

实现用于展示品牌列表的 JSP 页面 brand.jsp

<%@ page import="cn.icexmoon.logindemo.service.BrandService" %>
<%@ page import="cn.icexmoon.logindemo.service.impl.BrandServiceImpl" %>
<%@ page import="cn.icexmoon.logindemo.entity.Brand" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<input type="button" value="新增"><br>
<hr>
<%
  BrandService brandService = new BrandServiceImpl();
  List<Brand> brands = brandService.getAllBrands();
%>
<table border="1" cellspacing="0" width="800">
  <tr>
    <th>序号</th>
    <th>品牌名称</th>
    <th>企业名称</th>
    <th>排序</th>
    <th>品牌介绍</th>
    <th>状态</th>
    <th>操作</th>
  </tr>
  <% for(Brand b : brands){ %>
  <tr align="center">
    <td><%= b.getId() %></td>
    <td><%= b.getBrandName() %></td>
    <td><%= b.getCompanyName() %></td>
    <td><%= b.getOrdered() %></td>
    <td><%= b.getDescription() %></td>
    <td>
      <% if(b.getStatus()==0){ %>
      启用
      <% }else{ %>
      禁用
      <% } %>
    </td>
    <td><a href="#">修改</a> <a href="#">删除</a></td>
  </tr>
  <% } %>
</table>

</body>
</html>

在 JSP 中调用了服务层的代码,需要使用<%@ page import="..." %>导入到 Tomcat 生成的 Servlet 类中。

此外获取到品牌列表数据后,这里通过循环遍历的方式输出表格内容。

4.EL 和 JSTL

将 Java 代码和 HTML 混合起来的 JSP 并不是一个好的解决方案,可读性差。MVC 思想中,将视图(页面展示)和数据准备分离是很重要的一条原则。当然使用 JSP 也可以实现视图和数据的分离,比如将数据准备放在 Servlet 中,在 JSP 中加载数据和页面渲染。

但是加载数据和页面渲染同样要使用 JSP 脚本,可以使用 EL 和 JSTL 两种技术来进行简化。

4.1.EL 表达式

EL(Expression Language,表达式语言)用于简化在 JSP 中获取数据的操作。

EL 中,可以用${name}表示一个名称为name的域属性值。这里所谓的”域“分为以下几种:

  • page:当前页面
  • request,当前请求
  • session,当前会话
  • application,当前应用

我们之前使用的HttpServletRequest.setAttribute就是设置request域的属性,request域的属性对当前 HTTP 请求都有效,所以可以用于多个 Servlet 流转时传递信息。

一个简单示例:

@WebServlet("/brand/list")
public class ListController extends HttpServlet {
    private BrandService brandService = new BrandServiceImpl();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Brand> brands = brandService.getAllBrands();
        request.setAttribute("brands", brands);
        request.getRequestDispatcher("/demo.jsp").forward(request, response);
    }
	// ...
}

demo.jsp 的内容:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${brands}
</body>
</html>

这里<%@page isELIgnored="false" %>是为了开启 EL 表达式解析功能。

此时请求 /brand/list 就能在页面上直接打印我们传递的List对象了。

查看 Tomcat 生成的 demo_jsp.java

out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${brands}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));

可以看到是将${brands}作为 EL表达式解析,以获取属性值。

如果 EL 表达式不起作用,可以检查 web.xml 中的web-app版本,在低版本中默认不开启 EL 表达式。具体相关内容可以阅读这篇文章

这里获取属性值的规则是按照域的大小从小到大获取:

image-20210818152857407

即先检查 Page 中有没有该名称的属性,再检查 Request,以此类推,直到找到为止。

4.2.JSTL

JSTL(Jsp Standard Tag Library,JSP 标准标签库)是用于取代 JSP 中常见 Java 代码的一组标签库。

要使用 JSTL 需要引用依赖:

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

使用 JSTL 标签替换 JSP 脚本:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<input type="button" value="新增"><br>
<hr>
<table border="1" cellspacing="0" width="800">
  <tr>
    <th>序号</th>
    <th>品牌名称</th>
    <th>企业名称</th>
    <th>排序</th>
    <th>品牌介绍</th>
    <th>状态</th>
    <th>操作</th>
  </tr>
<c:forEach items="${brands}" var="b">
  <tr align="center">
    <td>${b.id}</td>
    <td>${b.brandName}</td>
    <td>${b.companyName}</td>
    <td>${b.ordered}</td>
    <td>${b.description}</td>
    <td>
      <c:if test="${b.status==0}">
        启用
      </c:if>
      <c:if test="${b.status==1}">
        禁用
      </c:if>
    </td>
    <td><a href="#">修改</a> <a href="#">删除</a></td>
  </tr>
</c:forEach>
</table>
</body>
</html>

JSP 的内容简略了很多,并且可读性更强,更接近于 HTML 的风格。

更多的 JSTL 标签介绍可以阅读这里

5.案例:对品牌的增加、删除和修改

用上边介绍的内容可以在之前示例的基础上进一步实现对品牌的增加、删除和修改。

实现并不复杂,可以查看视频我参考我实现的完整示例

The End,谢谢阅读。

本文的完整示例可以从这里获取。

6.参考资料

更多推荐

PLC串口通讯和通讯接口知识汇总

在使用PLC的时候会接触到很多的通讯协议以及通讯接口,最基本的PLC串口通讯和基本的通讯接口你都了解吗?一、什么是串口通讯?串口是一种接口标准,是计算机上一种非常通用设备通信的协议。它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。典型的串口通讯标准常见有如下三种。EIARS232(通常简称“RS232”):

【C++】命名空间 namespace 与 标准流 iostream ( 命名空间概念简介 | 命名空间定义 | 命名空间使用 | iostream 中的命名空间分析 )

文章目录一、命名空间namespace1、命名空间基本概念2、名称概念4、C语言的命名空间3、命名空间避免标识符冲突二、命名空间定义1、命名空间基本概念2、命名空间定义语法3、代码示例-命名空间定义使用三、命名空间使用1、命名空间默认访问方式2、使用命名空间3、使用默认的命名空间4、代码示例-使用命名空间四、标准流io

MySQL学习系列(11)-每天学习10个知识

目录1.数据库设计的关键因素2.使用存储过程和函数来提高性能和可重用性3.MySQL性能优化4.使用视图简化查询和提供数据安全性5.数据库备份和恢复策略的重要性和实践经验6.在分布式系统中保证数据一致性和可用性7.理解MySQL的复制和其在实际应用中的作用8.使用游标进行分页查询和遍历查询结果9.使用窗口函数10.数据

Redis 面试常见问答

本文出自:https://thinkinjava.cn作者:莫那鲁道1.什么是缓存雪崩?怎么解决?一般而言,我们会利用缓存来缓冲对数据库的冲击,假如缓存无法正常工作,所有的请求便会直接发送至数据库,进而导致数据库崩溃,从而导致整个系统崩溃。如何解决呢?2种策略(同时使用):对缓存做高可用,防止缓存宕机使用断路器,如果缓

深入学习 Redis - 分布式锁底层实现原理,以及实际应用

目录一、Redis分布式锁1.1、什么是分布式锁1.2、分布式锁的基础实现1.2.1、引入场景1.2.2、基础实现思想1.2.3、引入setnx1.3、引入过期时间1.4、引入校验id1.5、引入lua脚本1.5.1、引入lua脚本的原因1.5.2、lua脚本介绍1.6、过期时间续约问题(看门狗WatchDog)1.7

十四、流式编程(2)

本章概要中间操作跟踪和调试流元素排序移除元素应用函数到元素在map()中组合流中间操作中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。跟踪和调试peek()操作的目的是帮助调试。它允许你无修改地查看流中的元素。代码示例:Peeking.javaclassPeeking{publicst

Docker概念通讲

目录什么是Docker?Docker的应用场景有哪些?Docker的优点有哪些?Docker与虚拟机的区别是什么?Docker的三大核心是什么?如何快速安装Docker?如何修改Docker的存储位置?Docker镜像常用管理有哪些?如何创建Docker容器?Docker在后台的标准运行过程是什么?Docker网络模式

Apollo

Apollo🍓目前市面上比较多的配置中心🍓Apollo组件🍓Apollo特性🍓Apollo服务端安装🍓部署架构🍓核心概念🍓客户端连接Apollo🍓配置发布原理代码地址:https://gitee.com/xuhx615/apollo-demo.git🍓目前市面上比较多的配置中心⭐Disconf百度开源

Dubbo3基础使用

1、Dubbo概述现在SpringCloudAlibaba比较火,用的比较多是吧,那dubbo是不是过时的呢?并不是的,以前有人把Dubbo和SpringCloud进行对比,其实两者是不同维度的,不能对比,dubbo就是一个rpc框架,SpringCloud是一个生态,里面包括很多组件,并且dubbo3也可以和Spri

【K8S系列】深入解析k8s网络插件—Weave Net

序言做一件事并不难,难的是在于坚持。坚持一下也不难,难的是坚持到底。文章标记颜色说明:黄色:重要标题红色:用来标记结论绿色:用来标记论点蓝色:用来标记论点Kubernetes(k8s)是一个容器编排平台,允许在容器中运行应用程序和服务。今天学习一下k8s网络插件-WeaveNet相关知识希望这篇文章能让你不仅有一定的收

一文总结提示工程框架,除了CoT还有ToT、GoT、AoT、SoT、PoT

夕小瑶科技说原创编译|谢年年大语言模型LLM被视为一个巨大的知识库,它可以根据你提出问题或陈述的方式来提供答案。就像人类可能会根据问题的不同提供不同的答案一样,LLM也可以根据输入的不同给出不同的答案。因此,你的问题或陈述方式就显得非常重要。如何引导大语言模型给出更恰当的答案,是最近研究的热点。经常用到的方法如让大模型

热文推荐