【Java Web编程 三】JSP基本概念和语法

简介: 【Java Web编程 三】JSP基本概念和语法

上文中提到,Tomcat会处理JSP和Servlet两种类型的文件,那么我们先来看看这两种类型文件是什么,有什么作用,然后再从Tomcat的架构角度去看请求是怎么被处理的,本篇文章重点探讨JSP的整体运行机制。

JSP概念简介

前文我们说过有静态和动态两种网站模式,其实我们大多数的网站都是动态网站,所以不能依赖单纯的Html去构建。这个时候我们有两种方式去解决这个问题:

  1. 把html代码和样式嵌入到Java代码中,然后将Java文件解析运行,这种方式下我们需要分析的是html代码,将html代码打印出来
  2. 把Java代码嵌入到html代码和样式中,然后将该文件动态解析为Java文件运行,这种方式下我们需要分析的是Java代码,将Java代码运行。

依据实际情况来看,因为html样式代码数量远大于Java代码,我们的修改动作也较为频繁,而我们希望更方便的调整html样式而不需要面对大量的print打印语句。

所以我们选择第二种方式,由此也引出了JSP的概念:Java Server Page 简称 JSP,是由 Sun 公司倡导建立的一种动态网页技术标准,用于开发动态网页,JSP就是将传统Java代码嵌入到Html页面代码中,由Web服务器进行编译执行,生成最终的静态Html返回客户端

JSP执行流程

浏览器无法直接运行JSP文件,只有将包含JSP文件的Web项目部署到Web服务器上,才能看到JSP的显示效果,当客户端浏览器向服务器发出请求访问一个JSP页面后,服务器根据该请求加载相应的JSP 页面,并对该页面进行转换、编译和执行。

具体的执行过程如下:

  1. 浏览器发送一个 HTTP 请求给服务器。
  2. Web 服务器识别出这是一个对 JSP 网页的请求,并且将该请求传递给 JSP 引擎。通过使用 URL或者 .jsp 文件来完成。
  3. JSP 引擎从磁盘中载入 JSP 文件,然后将它们转化为 Servlet。这种转化只是简单地将所有模板文本改用 println() 语句,并且将所有的 JSP 元素转化成 Java 代码。
  4. JSP 引擎将 Servlet 编译成可执行类,并且将原始请求传递给 Servlet 引擎。
  5. Web 服务器的某组件将会调用 Servlet 引擎,然后载入并执行 Servlet 类。在执行过程中,Servlet 产生 HTML 格式的输出并将其内嵌于 HTTP response 中上交给 Web 服务器。
  6. Web 服务器以静态 HTML 网页的形式将 HTTP response 返回到浏览器中。
  7. 最终,Web 浏览器处理 HTTP response 中动态产生的HTML网页,就好像在处理静态网页一样

总体而言,引擎会帮我们将JSP编译为Servlet,然后执行

JSP组成元素

JSP文件中包括两类代码:JSP元素和Template(模版):

  • JSP元素:指的就是<%...... %>内的代码,这部分代码是JSP引擎直接处理的部分,这部分必须符合Java语法
  • Template(模版):指的就是<%...... %>外的代码,例如代码中HTML的内容,这些数据JSP引擎不作处理,直接返回给客户端的浏览器

所以解析处理的时候其实只需要处理JSP元素。

JSP生命周期

JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命周期还包括将JSP文件编译成servlet:

  1. 编译阶段:servlet容器编译servlet源文件,生成servlet类。当浏览器请求JSP页面时,JSP引擎会首先去检查是否需要编译这个文件。如果这个文件没有被编译过,或者在上次编译后被更改过,则编译这个JSP文件。分为三个步骤:解析JSP文件;将JSP文件转为servlet;编译servlet
  2. 初始化阶段:加载与JSP对应的servlet类,创建其实例,并调用它的初始化方法jspInit
  3. 执行阶段:调用与JSP对应的servlet实例的服务方法_jspService
  4. 销毁阶段:调用与JSP对应的servlet实例的销毁方法jspDestroy,然后销毁servlet实例

整个流程图如下:

JSP语法标识

JSP的语法分为三类:指令标识、脚本标识注释标识动作标识。这里我们以一个JSP页面进行举例,贯穿接下来的练习代码

index.jsp文件

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" %>
<%@ include file="top.jsp" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Hello World</title>
    <%!
        int number = 0;// 声明全局变量
        int count() {
            number++;//number 自增
            return number;
        }%>
</head>
<h1><%= "Hello World!" %>
</h1>
<br/>
<body>
<%
    String print = "我是脚本语句内容";
    List<String> strList = new ArrayList<>();
    strList.add("脚本语句1");
    strList.add("脚本语句2");
    strList.add("脚本语句3");
//  strList.add("脚本语句4");  //这是一个Java注释,用来注释Java代码,查看源码时不可见
%>
<a href="hello-servlet">我的第一个JavaWeb项目</a> <!-- 这是一个html注释,可见<%= new Date().getTime() %> -->
<br/>
<%--这是一个JSP注释,查看源码时不可见--%>
声明语句示例:这是第 <%=count()%> 次访问该页面
<br/>
表达式语句示例: <%="我是一个表达式语句"%>
<br/>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>
<jsp:forward page="result.jsp"></jsp:forward>
跳转语句之后,代码不会被执行:<%="我是一个跳转后的表达式语句"%>
</body>
</html>

top.jsp文件

<%--
  Created by IntelliJ IDEA.
  User: tianmaolin
  Date: 2021/7/13
  Time: 8:15 下午
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" %>
<html>
<head>
    <title>我是include的top.jsp页面</title>
</head>
<body>
<%
  String printTop="top打印";
%>
<a href="hello-servlet">我是top的打印,<%=printTop%></a>
</body>
</html>

result.jsp文件

<%--
  Created by IntelliJ IDEA.
  User: tianmaolin
  Date: 2021/7/14
  Time: 11:32 上午
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>跳转的结果页面</title>
</head>
<body>
<a>我是跳转后的结果页面</a>
</body>
</html>

test.jsp文件

按照语法基础,我们实践三种语法流程:判断逻辑,循环逻辑:

<%--
  Created by IntelliJ IDEA.
  User: tianmaolin
  Date: 2021/7/14
  Time: 12:30 下午
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*,java.io.*" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%! int day = 3; %>
if语句输出练习:
<br/>
<% if (day == 1 || day == 7) { %>
<p>今天是周末</p>
<% } else { %>
<p>今天不是周末</p>
<% } %>
<br/>
for语句输出练习;
<br/>
<%for ( int fontSize = 1; fontSize <= 3; fontSize++){ %>
<span style="color: green; ">
    循环第 <%=fontSize%> 遍<br/>
</span><br/>
<%}%>
</body>
</html>

指令标识

指令标识主要用于设定在整个 JSP 页面范围内都有效的相关设置信息,它是被服务器解释并执行的,不会产生任何内容输出到网页中,格式如下:

<%@ 指令名 属性 1=“属性值 1” 属性 2=“属性值 2”...%>
  • 指令名:用于指定指令名称,在 JSP 中包含 page、include 和 taglib 3 个指令。
  • 属性:用于指定属性名称,不同的指令包含不同的属性。在一个指令中,可以设置多个属性,属性之间用逗号或空格隔开。
  • 属性值:用户指定属性值

上述文件中<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 就是一个page指令。

指令标识包含:page指令、include指令以及taglib指令。

page指令

Page指令为容器提供当前页面的使用说明。一个JSP页面可以包含多个page指令,page的标准语法如下:

<%@ page attribute="value" %>

page指令的常用属性如下:

例如我们想要访问list相关的方法,就需要引入java的util类:

<%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%>

include指令

JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。Include指令的语法格式如下:

<%@ include file="文件相对 url 地址" %>

include 指令中的文件名实际上是一个相对的 URL 地址,例如:

注意:在应用 include 指令包含文件时,为使整个页面的层次结果不发生冲突,应将被包含页面中的 元素和 元素删除,因为包含该页面的文件中已经指定了这些标记

Taglib指令

在 JSP 文件中,可以通过 taglib 指令声明该页面中所使用的标签库,同时引用标签库,并指定标签前缀,在页面中引用标签库后,就可以通过前缀引用标签库中的标签

<%@taglib prefix=“core” uri=“http://java.sun.com/jsp/jstl/core” %>

标签库的使用后续会提到。

脚本标识

脚本标识是JSP中最主要的元素,相当于JSP中需要编译的那一部分内容,也是JSP语法中的核心内容,包含声明、表达式以及脚本段

声明

声明标识用于在 JSP 页面中定义全局的变量或方法,通常使用该标识定义整个 JSP 页面需要引用的变量或方法,在JSP文件中,必须先声明这些变量和方法然后才能使用它们,相当于Java里的成员变量和成员方法定义。语法规则如下:

<%! 声明变量或方法代码 %>

一个全局的统计访问次数的例子如下:

<%!
        int number = 0;// 声明全局变量
        int count() {  //声明全局方法
            number++;//number 自增
            return number;
        }%>
  这是第 <%=count()%> 次访问该页面

可以打印出来第几次访问页面

脚本段

Scriptlet(代码片段)脚本程序,是在 JSP 页面中嵌入的 Java 代码或脚本代码,代码片段将在页面请求的处理期间被执行,通过 Java 代码可以定义变量或流程控制语句等,相当于Java里main方法的代码逻辑,语法规则如下:

<% 表达式或者变量名 %>

脚本段语句示例:

<%
    String print = "我是脚本语句内容";
    List<String> strList = new ArrayList<>();
    strList.add("脚本语句1");
    strList.add("脚本语句2");
    strList.add("脚本语句3");
%>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>

可以直接打印脚本段内的内容。

表达式

JSP 表达式用于向页面中输出信息,类似于System.out.println()括号中的内容,相当于Java里main方法里的System.out.println()语句,只不过是输出回客户端,而非服务端本地打印,语法规则如下:

<%= 表达式或者变量名 %>

以上我们无论是声明还是脚本段的输出其实都用到了表达式:

<br/>
声明语句示例:这是第 <%=count()%> 次访问该页面
<br/>
表达式语句示例: <%="我是一个表达式语句"%>
<br/>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>

注释标识

注释用于对某些代码做功能性的说明,从而增加程序的可读性。JSP 程序中可以包含 3 种不同类型的注释:

JSP注释

JSP注释主要有两个作用:为代码作注释以及将某段代码注释掉。主要是给JSP页面提供注释,JSP注释在客户端浏览器无法查看到,注释内容不会被发送至浏览器甚至不会被编译,其语法规则如下:

<%-- JSP标准注释 --%>

例如我们可以这么编写:

<%--表达式展示的JSP注释--%>
声明语句示例:这是第 <%=count()%> 次访问该页面
<br/>
表达式语句示例: <%="我是一个表达式语句"%>
<br/>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>
</body>

Java注释

Java注释指的是java代码,和Java代码的注释格式是一致的,同样的Java注释在客户端浏览器无法查看到,注释内容不会被发送至浏览器甚至不会被编译,语法规则如下:

<%
       //Java单行注释
       /*
           Java多行注释
       */
%>

例如我们可以这么编写:

<%
    String print = "我是脚本语句内容";
    List<String> strList = new ArrayList<>();
    strList.add("脚本语句1");
    strList.add("脚本语句2");
    strList.add("脚本语句3");
//  strList.add("脚本语句4");  //这是一个Java注释,用来注释Java代码
%>

HTML注释

HTML注释主要给JSP页面的Template元素提供注释,HTML网页注释在客户端浏览器可以看到,语法规则如下:

<!-- HTML网页注释 -->

这类在查看源码时可见,其中的脚本语句也是可执行的,示例如下:

<!-- 这是一个html注释,可见<%= new Date().getTime() %> -->

点击查看源码我们可以看到注释:

动作标识

JSP行为标签使用XML语法结构来控制servlet引擎。它能够动态插入一个文件,重用JavaBean组件,引导用户去另一个页面,为Java插件产生相关的HTML等等。行为标签只有一种语法格式,它严格遵守XML标准。其语法格式如下:

<jsp:action_name attribute="value" />

包含很多种可以作用的动作标识:

举例说明如下,执行到跳转动作后会跳转到结果页面:

<jsp:forward page="result.jsp"></jsp:forward>
跳转语句之后,代码不会被执行:<%="我是一个跳转后的表达式语句"%>

总结一下

这篇Blog我们大致了解了JSP的基本概念,为什么要用JSP,以及JSP的生命周期、执行流程和组成元素,但是最最重要的还是JSP的基本语法:指令、注释、脚本、动作,这几个各有用处,指令管理页面的配置和属性,脚本管理JSP元素的代码和逻辑,动作可以简化指令和脚本,用标签的形式去体现,让整个页面Java代码更少看起来更加的配置化,注释则用于对文件进行解释。其中脚本可谓核心,我们希望脚本的内容越少越好,这样JSP的视图而非逻辑作用发挥的就越好,所以后续我们还会学习一些高级语法:JSTL标签和EL表达式,这两配合起来可以大量简化写法

相关文章
|
1天前
|
安全 Java 程序员
Java并发编程:理解并应用ReentrantLock
【4月更文挑战第30天】 在多线程的世界中,高效且安全地管理共享资源是至关重要的。本文深入探讨了Java中的一种强大同步工具——ReentrantLock。我们将从其设计原理出发,通过实例演示其在解决并发问题中的实际应用,以及如何比传统的synchronized关键字提供更灵活的锁定机制。文章还将讨论在使用ReentrantLock时可能遇到的一些挑战和最佳实践,帮助开发者避免常见陷阱,提高程序性能和稳定性。
|
1天前
|
缓存 Java 调度
Java并发编程:深入理解线程池
【4月更文挑战第30天】 在Java并发编程中,线程池是一种重要的工具,它可以帮助我们有效地管理线程,提高系统性能。本文将深入探讨Java线程池的工作原理,如何使用它,以及如何根据实际需求选择合适的线程池策略。
|
1天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】 本文将深入探讨Java中的线程池,解析其原理、使用场景以及如何合理地利用线程池提高程序性能。我们将从线程池的基本概念出发,介绍其内部工作机制,然后通过实例演示如何创建和使用线程池。最后,我们将讨论线程池的优缺点以及在实际应用中需要注意的问题。
|
1天前
|
Java 大数据 数据库连接
java编程的优点
【4月更文挑战第30天】java编程的优点
5 0
|
2天前
|
存储 安全 Java
【亮剑】Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
【4月更文挑战第30天】Java并发编程涉及`ThreadLocal`、`Volatile`、`Synchronized`和`Atomic`四个关键机制。`ThreadLocal`为每个线程提供独立变量副本;`Volatile`确保变量可见性,但不保证原子性;`Synchronized`实现同步锁,保证单线程执行;`Atomic`类利用CAS实现无锁并发控制。理解其原理有助于编写高效线程安全代码。根据业务场景选择合适机制至关重要。
|
2天前
|
安全 Java API
Java 8新特性概述及其对编程实践的影响
【4月更文挑战第30天】本文将详细讨论Java 8的新特性,包括Lambda表达式、Stream API以及Optional类等,并探讨这些新特性如何改变了Java编程的实践。我们将通过实例代码展示这些新特性的用法,并分析其对提高代码可读性和编写效率的影响。
|
2天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
|
2天前
|
存储 安全 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第30天】在Java开发中,并发编程是一个复杂而又关键的领域。它允许多个线程同时执行,从而提高程序性能和资源利用率。然而,并发编程也带来了许多挑战,如数据不一致、死锁和线程安全问题。本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化策略。我们将通过实例分析如何在保证线程安全的同时提高程序性能,为Java开发者提供实用的指导。
|
2天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
2天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第30天】本文将深入探讨Java并发编程的核心概念,包括线程安全、同步机制、锁优化以及性能调优。我们将通过实例分析如何确保多线程环境下的数据一致性,同时介绍一些常见的并发模式和最佳实践,旨在帮助开发者在保证线程安全的同时,提升系统的性能和响应能力。