目录
可能是JSP
知识总结的最全的博客之一了,你想要寻找的知识,这里都有 ;
作者:淮左白衣
来源:笔者当时学web的笔记
时间:-2018年5月4日21:25:09
jsp
JSP
全称是 java servlet pages
,它和 servlet
技术一样,都是Sun公司定义的一种用于开发动态web页面的技术
为什么jsp
也是一种动态web资源的开发技术呢?
写jsp
,虽然就像是在写HTML
,但是jsp
允许在页面中编写java
代码,并且允许开发人员在页面中获取request
、response
等web
开发常用对象,实现与浏览器的交互,所以jsp也是一种动态web资源的开发技术;
例子:输出当前时间
Date date = new Date();
//这个out对象是可以直接使用的,它是JspWriter的实例
out.write(date.toLocaleString());
Jsp
调用和运行原理(简略版)浏览器访问 jsp
页面时,Web
服务器是如何调用并执行一个jsp页面的?
首先知道,我们访问服务器的资源时,无论访问的是什么?比如访问的是HTML、jsp,其实我们访问的都是一个servlet,而不是真正的jsp、html ,因此,我们访问 jsp
其实就是去访问 servlet
;
Jsp
在被访问的时候,服务器会将它 翻译为 servlet
,转换后的servlet
,被保存在服务器目录下的work/项目名/apache/…
提问:
web 服务器在执行jsp页面时,是如何把jsp页面中的HTML排版标签输出给浏览器的?
这里其实是 jsp
对应的 servlet
的功劳,我们知道服务器会将jsp
转为一个servlet
对象,我们去访问jsp
的时候,实际访问到的也是这个servlet
对象;
在这个servlet
对象对应的类中。它是通过out.writer()
语句将 jsp
中的 HTML
语句,原封不动的打给浏览器;遇到<%java代码%>
,就直接执行;
jsp页面中的java代码,服务器是如何执行的?
是原封不动的,代码最后都在在servlet
类中执行了;
Web服务器在调用jsp时,会给jsp提供一些什么java对象?
会提供许多对象;request、response、out、application、session
等这些对象,在jsp
中是直接可以使用的,因为在jsp对应的servlet类中已经提供好了;
如果想要获得jsp
对应的servlet
类的对象的自身,使用page
,也是它内置提供的;
上面的几个问题,其实都可以翻看对应的 servlet 类的源码,,找到答案;
Jsp模板元素
Jsp
页面中的 HTML
内容称之为 jsp
模板元素。
Jsp的模板元素定义了网页的基本骨架,即定义了页面的结构和外观;
下面的几个概念,分清楚了,别混淆;
Jsp脚本表达式
语法:<%= xxxx %>
jsp脚本表达用于将程序数据输出到客户端,只能写在一行;
Jsp
引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应的位置用out.print(...)
,将数据输出给客户端。
Jsp脚本表达式的变量或者表达式后面不能有分号
,其实原因很简单,脚本表达式是要被放到 out.print(...)
里面的,在结尾加上一个 ; 会被输出,而我们并不想输出这个分号;
Jsp脚本片段
jsp脚本片段用于在jsp页面中编写 多行 java代码;
语法:
<%
多行java代码
%>
注意:jsp
脚本片段中只能出现java
代码,不能出现其他模板元素
,因为,jsp引擎
在翻译jsp
页面时,会将jsp脚本片段
中的 java
代码原封不动的放到 servlet
的 _jspService
方法中。
jsp脚本片段中的java代码,必须严格遵守java语法!废话,最后被转到servlet中,java代码原封不动的;
在一个jsp页面中可以有多个脚本片段,在脚本片段之间是可以写上模板元素的;并且多个脚本片段之间的数据是共享的;(了解下底层原理,就知道为啥共享了
)
单个脚本片段中的java代码可以是不完整的,但是多个脚本片段中,java代码必须完整了 ;
Jsp声明
在jsp中,脚本片段中java代码,都被翻译到servlet的jspService方法中了;
那么假如,我们想要将java代码,写到jspService方法外面的话,就需要使用jsp声明
了;
<%!
java代码
%>
我们就可以为servlet
类添加方法
、字段
等属性了,但是一般没啥人使用这个技术;
Jsp注释
格式:
<%--
注释信息
--%>
JSP引擎在将 jsp
页面翻译成 servlet
程序时,会 忽略jsp
页面中被注释的内容 ;注意是被jsp引擎翻译时,就抛弃了,也就是在servlet类中将没有这个注释的内容;但是你要是使用HTML的注释,就不行了;
Jsp指令
jsp指令
是为 jsp引擎
而设计的。它们并不直接产生任何可见输出,而只是告诉引擎如何处理jsp页面中的其余部分。
在jsp2.0
规范中共定义3个指令:
今天2018年4月23日,jsp规范,早就不是2.0点了。需要注意下,但是不影响你学习JSP
page指令
Include指令
taglib指令
<%@ 指令 属性名=“值” %>
举例:
<%@ page contentType="text/html; ISO-8859-1" %>
如果一个指令有多个属性,这多个属性可以写在一个指令中,用 空格
分隔多个属性,也可以分开写 ;
下面讲解3大指令;
page指令
用于定义jsp页面的各种属性,无论page指令出现在jsp页面中的什么地方,它作用的都是整个jsp页面;
为了保持程序的可读性和遵循良好的编程习惯,page指令
最好是放在整个jsp页面的初始位置。
Jsp2.0规范中定义的 page指令
的完整语法:
属性名 = “值 ” |
作 用 |
---|---|
pageEncoding = "xxx" |
指明翻译 jsp 的时候,使用 xxx 码表 |
contentType = "text/html;charset=UTF-8" |
指明jsp 翻译成的 servlet 文件,里面的 response 使用的码表 |
language = "java" |
指明 jsp 页面中的语言是 java |
extends = "xxx.class" |
表名生成jsp 翻译的servlet 类继承自xxx ,不过一般,继承自默认类,即可。不需要改动 |
import = "xxx" |
表示,我们的java 语句,需要导入的包,一般自动导包就好了,不用我们手动去指定这个属性 |
session = "false / true " |
false 表示,翻译过去的servlet 中不要自动创建session 对象,目的是为了,在不需要 session 的情况下,避免生成 session ,减轻服务器的压力。或者我们需要session 对象,但是,我们想手动让服务器创建session 对象的 |
buffer = "8kb / none / xx kb" |
out.print() 的数据,不是直接写到浏览器中(效率太慢),还是先写到缓冲区中,再写到浏览器,buffer 指定缓冲区的大小,默认是8kb |
autoFlush = "false / true" |
是否自动刷新缓冲区,默认为true |
isThreadSafe = "true" |
翻译过去的servlet 是否是线程安全的,这个最TM奇怪了,默认为true。但是表示默认线程不安全 |
isErrorPage = "true" |
表示该jsp 是页面 错误处理 页面,属性值置为true 时,jsp 翻译为 servlet 时,会传进去一个异常对象,表示错误的异常;作用 当服务器出错了,跳到错误页面(404页面),然后返回给浏览器,但这404是个正确的页面,服务器就会返回200状态码,我们 为了告知浏览器我们出错了,就在页面中加上这个属性值,服务器将返回500 |
isELIgnored = "true" |
是否忽略 EL表达式 |
errorPage = "url" |
用来指定当前页面出错时,跳转的界面。属性值必须使用相对路径,以 / 开头则相对当前 web 应用;没有 / 打头,则相对于当前页面;当然,我们也可以在服务器的 web.xml 文件 使用 元素为整个web 应用指定错误处理页面,其中的两个子标签,一个用于指明异常的类型(写全名),这个子标签还可以写成状态码,一个用于指明出现该异常。跳准的界面地址;如果我们在 jsp 中写明了 errorPage 属性,那么web.xml 文件中的错误处理配置,对此jsp 不生效 |
include
指令用于引入 jsp
页面,如果使用 include
引入其他 jsp
页面,那么 jsp
引擎将把这两个jsp
翻译为一个servlet
,所以,include
指令引入通常也称为 静态导入;
属性名 = “值 ” |
作 用 |
---|---|
file = "url" |
其中的file 属性用于指定被引入文件的路径。路径以“/” 开头,来表示当前web 应用; |
细节:
jsp
语法HTML
扩展名,jsp引擎
还是会按照处理jsp
页面的方式处理页面的内容;为了,见名思意,jsp规范
建议使用 .jspf
作为静态导入文件的扩展名;.jspf
指令会涉及到两个jsp页面,并会把2个jsp翻译为一个servlet,所有这两个jsp页面的指令不能冲突;HTML
语句了,否则页面将不是一个完成的页面,会出现两个
这样的标签;动态包含:
语法: request.getRequestDispatcher("url").include(request,response);
这是在运行时包含,会将 每一个 jsp
都对应生成一个servlet
,也就是最终生成不止一个 servlet
;
我们一般选择静态导入
,性能高 ;
在讲标签库的时候,再讲;
乱码产生的根由:无外乎两种
jsp文件
保存的编码和服务器翻译 servlet
的时候的编码不一致servlet
的 response
的编码编码不一致;解决方法: <%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8" %>
我们发现只要这两个地方使用相同的编码,乱码就不会产生;
pageEncoding=”xxx”
告诉服务器翻译时使用什么码表;
contentType="text/html;charset=UTF-8"
再告诉浏览器用什么码表打开 ;
其实可以不告诉浏览器用什么码表,因为 只要告诉服务器翻译的码表,服务器会自动将servlet的response的码表设为一致的;但是,我们还是写上吧
每个jsp
页面在 第一次 被访问时,web容器
都会把请求交给 jsp引擎
(即一个java程序)去处理。Jsp引擎
先将 jsp
翻译成一个_jspServlet
(实际上就是一个servlet),然后按照 servlet
的调用方式进行调用 ;
由于JSP
第一次访问时,会被翻译成servlet,所有第一次访问通常会比较慢,但是第二次访问,JSP引擎
如果发现 JSP
没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响;
JSP引擎
在调用 JSP
对应的 _JspServlet
时,会 传递或创建 9个与WEB开发相关的对象供_JspServlet
使用。
JSP
技术的设计者为了方便web
开发人员在编写JSP
页面时,获得这些对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这9个变量就可以快速的获得这9大对象的引用;
这九大隐式对象就是:response
、request
、session
、application(ServletContext)
、page
(jsp
对象自身)、config(servlertConfig)
、exception
、out(JspWriter)
、PageContext
前面7个,在学JSP之前,都已经学过了,这里重点讲最后两个;
Out隐式对象
作用:Out隐式对象
用于向客户端发送文本数据;
Out对象
是通过调用 PageContext
对象的 getOut()
方法返回的,其作用和用法与servletResponse.getWriter
方法返回的 PrintWriter
对象非常相似。
JSP
页面中的out
隐式对象的类型为JspWriter
,JspWriter
相当于一种带缓存功能的PrintWriter
,设置JSP
页面的page
指令的buffer
属性可以调整它的缓存大小,甚至关闭它的缓存(buffer
属性的值为none
);
只有向out
对象中写入了内容,且 满足如下任何一个条件时(相当于刷新缓冲区),out
对象才会去调用servletResponse.getWriter
方法,并且通过该方法返回PrinterWriter
对象将out
对象缓冲区中的内容 真正的写入 到servlet
引擎提供的缓冲区中(response
缓冲区):
条件
:
√ 设置page
指令的buffer属性关闭了out
对象的缓存功能
√ out
对象的缓冲区已满 ;
√ 整个JSP
页面结束;
备注
:
Out
对象输出的数据 可能滞后于 response.getWriter
对象输出的数据,原因就在于out对象自己的缓冲区;out对象的缓存区满了,才将数据刷新到response的缓冲区;服务器发现response缓冲区有数据,才会将数据发送到客户端;
是jsp
技术中最重要
的一个对象,它代表jsp
页面的运行环境
;
生命周期
是一个jsp
页面;
这个对象自身就封装可其他8大隐式对象的引用;(这个最厉害了)
它自身还是一个域对象,可以用来保存数据;
这个对象中,还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其他资源、检索其他域对象中的属性;
PageContext
对象获得其他对象通过 getXXX()
方法;
PageContext
对象中包含其他8大隐式对象的引用;
首先知道一个事实:JSP
页面中是不应该出现java
代码,良好的jsp
中,是不应该出现一行java
代码的;
但是有些时候,我们为了获取servlet
传过来的数据,不得已要使用java
代码,这时候,我们就需要一个技术:自定义标签;(以后会讲这个技术)
我们把java
代码替换成一个自定义标签
,这个标签对应着一个java
类,我们需要把web中的这些对象传给java
类,一个一个传,很麻烦的,因此,我们就直接传一个pageContext
对象过去;
域 | 范围(从小到大 ) |
---|---|
Page 域: |
pageContext,最小的域,只能在页面中使用 |
Request 域: |
请求域 |
Session 域: |
会话级别的域 |
ServletContext 域: |
最大的域,在整个应用程序中可用 |
获取数据
pageContext.getAttribute(String) ;
设置数据
pageContext.setAttribute(String,String);
移除数据
pageContext.removeAttribute(String);
获取id对应的域的数据
pageContext.getAttribute(String,id) ;
设置id对应的域数据
pageContext.setAttribute(String,String,id);
移除id对应的域数据
pageContext.removeAttribute(String,id);
PageContext查找属性的方法
pageContext.findAttribute(String)
用于查找某个属性的属性值,它会依次从page
、request
、session
、application
四个域,从小到大的查找,在某个域中查到数据,即刻返回这个数据,不会再继续查下去。如果四个域都没查到,则返回null;
这个方法,便利于我们可以在jsp
中直接使用这个方法来查找数据,而不要去关注数据在哪一个域中;
PageContext
类中定义了一个forward
方法和两个include
方法来分别简化和替代RequestDispatcher(...).forward
方法和RequestDispatcher(...).include
方法。
原始的操作
:
// forward
request.getRequestDispatcher("/xxx").forward(request, response);
// include
request.getRequestDispatcher("/xxx").include(request, response);
使用pageContext
简化:
// forward
pageContext.forward("/xxx");
// include
pageContext.include("/xxxx");
方法接受的资源如果以 /
开头,/
代表当前web
应用;
JSP Action (JSP动作)
,它用于jsp
页面中提供业务逻辑功能,避免在JSP
页面中直接编写java
代码,造成jsp
页面难以维护;
常用的三种标签:
// 动态包含
include page=""> include>
//跳转页面
"">
// 负责带数据到另一个JSP页面中,value的值,可以是脚本表达式
// 一般和包含include便签一起使用
param name="" value=""> param>
// 将数据带到xxx.jsp 中
include page="xxxx.jsp">
param name="xxx" value="xxx"/>
include>
其中
Jsp跳转
应用场景:
在配置欢迎首页的时候,是不让配置servlet的,只能配置成jsp。 因此,在jsp中进行跳转页面;
<servlet>
<servlet-name>myformservlet-name>
<jsp-file>/form.jspjsp-file>
servlet>
<servlet-mapping>
<servlet-name>myformservlet-name>
<url-pattern>/myform.htmlurl-pattern>
servlet-mapping>
如果访问JSP
的时候,出现服务器报错,紧接着再次访问,服务器又不报错的情况,像神经病一样一时好一时坏;
出现这样的问题的原因在于:JSP
文件被我们写错了;并其写错之前,这个JSP曾经被我们正确写对过,并且在服务器中有翻译过的servlet,这样,当我们把JSP
修改出错以后,再次访问,服务器发现JSP
被修改过,就会重新翻译一次,生成新的servlet,覆盖之前的servlet。但是由于,JSP
语法有错,无法被正确的翻译为servlet
,这样旧的servlet
就不会被覆盖掉。还存在在服务器中;
这样,当我们第一次访问的时候,服务器翻译JSP
失败,就会爆错,但是我们紧接着,再次访问,服务器发现,这人怎么回事啊,我刚翻译完这个servlet
啊,它就不会再次翻译servlet,而是把之前旧的servlet当成上一次翻译的servlet
(这个过程,跟JSP变没变,没有任何关系,应该是JSP设计者,为了减轻服务器的压力设计的),给我们;这就是我们第一次报错,紧接着再次访问,就正确的原因;
当我们,过一会再次访问,服务器就会发现,我曹这个JSP,我并没有翻译啊,就会再次翻译,然后,就会再次报错,周而复始,给我们的感觉就是JSP在抽风,时好时坏;
仿佛一下子回到了90年代的夏天,你光着上身,穿着大裤衩,一双简单的人字拖,摇着一把破旧的芭蕉扇,VCD里转动着盗版的碟片,满满都是广东香港泛滥到内地的流行歌曲和港片,手里的冰激凌在融化,碟片偶尔会卡,你拿起遥控摁了一下快进键,一下子就这么快进了十几年。
写的真好!
《over》