购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第2章
JSP的基本语法

2.1 了解JSP页面

2.1.1 JSP的概念

JSP(Java Server Pages)是Sun公司开发的一种服务器端动态页面生成技术,主要由HTML和少量的Java代码组成,目的是将表示逻辑从Servlet中分离出来,简化了Servlet生成页面。JSP部署在服务器上,可以响应客户端请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者,因此客户端只要有浏览器就能浏览。它使用JSP标签在HTML网页中插入Java代码。标签通常以“<%”开头,以“%>”结束。

JSP通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。

JSP标签有多种功能,比如访问数据库、记录用户选择信息、访问JavaBeans组件等,还可以在不同的网页中传递控制信息和共享信息。

Java Servlet是JSP的技术基础,大型的Web应用程序的开发需要Java Servlet和JSP配合才能完成。JSP具备Java技术的简单易用特性,其使用具有以下几点特征:

· 跨平台:JSP是基于Java语言的,它能完全兼容Java API,JSP最终文件也会编译成.class文件,所以它跟Java一样是跨平台的。

· 预编译:预编译是指用户在第一次访问JSP页面时,服务器将对JSP页面进行编译,只编译一次。编译好的程序代码会保存起来,用户下一次访问会直接执行编译后的程序。这样不仅减少了服务器的资源消耗,还大大提升了用户访问速度。

· 组件复用:JSP可以利用JavaBean技术编写业务组件、封装业务逻辑或者作为业务模型。这样其他JSP页面可以重复利用该模型,减少重复开发。

· 解耦合:使用JSP开发Java Web可以实现界面的开发与应用程序的开发分离,实现显示与业务逻辑解耦合。界面开发专注界面效果,程序开发专注业务逻辑。最后业务逻辑生成的数据会动态填充到界面进行展示。

2.1.2 第一个JSP页面

打开IDEA,选择第1章创建的HelloWorld项目创建新Module,右击,弹出的菜单如图2.1所示,单击New→Module…菜单项,打开如图2.2所示的窗口,设置模块名为ch02,定位模块目录。最后单击Create按钮创建ch02模块。ch02模块将在IDEA主窗口左侧显示。

图2.1 创建Module

图2.2 设置模块名称并定位模块目录

在IDEA主窗口左侧选择ch02并右击,在弹出的菜单中选择Add Framework Support选项,弹出如图2.3所示的界面,选择Web Application(4.0)选项。接下来的操作基本跟1.6.4节讲解的项目开发和发布步骤一致,读者可以回头查看一下相关细节,并对ch02模块进行配置。

图2.3 添加Web支持

注意有一个重要的配置:选择ch02模块名并右击,在弹出的菜单中选择Open Module Settings选项,打开Project Structure窗口,在Artifacts选项窗口的Output Layout中,单击+号打开菜单,选取Directory Content,打开Select Path窗口,把ch02\web目录加进去,结果如图2.4所示。这个用来配置Web应用打包时,把web目录下的页面文件及其他静态资源文件都打包进去。

图2.4 添加web目录下的内容

在ch02的web目录下,创建JSP文件,并将文件命名为jsp_first.jsp,在其中编写如下代码:

可以看到,新建的JSP页面与HTML几乎没有区别,只不过最上方多了page指令。接下来,打开Tomcat配置窗体(参看图1.26),把URL设置为http://localhost:8080/ch02_war_exploded/jsp_first.jsp。运行ch02模块,把这个应用发布到Tomcat,IDEA自动打开浏览器访问这个应用的URL,可以看到页面上输出了body的文字内容。

2.1.3 JSP的执行原理

JSP的工作模式是请求/响应模式,JSP文件第一次被请求时,JSP容器把文件转换成Servlet,然后Servlet编译成.class文件,最后执行.class文件。过程如图2.5所示。

图2.5 JSP的执行原理

JSP执行的具体步骤如下:

首先是Client发出请求,请求访问JSP文件。

JSP容器将JSP文件转换为Java源代码(Servlet文件)。在转换过程中,如果发现错误,则中断转换并向服务端和客户端返回错误信息,请求结束。

如果文件转换成功,JSP容器会将Java源文件编译成.class文件。

Servlet容器会加载该.class文件并创建Servlet实例,然后执行jspInit()方法。

JSP容器执行jspService()方法处理客户端请求,对于每一个请求,JSP容器都会创建一个线程来处理,对于多个客户端同时请求JSP文件,JSP容器会创建多个线程,使得每一次请求都对应一个线程。

由于首次访问需要转换和编译,因此可能会产生轻微的延迟。另外,当遇到系统资源不足等情况时,Servlet实例可能会被移除。

处理完请求后,响应对象由JSP容器接收,并将HTML格式的响应信息发送回客户端。

为了更好地理解JSP的执行原理,接下来分析一下JSP生成的Servlet代码。

以第一个JSP页面为例,启动Tomcat过程中有参数:CATALINA_BASE,进入后面的目录,目录路径如图2.6所示。

图2.6 Tomcat部署路径

进入目录“CATALINA_BASE/work/Catalina/localhost/ch02_war_exploded/org/apache/jsp”,其目录结构如图2.7所示,内有jsp_first.jsp转换和编译的结果文件。

图2.7 JSP转换和编译后的文件

可以看出,jsp_first.jsp被转换和编译成jsp_005ffirst_jsp.java和jsp_005ffirst_jsp.class,其中主要看转化成Servlet的代码,如图2.8所示。

图2.8 JSP转换和编译后的文件内容

怎么知道这个类就是Servlet呢?这个类继承了HttpJspBase,接着继续跟进HttpJspBase,可以看到这个类就是Servlet。由此可见,jsp_005ffirst_jsp.java就是Servlet,局部截图如图2.9所示。

图2.9 HttpJspBase源码部分内容

通过跟踪源码,不难发现JSP转换成Servlet,由Servlet容器接管执行页面显示代码,最终以HTML的形式返回给客户端。

2.2 指令标识

JSP指令用来设置整个JSP页面相关的属性,如网页编码和脚本语言。JSP指令只负责告诉JSP引擎(如Tomcat)如何处理JSP页面的其余部分代码。引擎会根据指令信息来编译JSP,生成Java文件。在生成的Java文件中,指令就不存在了。

通常在代码中会把JSP指令放在JSP文件最上方,但这不是必需的。

指令通常以<%@标记开始,以%>标记结束,它的具体语法如下:

     <%@ 指令名称 属性1="属性值1" 属性2="属性值2" … 属性n="属性值n" %>

指令可以有很多个属性,它们以键-值对(Key-Value Pair)的形式出现。

2.2.1 page指令

JSP的page指令是页面指令,定义在整个JSP页面范围有效的属性和相关的功能。page指令可以指定使用的脚本语言、导入需要的类、输出内容的类型、处理异常的错误页面以及页面输出缓存的大小,还可以一次性设置多个属性。

一个JSP页面可以包含多个page指令。

其语法格式如下:

     <%@ page attribute="value" %>

表2.1所示是Page指令的属性。

表2.1 Page指令的属性

可以在一个页面上使用多个page指令,其中的属性只能使用一次(import属性除外)。

示例代码如下:

注意: 如果out.print报错,需要把Tomcat下面的jsp-api.jar、servlet-api.jar引入Modules下面的Dependencies中。

2.2.2 include指令

JSP的include指令用于通知JSP引擎在编译当前JSP页面时,将其他文件中的内容引入当前JSP页面转换成的Servlet源文件中,这种源文件级别引入的方式称为静态引入。当前JSP页面与静态引入的文件紧密结合为一个Servlet。这些文件可以是JSP页面、HTML页面、文本文件或一段Java代码。

其语法格式如下:

     <%@ include file="relativeURL|absoluteURL" %>

file属性指定被包含的文件,不支持任何表达式,例如下面是错误的用法:

     <% String f="my.html"; %>
     <%@ include file="<%=f %>" %>

不可以在file所指定的文件后接任何参数,如下用法也是错误的:

     <%@ include file="my.jsp?id=100" %>

如果file属性值以“/”开头,将在当前应用程序的根目录下查找文件;如果是以文件名或文件夹名开头的,则在当前页面所在的目录下查找文件。

提示: 使用include指令包含的文件将原封不动地插入JSP文件中,因此在所包含的文件中不能使用标记,否则会因为与原有的JSP文件有相同标记而产生错误。另外,因为源文件和被包含的文件可以相互访问彼此定义的变量和方法,所以要避免变量和方法的命名冲突。

示例如下:

jsp_include_01.jsp:

jsp_included.jsp:

2.2.3 taglib指令

taglib指令可以引入一个自定义标签集合的标签库,包括库路径、自定义标签。

其语法格式如下:

     <%@ taglib uri="uri" prefix="prefixOfTag" %>

uri属性确定标签库的位置,prefix属性指定标签库的前缀。

将%TOMCAT_HOME%/webapps/examples/WEB-INF/lib复制到项目web/WEB-INF/lib目录下。

示例代码如下:

此处笔者用了JSTL库,详见第9章 JSTL标签。

2.3 脚本标识

JSP脚本标识包括3部分:JSP表达式(Expression)、声明标识(Declaration)和脚本程序(Scriptlet)。

2.3.1 JSP表达式

JSP表达式中包含的脚本语言先被转化成String,然后插入表达式出现的地方。表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式。

JSP表达式的语法格式如下:

     <%= 表达式 %>

注意: “<%”与“=”之间不可以有空格,“=”与其后面的表达式之间可以有空格。

示例代码如下:

2.3.2 声明标识

一个声明语句可以声明一个或多个变量或方法,供后面的Java代码使用。在JSP文件中,必须先声明这些变量和方法,然后才能使用它们。服务器执行JSP页面时,会将JSP页面转换为Servlet类,在该类中会把使用JSP声明标识定义的变量和方法转换为类的成员和方法。

声明标识语法如下:

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

注意: “<%”与“!”之间不可以有空格,“<%!”与“%>”可以不在同一行。

示例代码如下:

2.3.3 脚本程序/代码片段

脚本程序可以包含任意的Java语句、变量、方法或表达式。

语法如下:

     <% Java代码或是脚本代码 %>

注意: 代码片段就是在JSP页面中嵌入Java代码或脚本代码。代码片段将在页面请求的处理期间被执行。

(1)通过Java代码可以定义变量或流程控制语句等。

(2)通过脚本代码可以应用JSP的内置对象在页面输出内容、处理请求和响应、访问Session会话等。

提示: 代码片段与声明标识的区别是,通过声明标识创建的变量和方法在当前JSP页面中有效,它的生命周期是从创建开始到服务器关闭结束;代码片段创建的变量或方法也是在当前JSP页面中有效,但它的生命周期是页面关闭后就会被销毁。

示例代码如下:

2.4 JSP注释

注释就是对程序代码的解释和说明。注释的地位跟代码同等重要。它能提高代码的可读性,了解代码的功能机制,从而提高团队合作开发的效率。对一些复杂的大型系统,没有注释会导致异常追踪非常困难,它是代码开发规范必备的要求,每个初学者必须养成写注释的习惯。

JSP中的注释有4种:

· HTML中的注释。

· 带有JSP表达式的注释。

· 隐藏注释。

· 脚本程序中的注释。

2.4.1 HTML中的注释

JSP文件中包含大量的HTML标记,所以HTML中的注释同样可以在JSP文件中使用。HTML注释语法如下:

     <!-- 注释内容 -->

HTML中的注释内容不会在客户端浏览器中显示,但可以通过HTML源代码看到这些注释内容。

示例:

     <!-- 1. HTML 注释示例 -->
     <h1>HTML注释示例</h1>

通过查看网页源码,依然能看到注释信息,如图2.10所示。

图2.10 HTML注释示例

2.4.2 带有JSP表达式的注释

通过前面的学习,我们知道JSP中常常嵌入表达式,而JSP注释中同样可以嵌入JSP表达式,其语法格式如下:

     <!-- HTML注释内容<%=JSP 表达式%>-->

JSP页面被请求后,服务器能够自动识别并执行注释中的JSP表达式,对于注释中的其他内容则不做任何操作。当服务器将执行结果返回给客户端浏览器后,注释的内容也不会在浏览器中显示。

示例如下:

查看网页源代码时,只能看到JSP表达式执行后的结果,看不到原来的JSP表达式,如图2.11所示。

图2.11 注释中带有JSP表达式示例

2.4.3 隐藏注释

无论是HTML注释还是带有JSP表达式的注释,虽然都不能在客户端浏览器中显示,但是通过查看网页源代码还是能看到注释的内容,因此严格来说,这两种注释其实并不安全。而下面即将介绍的隐藏注释可以解决这个问题。

隐藏注释的内容不会显示在客户端的任何位置(包括HTML源代码),安全性较高,其注释格式如下:

     <%--注释内容--%>

示例如下:

查看网页源代码,如图2.12所示。可以看到,代码里面的注释也不显示。

图2.12 隐藏注释示例

2.4.4 脚本程序中的注释

脚本注释略微复杂,一般如果脚本是Java语言,其注释语法跟Java是一样的。

脚本程序中的注释有3种:单行注释、多行注释和文档注释。

单行注释语法如下:

     // 注释内容

多行注释语法如下:

     /*
      *注释内容
      */

文档注释语法如下:

     /*
     文档的注释内容
     */

示例如下:

其效果图如图2.13所示。

图2.13 脚本注释示例

程序在编译过程中,脚本语言已经自动优化,因此脚本内的Java代码注释在网页源代码中不显示。

2.5 动作标识

前面2.2节学习的JSP三种指令标识称为编译指令,而本节将要学习7种常用的动作标识。JSP动作与JSP指令的不同之处是,JSP页面被执行时首先进入翻译阶段,程序会先查找页面中的指令标识,并将它们转换成Servlet,这些指令标识会先被执行,从而设置整个JSP页面,所以JSP指令是在页面转换时期被编译执行的,且编译一次,而JSP动作是在客户端请求时按照在页面中出现的顺序被执行的,它们只有被执行的时候才会去实现自己所具有的功能,且基本上是客户每请求一次,动作标识就会被执行一次。

JSP动作标识的通用格式如下:

     <jsp:动作名 属生1="属性值1"...属性n="属性值n" />

或者

     <jsp:动作名; 属性1="属性值1"...属性n="属性值n">相关内容</jsp:动作名>

动作标识基本上都是预定义的函数,常用的动作标识如表2.2所示。

表2.2 常用的动作标识

下面重点介绍常用的3个动作。

2.5.1 包含文件标识<jsp:include>

<jsp:include>动作用来包含静态和动态的文件,把指定文件插入正在生成的页面。语法格式如下:

     <jsp:include page="相对URL地址" flush="true" />

前面介绍include指令的时候,也是用来包含文件的。它们引入文件的时机不一样,include指令是在JSP文件被转换成Servlet的时候引入文件,而<jsp:include>动作是在页面被请求的时候插入文件。

<jsp:include>动作在JSP页面执行时引入的方式称为动态引入,主页面程序与被包含文件是彼此独立、互不影响的。

<jsp:include>动作对动态文件和静态文件的处理方式是不同的。

如果包含的是静态文件,被包含文件的内容将直接嵌入JSP文件中,当静态文件改变时,必须将JSP文件重新保存(重新转译),然后才能访问变化了的文件。

如果包含的是动态文件,则由Web服务器负责执行,把执行后的结果传回包含它的JSP页面中,若动态文件被修改,则重新运行JSP文件时会同步发生变化。

示例代码如下:

     <h2>include 动作示例:</h2>
     <jsp:include page="jsp_included.jsp" flush="true" />

前面学习的include指令和<jsp:include>动作都是用来包含文件的,其作用基本类似。下面介绍一下它们的差异。

1.属性不同

include指令通过文件属性来指定被包含的页面,该属性不支持任何表达式。<jsp:include>动作是通过页面属性来指定被包含页面的,该属性支持JSP表达式。

2.处理方式不同

使用include指令,被包含文件的内容会原封不动地插入包含页中,JSP编译器对合成的新文件进行编译,最终编译后只生成一个文件。使用<jsp:include>动作时,只有当该标记被执行时,程序才会将请求转发到(注意是转发而不是请求重定向)被包含的页面,再将其执行结果输出到浏览器,然后重新返回包含页来继续执行后面的代码。因为服务器执行的是两个文件,所以JSP编译器将对这两个文件分别编译。

3.包含方式不同

include指令的包含过程为静态包含,在使用include指令包含文件时,服务器最终执行的是将两个文件合成后由JSP编译器编译成一个Class文件,所以被包含文件的内容是固定不变的;如果改变了被包含的文件,主文件的代码就发生了改变,服务器会重新编译主文件。

<jsp:include>动作的包含过程为动态包含,通常用来包含那些经常需要改动的文件。因为服务器执行的是两个文件,被包含文件的改动不会影响主文件,所以服务器不会对主文件重新编译,而只需重新编译被包含的文件即可。编译器对被包含文件的编译是在执行时才进行的,只有当<jsp:include>动作被执行时,使用该标记包含的目标文件才会被编译,否则被包含的文件不会被编译。

4.对被包含文件的约定不同

使用include指令包含文件时,因为JSP编译器是对主文件和被包含文件进行合成后再翻译,所以对被包含文件有约定。例如,被包含的文件中不能使用“、”标记,被包含文件要避免变量和方法在命名上与主文件冲突的问题。

注意: include指令和动作的最终目的是简化复杂页面的代码,提高代码的可读性和可维护性,体现了编程中模块化的思想。

2.5.2 请求转发标识<jsp:forward>

<jsp:forward>动作把请求转到其他的页面。该动作只有一个属性page。语法格式如下:

     <jsp:forward page="URL地址" />

读者一定会有这样的体验,在一些需要输入用户密码的网站,登录之后都会有跳转到欢迎页面或者首页,<jsp:forward>动作标记就可以实现页面的跳转,将请求转到另一个JSP、HTML或相关的资源文件中。当<jsp:forward>动作被执行后,当前的页面将不再被执行,而是去执行指定的页面,用户此时在地址栏中看到的仍然是当前网页的地址,而页面内容却已经是转向的目标页面的内容了。

示例代码如下:

jsp_action_forword.jsp:

     <jsp:forward page="jsp_action_forword_b.jsp"></jsp:forward >

jsp_action_forword_b.jsp:

2.5.3 传递参数标识<jsp:param>

<jsp:param>动作以键-值对的形式为其他标签提供附加信息,通俗地说就是页面传递参数,它常和<jsp:include>、<jsp:forward>等一起使用,语法如下:

     <jsp:param name="paramName" value="paramValue" />

其中,name属性用于指定参数名,value属性用于指定参数值。

示例代码如下:

jsp_action_param.jsp:

此处,笔者在页面跳转处理上与上一小节共用了一个页面,在之前的程序上添加了部分逻辑处理,代码如下(jsp_action_forword_b.jsp):

页面程序的执行结果如图2.14所示。

图2.14 程序的参数传递执行结果

在参数传递过程中,从原始页面输入的用户密码,经过页面跳转,输入的用户密码参数传递到新页面并展示出来。在Web开发中,页面传参是非常通用的技术。

2.6 实践与练习

1.加强IDEA软件的使用,用好工具可以极大地提升效率。

2.简述JSP的执行原理,并独立画出JSP的执行原理图。

3.掌握JSP指令的作用,了解三大指令的使用场景,学会使用指令。

4.掌握脚本和注释的使用。

5.学习并掌握动作标识,并自主学习后面几种动作的使用语法。

6.尝试使用本章学习的指令、表达式、动作完成简单的登录程序。 ZuRb5NARs499upek83wDhi+4XIK0oYHEnsLQZKvND77mWroMktxojHIgE2hurOLu

点击中间区域
呼出菜单
上一章
目录
下一章
×