其他更多 java 基础文章:
java 基础学习(目录)
本章内容较多, 全部认真看完可能需要一小时以上, 建议边看边做笔记, 否则容易混乱
一, JSP 概述
1.1,JSP 结构
网络服务器需要一个 JSP 引擎, 也就是一个容器来处理 JSP 页面. 容器负责截获对 JSP 页面的请求. 内嵌 JSP 容器的 Apache 支持 JSP 开发.
JSP 容器与 web 服务器协同合作, 为 JSP 的正常运行提供必要的运行环境和其他服务, 并且能够正确识别专属于 JSP 网页的特殊元素.
下图显示了 JSP 容器和 JSP 文件在 Web 应用中所处的位置.
1.2,JSP 运行流程
以下步骤表明了 Web 服务器是如何使用 JSP 来创建网页的:
就像其他普通的网页一样, 您的浏览器发送一个 HTTP 请求给服务器. Web 服务器识别出这是一个对 JSP 网页的请求, 并且将该请求传递给 JSP 引擎. 通过使用 URL 或者. jsp 文件来完成.
JSP 引擎从磁盘中载入 JSP 文件, 然后将它们转化为 servlet. 这种转化只是简单地将所有模板文本改用 println()语句, 并且将所有的 JSP 元素转化成 Java 代码.
JSP 引擎将 servlet 编译成可执行类, 并且将原始请求传递给 servlet 引擎.
Web 服务器的某组件将会调用 servlet 引擎, 然后载入并执行 servlet 类. 在执行过程中, servlet 产生 html 格式的输出并将其内嵌于 HTTP response 中上交给 Web 服务器. Web 服务器以静态 HTML 网页的形式将 HTTP response 返回到您的浏览器中. 最终, Web 浏览器处理 HTTP response 中动态产生的 HTML 网页, 就好像在处理静态网页一样.
以上提及到的步骤可以用下图来表示:
一般情况下, JSP 引擎会检查 JSP 文件对应的 servlet 是否已经存在, 并且检查 JSP 文件的修改日期是否早于 servlet. 如果 JSP 文件的修改日期早于对应的 servlet, 那么容器就可以确定 JSP 文件没有被修改过并且 servlet 有效. 这使得整个流程与其他脚本语言 (比如 PHP) 相比要高效快捷一些.
Jsp 生成 java 源码, 默认第一次生成, 之后直接执行, 除非内容修改, 具体点说, 由于 JSP 只会在客户端第一次请求的时候被编译, 因此第一次请求 JSP 时会感觉比较慢, 而之后的请求因为不会编译 JSP, 所以速度就快多了.
如果将 Tomcat 保存的 JSP 编译后的 class 文件删除, Tomcat 也会重新编译 JSP. 在开发 Web 程序的时候经常需要修改 JSP, Tomcat 能够自动检测到 JSP 程序的改动, 如果检测到 JSP 源代码发生了改动, Tomcat 会在下次客户端请求 JSP 时重新编译 JSP, 而不需要重启 Tomcat, 这种自动检测功能默认是开启的, 检测改动会消耗少量的时间, 在部署 Web 应用程序的时候可以在 Web.xml 中将它关掉.
这也就是为什么我们能够在 jsp 页面直接修改内容, 而不用重新启动服务器的原因.
总的来说, JSP 网页就是用另一种方式来编写 servlet 而不用成为 Java 编程高手. 除了解释阶段外, JSP 网页几乎可以被当成一个普通的 servlet 来对待.
1.3,JSP 生命周期
理解 JSP 底层功能的关键就是去理解它们所遵守的生命周期. JSP 生命周期就是从创建到销毁的整个过程, 类似于 servlet 生命周期, 区别在于 JSP 生命周期还包括将 JSP 文件编译成 servlet.
以下是 JSP 生命周期中所走过的几个阶段:
编译阶段:
servlet 容器编译 servlet 源文件, 生成 servlet 类
初始化阶段:
加载与 JSP 对应的 servlet 类, 创建其实例, 并调用它的初始化方法
执行阶段:
调用与 JSP 对应的 servlet 实例的服务方法
销毁阶段:
调用与 JSP 对应的 servlet 实例的销毁方法, 然后销毁 servlet 实例
很明显, JSP 生命周期的四个主要阶段和 servlet 生命周期非常相似, 下面给出图示:
二, JSP 语法
2.1,jsp 脚本
使用<% 编写 java 代码 %>, 中间 java 代码必须遵循 Java 语法
来看看, jsp 变为 servlet 时的代码是如何编写的
2. 使用 <%=xxx %> 来输出结果
使用 <%=result %> 来输出结果, servlet 中就会将其转换为 out.print(result)进行输出. 输出各种类型数据: int,double,boolean,String,Object 等.
注释
<%-- --%>:jsp 注释 <!-- -->: 这个注释, 会发送到浏览器端的源码中显示 注释分别在 servlet 中如何显示:
在 servlet 中
总结: JSP 注释不会在 servlet 文件中显示, 而 java 注释则会, 但其所有的注释到了浏览器端, 都不会出现在源码中, 只有这个注释会到浏览器的网页源码中去.
JSP 中申明方法与属性(全局变量)使用<%! 方法, 属性 %>
2.2,3 个指令
JSP 指令 (directive) 是为 JSP 引擎而设计的, 它们并不直接产生任何可见输出, 而只是告诉引擎如何处理 JSP 页面中的其余部分. 指令用来申明 JSP 页面的一些属性, 比如编码方式, 文档类型. 我们在 servlet 中也会申明我们使用的编码方式和响应的文档类型的, 而 JSP 就是用指令来申明.
JSP 指令格式:<%@ directive {attribute=value}* %>(<%@ 指令名称 属性 1="属性值 1" 属性 2="属性值 2"...%>)
分析:
directive: 指令名称, 例如 page 指令
attribute=value: 紧跟指令名称后面的就是各种属性, 以键值对的形式书写
*: 代表后面能跟 0 个或多个属性.
2.2.1,page 指令(用来声明 JSP 页面的属性等)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>page 指令, 后面跟着三个属性, 分别是 language,contentType,pageEncoding.
这只是其中的几个属性, 并没有写全, page 指令允许的属性如下表所示:
属性名称 | 取值范围 | 描述 |
---|---|---|
language | java | 解释该 JSP 文件时采用的语言,一般为 java 语言,默认为 java |
extends | 任何类的全名 | 编译该 JSP 文件时继承哪个类,JSP 为 Servlet,因此当指明继承普通类时需要实现 Servlet 的 init、destroy 等方法 |
import | 任何包名、类名 | 引入该 JSP 中用到的类、包等,import 是唯一可以声明多次的 page 指令属性,一个 import 可以引用 uogelei,中间用英文逗号隔开,如 & lt;%@ page import="java.util.List,java.util.ArrayList"%> |
session | true、false | 该 JSP 内是否内置 Session 对象,如果为 true,则内置 Session 对象,可直接使用,否则反之,默认为 true |
autoFlush | true,false | 是否运行缓存,如果为 true,则使用 out.println() 等方法输出的字符串并不是立刻到达客户端服务器的,而是暂时存到缓存里,缓存满了或者程序执行完毕或者执行 out.flush() 操作时才到客户端,默认为 true。 |
buffer | none 或者数字 KB | 指定缓存大小,当 autoFlush 设为 true 时有效,例如 & lt;%@ page buffer=10kb%> |
isThreadSafe | true,false | 是否线程安全,如果为 true,则运行多个线程同时运行该 jsp 程序,否则只运行一个线程,其余线程等待,默认为 false |
isErrorPage | true,false | 指定该页面是否为错误显示页面,如果为 true,则该 JSP 内置有一个 Exception 对象 exception,可直接使用,否则没有,默认为 false |
errorPage | 某个 JSP 页面的相对路径 | 指明一个错误页面,如果该 JSP 程序抛出一个未捕捉的异常,则转到 errorPage 指定的页面,errorPage 指定的页面通常 isErrorPage 属性为 true,且内置的 exception 对象为未捕捉的异常 |
contentType | 有效的文档类型 | 客户端浏览器根据该属性判断文档类型,例如 HTML 格式为 text/html、纯文本格式为 text/plain、JPG 图像为 image/jpeg、GIF 图像为 image/gif、WORD 文档为 application/msword,该属性常跟着 charset 设置编码一起,作用是通知服务器和浏览器都使用同一个码表 |
info | 任意字符串 | 指明 JSP 的信息,该信息可以通过 Servlet.getServletInfo() 方法获取到 |
trimDirective Whitespaces | true、false | 是否去掉指令前后的空白字符,默认为 false |
pageEncoding | UTF-8,ISO-8859-1 等 | 指定一张码表来对该 JSP 页面进行编码 |
2.2.2,include 指令
比较简单, 只有一种形式 <%@ include file="relativeURL"%>relativeURL: 本应用程序内另一个 JSP 文件或者 HTML 文件的路径, 例如, 网址内所有页面均有一个统一风格的导航栏和页脚版权, 那么就可以使用该指令将其包含进来.
特点:
include 指令会将包含页面的源代码添加到使用 include 指令的页面中来, 然后编译成 class 文件, 而等下会讲到的一个 JSP 行为,<jsp:include page="relativeURL">作用跟 include 指令一样, 但是不同的是, include 行为是运行时单独执行包含页面, 然后把执行的结果包含到本页面来, 属于先运行后包含.
注意:
静态包含: 把其它资源包含到当前页面中 .
<%@ include file="/include/header.jsp" %>
动态包含:
<jsp:include page="/include/header.jsp"></jsp:include>
两者的区别: 翻译的时间段不同
前者: 在翻译时就把两个文件合并
后者: 不会合并文件, 当代码执行到 include 时, 才包含另一个文件的内容.
原则: 能用静的就不用动的.
2.2.3,taglib 指令
JSP 支持标签技术, 后面会讲到标签的用法, jstl 标签库的使用等 作用: 用来指明 JSP 页面内使用的 JSP 标签库, taglib 指令有两个属性, uri 为类库的地址, prefix 为标签的前缀 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
2.3,6 个动作
前面讲了 JSP 语法, 介绍了 JSP 页面中的内容有哪些, 分别有什么作用, 就两个东西, 模块数据和元素. 其中元素有包括脚本, 指令, 标签, 脚本就是 JSP 中嵌入 java 代码, 指令作用就是申明页面的属性, 那标签是干嘛的, 标签分为 JSP 自带内置的标签, 和通过 taglib 指令来使用 JSP 标签库, 或者自定义标签. 现在我们先来讲一些 JSP 内置的标签. JSP 内置的标签就被称为 JSP 行为(JSP Actions). 只要书写很少的标记代码就能使用 JSP 提供的丰富功能, JSP 行为其实是对常用的 JSP 功能的抽象与封装, 可以取代 jsp 脚本, 让 JSP 中就少一些嵌入 java 代码的地方.
简单的说就是使用标签的形式来表示一段 java 代码, 格式:
<jsp:elements {attribute="value"}*/>
分析:
jsp: 标签的前缀, 说明是 jsp 内置的标签 ,
elements: 行为的名称,
attribute=value: 使用键值对来编写属性
*: 能指定 0 个或多个属性对
2.3.1,<jsp:include />行为(动态包含)
<jsp:include page="/include/header.jsp"></jsp:include>
include 行为用于运行时包含某个文件, 如果被包含的文件为 JSP 程序, 则先会执行 JSP 程序, 然后在把执行的结果包含进来.
作用是跟 include 指令一样的, 唯一的区别就在于, include 指令是将被包含的文件的源码加入到了本 JSP 程序中, 然后在进行编译, 属于静态包含, 而 include 行为只是将被包含的文件的运行结果包含进自己. 属于动态包含.
2.3.2,Java bean 行为
是一组与 Java Bean 相关的行为, 包括 useBean 行为, setProperty 行为, getProperty 行为等. Java Bean 就是普通的 Java 类, 也被称为 POJO, 只有私有的属性与对应的 getter 方法和 setter 方法, 注意其中当私有的属性为 boolean 类型时, 习惯上一般把 getter 方法写成 isXxx(); 而不是 getXxx();
1)userBean 行为
<jsp:useBean id="beanObject" class="className" scope="Value"> 作用: 在 jsp 中定义一个 java bean 对象.
分析:
id: 指明 Java Bean 对象的名称, JSP 中可以使用该名称引用该 Java Bean 对象, 相当于给 new 出来的对象取一个变量名
class:Java Bean 类的全名
scope: 该 java bean 对象的作用范围, 可以写的就四个, 也就是 JSP 的四大作用域, page,request,session,application
---page: 只能在当前 JSP 页面使用, 如果不在 JSP 页面, 那么就会失效
---request: 这个前面学过, A 页面请求转发到 B 页面, 那么使用的是同一个 request, 那么 A,B 页面都算是 request 的作用域, 也就是通过请求转发的页面都是其作用域
----session: 该作用域在一个 Web 项目下任何位置应该读访问的到, 只要 cookie 不关闭, 并且 cookie 设置的访问路径为 "/",
---application: 其实就是 Servlet 中的 servletContext, 服务器下的所有项目都能访问到.
2)setProperty 行为 <jsp:setProperty name="beanName" property="propertyName" value="">
分析:
对 Java Bean 对象进行属性的设置
name:java bean 对象的名称, 也就是在 useBean 行为中的 id
property: 对象中的属性名,
value: 要对其属性进行赋值的值
3)getProperty 行为 <jsp:getProperty name="beanName" property="propertyName" />
分析:
获取 JavaBean 对象的某个属性值
name:java bean 对象的名称, 也就是在 useBean 行为中的 id
property: 对象的属性名
2.3,<jsp:forward />行为
实现请求转发功能, Servlet 中通过 request.getRequestDispatcher("someServlet").forward(request,response); 而在 JSP 中也能够实现相同的功能, 只不过用的是 < jsp:forward />行为, 实际上 forward 行为就是对其进行了封装.
格式:
- <jsp:forward page="someServlet">
- <jsp:param name="param1" value="value1" />
- <jsp:param name="param2" value="value2" />
- </jsp:forward>
分析: page: 需要跳转到的页面或者 servlet,jsp:param / 参数行为, 带一些参数过去, name,value 是以键值对的形式带过去的
2.4,9 个内置对象
我们知道 JSP 中的内容就只有两种, 模版数据和元素, 元素就包括了指令, 脚本, 标签(行为), 脚本会慢慢被标签全部代替, 也就是说 JSP 中基本上不会嵌入 Java 代码, 但是我们也知道 JSP 会转换为 servlet, 在 Servlet 中, 输出数据时, 都需要通过 response.getWrite(); 但是在 JSP 中, 直接使用 out 对象进行输出, 为什么呢? 这就是因为 out 为 JSP 的一个隐藏对象, JSP 中内置了 9 个隐藏对象, 使得 JSP 比 Servlet 使用起来更简单, 更方便.
2.4.1, 九大内置对象概述
分析:
request: 请求对象, 类型: httpServletRequest
response: 响应对象, 类型: httpServletResponse
session: 表示一次会话, 在服务器端记录用户状信息的技术
application: 标识 Web 应用上下文, 类型: ServletContext, 详情就看 Servlet 中的 ServletContext 的使用
exception: 表示发生异常对象, 类型 Throwable, 在上面我们介绍 page 指令中的一个 errorPage 属性时就有说到他
page:page 对象代表当前 JSP 页面, 是当前 JSP 编译后的 Servlet 类的对象. 相当于 this.
config: 标识 Servlet 配置, 类型: ServletConfig,API 跟 Servlet 中的 ServletConfig 对象是一样的, 能获取该 servlet 的一些配置信息, 能够获取 ServletContext
out: 输出响应体 类型: JspWriter
pageContext: 表示 jsp 页面上下文(jsp 管理者) 类型: PageContext
注意: 标记了红色的对象就是 JSP 独有的, 其他的都是 Servlet 中的老东西.
在这个由 jsp 转换为 servlet 的文件中, 只能看到 8 个内置对象, 少了 exception 对象, 因为我们在将 page 指令时, 说过一个 isErrorPage 属性, 默认是 false, 被关闭了, 所以其中并没有 exception 对象.
2.4.2,pageContext(重要)
这个功能就比较强大了, 基本上什么他都有, 因为是它是 JSP 页面的管理者(上下文), 所以 JSP 中的内置对象呀, 它统统能够获得, 下面介绍它的 API:
1)获得其它八大内置对象 getXxx()
在普通类中可以通过 PageContext 获取其他 JSP 隐式对象. 自定义标签时就使用.
- pageContext.getOut();// 获得 out 对象
- pageContext.getApplication();// 获得 application 对象
等等....
2)对作用域的属性进行操作(四大作用域)
对默认作用域的属性进行操作. page
- Object getAttribute(String name);// 获得 page 作用域数据
- void setAttribute(String name,Object o);// 给 page 作用域设置内容
- void removeAttribute(String name);// 给 page 作用域移除内容
3)对指定作用域的属性进行操作
- Object getAttribute(String name,int Scope);// 获得 指定作用域中的数据
- void setAttribute(String name,Object o,int Scope);// 给指定作用域设置内容
- void removeAttribute(String name,int Scope); // 移除指定作用域的内容(page/request/session/application)
4)提供作用域常量
- PageContext.PAGE_SCOPEpage
- PageContext.REQUEST_SCOPErequest
- PageContext.SESSION_SCOPEresponse
- PageContext.APPLICATION_SCOPEapplication
5)一次获得指定名称内容
pageContext 中最厉害的方法是:
findAttribute(String name); // 自动从 page request session application 依次查找, 找到了就取值, 结束查找.
6)提供了的简易方法
- pageContext.forward("2.jsp");
- pageContext.include("2.jsp");
2.4.3,out 对象
类型: JspWriter
jsp 输出底层使用 response.getWriter(); 什么意思呢? 这里就要讲解一下 JSP 缓存和 Servlet 缓存了, 输出的过程是这样的
JSP 页面转换为 Servlet 后, 使用的 out 对象是 JspWriter 类型的, 所以是会先将要发送的数据存入 JSP 输出缓存中, 然后, 等 JSP 输出缓存满了在自动刷新到 servlet 输出缓存等 serlvet 输出缓存满了, 或者程序结束了, 就会将其输出到浏览器上. 除非手动 out.flush().
验证 servlet 输出缓存和 JSP 输出缓存和我们上面所说的是正确:
结果:
分析: 如果按没有 jsp 缓存和 servlet 缓存的话, 输出的结果应该是 aaaabbbbcccc, 但是输出的却是 bbbbaaaacccc, 为什么呢? 按照我们上面所说的原理进行分析, out 对象是先将其输出到 JSP 缓存中, 所以 aaaa 加入了 jsp 缓存, 而 response.getWriter().print("bbbb")是直接将 bbbb 输出到 servlet 缓存中, 然后又使用 out 对象将 cccc 输出到 jsp 缓存, 到程序结束, servlet 缓存中有 bbbb, 然后 jsp 会将缓存中的内容就刷新到 servlet 缓存中, serlvet 就是 bbbbaaaacccc 了, 然后到浏览器也就得到我们的输出结果了. 如果在 12 行将注释去掉, 那么输出的结果又会是什么呢? 答案就是 aaaabbbbcccc, 过程自行分析.
2.4.4,config 对象
类型: ServletConfig
能够获取 servlet 的初始化参数, 获取 servletContext 对象, 获取 servletName.
2.4.5,exception 异常对象
包含了异常的信息
使用它, 必须结合 page 指令中的 isErrorPage 属性和 errorPage 属性.
如下例子, exception.jsp 抛异常的一个 NullPointException, 并且跳转到 error.jsp 错误显示页面, 其中 errorPage 属性的意思是如果发生未捕捉到的异常, 将会跳转到 error.jsp 页面
error.jspisErrorPage 属性说明该页面是一个错误显示页面, 则可以使用 exception 对象
访问: 访问 http://localhost:8080/Web_Jsp/exception.jsp
2.4.6, 总结: 九大内置对象和 servlet 中对象的关系
page 就是 jsp 转换为 servlet 对象本身, 也就是 this
config -- Servlet 中的 servletConfig
application -- Servlet 中的 ServletContext
request-- Servlet 中的 request
response-- Servlet 中的 response
session-- Servlet 中的 session out--JspWriter
exception-- 异常对象
pageContext-- 表示 jsp 页面上下文(jsp 管理者) 类型: PageContext
其中 pageContext 是最厉害的, 因为它可以得到其他 8 个内置对象
2.5,4 大作用域
这四大作用域, 其实就是其九大内置对象中的四个, 为什么说他们也是 JSP 的四大作用域呢? 因为这四个对象都能存储数据, 比如 request.setAttribute()注意和 request.setParameter()区分开来, 一个是存储在域中的, 一个是请求参数, session.setAttribute(),application 其实就是 SerlvetContext, 自然也有 setAttribute()方法.
而 page 作用域的操作就需要依靠 pageContext 对象来进行了. 在上面我们也有提到 JSP 的四大作用域.
1)page 作用域 代表变量只能在当前页面上生效
2)request 作用域 代表变量能在一次请求中生效, 一次请求可能包含一个页面, 也可能包含多个页面, 比如页面 A 请求转发到页面 B.
3)session 作用域 代表变量能在一次会话中生效, 基本上就是能在 Web 项目下都有效, session 的使用也跟 cookie 有很大的关系. 一般来说, 只要浏览器不关闭, cookie 就会一直生效, cookie 生效, session 的使用就不会受到影响.
4)application 作用域 代表变量能一个应用下(多个会话), 在服务器下的多个项目之间都能够使用. 比如 baidu,wenku 等共享帐号.
来源: https://juejin.im/post/5c160422f265da614c4cb17d