今天我们来看一下Jsp的相关知识,首先来看看一下Jsp的相关定义:
简介:
JSP这门技术的最大的特点在于,写jsp就像在写html,但:
它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。
相比servlet而言,servlet很难对数据进行排版,而jsp除了可以用java代码产生动态数据的同时,也很容易对数据进行排版。
不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。
其原因为,程序的数据通常要美化后再输出:
让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。
Jsp的运行原理:
目标:
Web服务器是如何调用并执行一个jsp页面的?
Jsp页面中的html排版标签是如何被发送到客户端的?
Jsp页面中的java代码服务器是如何执行的?
Web服务器在调用jsp时,会给jsp提供一些什么java对象?
思考:JSP为什么可以像servlet一样,也可以叫做动态web资源的开发技术?
其实Jsp就是一个Servlet,所以我们要先介绍Servlet的相关技术,当我们第一次访问Jsp的时候,Jsp引擎都会将这个Jsp翻译成一个Servlet,这个文件存放在Tomcat中的work目录中,这里,我们新建一个MyJsp.jsp页面,然后访问以下,我们看一下翻译后的源码:
以下是MyJsp.jsp页面的内容:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head> <title>My JSP 'MyJsp.jsp' starting page</title> </head> <body>
This is my JSP page. <br>
</body>
</html>
下面是翻译之后的源码:
package org.apache.jsp; import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*; public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent { private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory(); private static java.util.List _jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.AnnotationProcessor _jsp_annotationprocessor; public Object getDependants() {
return _jspx_dependants;
} public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
} public void _jspDestroy() {
} public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException { PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null; try {
response.setContentType("text/html;charset=utf-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out; out.write("\r\n");
out.write("\r\n");
out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
out.write(" \r\n");
out.write(" <title>My JSP 'MyJsp.jsp' starting page</title>\r\n");
out.write(" \r\n");
out.write(" </head>\r\n");
out.write(" \r\n");
out.write(" <body>\r\n");
out.write(" This is my JSP page. <br>\r\n");
out.write(" </body>\r\n");
out.write("</html>\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
我们看到,这个类继承了org.apache.jasper.runtime.HttpJspBase,要想看到这个类的源码,我们需要下载tomcat的源码,然后找到这个类,源码如下:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.apache.jasper.runtime; import java.io.IOException; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.HttpJspPage;
import javax.servlet.jsp.JspFactory; import org.apache.jasper.compiler.Localizer; /**
* This is the super class of all JSP-generated servlets.
*
* @author Anil K. Vijendran
*/
public abstract class HttpJspBase
extends HttpServlet
implements HttpJspPage { protected HttpJspBase() {
} public final void init(ServletConfig config)
throws ServletException
{
super.init(config);
jspInit();
_jspInit();
} public String getServletInfo() {
return Localizer.getMessage("jsp.engine.info");
} public final void destroy() {
jspDestroy();
_jspDestroy();
} /**
* Entry point into service.
*/
public final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
_jspService(request, response);
} public void jspInit() {
} public void _jspInit() {
} public void jspDestroy() {
} protected void _jspDestroy() {
} public abstract void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
}
好吧,看到了,继承了HttpServlet类,所以说其实Jsp就是一个Servlet
Jsp的语法:
1.JSP模版元素
2.JSP表达式
3.JSP脚本片段
4.JSP注释
5.JSP指令
6.JSP标签
7.JSP内置对象
8.如何查找JSP页面中的错误
Jsp模板元素
JSP页面中的HTML内容称之为JSP模版元素。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
Jsp中的脚本表达式
JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:<%= 变量或表达式 %>
举例:当前时间:<%= new java.util.Date() %>
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;)。
如下图:Jsp翻译之后的Servlet中的脚本表达式翻译的结果:
就是将脚本表达式中的代码原封不动的使用out.print()输出
Jsp中的脚本片段
JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:
<%
多行java代码
%>
注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
举例:
<%
int x = 10;
out.println(x);
%>
<p>这是JSP页面文本</p>
<%
int y = 20;
out.println(y);
%>
多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);
单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:
<%
for (int i=1; i<5; i++)
{
%>
<H1>www.it315.org</H1>
<%
}
%>
Jsp的声明
JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。语法:
<%!
java代码
%>
所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。
多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
<%!
static
{
System.out.println("loading Servlet!");
}
private int globalVar = 0;
public void jspInit()
{
System.out.println("initializing jsp!");
}
%>
<%!
public void jspDestroy()
{
System.out.println("destroying jsp!");
}
%>
Jsp注释
JSP注释的格式:
<%-- 注释信息 --%>
JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。
Jsp指令
<%@ 指令 属性名="值" %>
首先我们来看一下page指令的用法
<%@ page
[ language="java" ]
[ extends="package.class" ]
[ import="{package.class | package.*}, ..." ]
[ session="true | false" ]
[ buffer="none | 8kb | sizekb" ]
[ autoFlush="true | false" ]
[ isThreadSafe="true | false" ]
[ info="text" ]
[ errorPage="relative_url" ]
[ isErrorPage="true | false" ]
[ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ]
[ pageEncoding="characterSet | ISO-8859-1" ]
[ isELIgnored="true | false" ]
%>
举例:<%@ page contentType="text/html;charset=gb2312"%>
contentType的charset是指服务器发送给客户端时的内容编码
JSP要经过两次的“编码”,第一阶段会用pageEncoding,第二阶段会用utf-8至utf-8,第三阶段就是由Tomcat出来的网页, 用的是contentType。
第一阶段是jsp编译成.java,它会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码(即.java),如果pageEncoding设定错了,或没有设定,出来的就是中文乱码。
第二阶段是由JAVAC的JAVA源码至java byteCode的编译,不论JSP编写时候用的是什么编码方案,经过这个阶段的结果全部是UTF-8的encoding的java源码。
JAVAC用UTF-8的encoding读取java源码,编译成UTF-8 encoding的二进制码(即.class),这是JVM对常数字串在二进制码(java encoding)内表达的规范。
第三阶段是Tomcat(或其的application container)载入和执行阶段二的来的JAVA二进制码,输出的结果,也就是在客户端见到的,这时隐藏在阶段一和阶段二的参数contentType就发挥了功效
contentType的設定.
pageEncoding 和contentType的预设都是 ISO8859-1. 而随便设定了其中一个, 另一个就跟着一样了(TOMCAT4.1.27是如此). 但这不是绝对的, 这要看各自JSPC的处理方式. 而pageEncoding不等于contentType, 更有利亚洲区的文字 CJKV系JSP网页的开发和展示, (例pageEncoding=GB2312 不等于 contentType=utf-8)。
jsp文件不像.java,.java在被编译器读入的时候默认采用的是操作系统所设定的locale所对应的编码,比如*就是GBK,*就是BIG5或者MS950。而一般我们不管是在记事本还是在ue中写代码,如果没有经过特别转码的话,写出来的都是本地编码格式的内容。所以编译器采用的方法刚好可以让虚拟机得到正确的资料。
但是jsp文件不是这样,它没有这个默认转码过程,但是指定了pageEncoding就可以实现正确转码了。
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*
<%@ page import="java.util.Date,java.sql.*,java.io.*"%>
上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类:
<%@ page import="java.util.Date"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.io.*"%>
isThreadSafe=false模式表示它是以Singleton模式运行。
该模式implements了接口SingleThreadMode,
该模式同一时刻只有一个实例,不会出现信息同步与否的概念。
若多个用户同时访问一个这种模式的页面,
那么先访问者完全执行完该页面后,后访问者才开始执行。
isThreadSafe=true模式表示它以多线程方式运行。
该模式的信息同步,需访问同步方法(用synchronized标记的)来实现。
<%
int x = 1/0;
%>
<!-- 错误页面问题同时jsp中的errorPage命令的优先级比较高 -->
<error-page>
<exception-type>Exception</exception-type>
<location>/error.jsp</location>
</error-page> <error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page> <error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
我们看到这里可以配置抛出的异常类型所对应的错误页面,也可以配置状态码对应的错误页面,而且这里配置的结果的优先级没有errorPage属性配置错误页面的优先级高。
例如:
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>
也可以写作:
<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>
下面在来看一下include指令
最后来看一下taglib指令
Jsp中内置的9个隐式对象
由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。
JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
这9个对象分别是哪些,以及作用也是笔试经常考察的知识点。
response
config
application
exception
Session
page
out
pageContext
request:HttpServletRequest
response:HttpServletResponse
session: HttpSession
application: servletContext
config:servletConfig
out:JspWriter
exception
page:this
pageContext
out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至可以关闭它的缓存。
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
2.out对象的缓冲区已满
3.整个JSP页面结束
<%
out.print("aaa");
response.getWriter().write("bbb");
%>
我们访问一下MyJsp.jsp页面:
getPage方法返回page隐式对象
getRequest方法返回request隐式对象
getResponse方法返回response隐式对象
getServletConfig方法返回config隐式对象
getServletContext方法返回application隐式对象
getSession方法返回session隐式对象
getOut方法返回out隐式对象
pageContext封装其它8大内置对象的意义,思考:如果在编程过程中,把pageContext对象传递给一个普通java对象,那么这个java对象将具有什么功能?
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)
pageContext对象中还封装了访问其它域的方法(和上面的方法不同之处就是多了一个参数,这个参数是可以直接指定相应的域)
public java.lang.Object getAttribute(java.lang.String name,int scope)
public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
public void removeAttribute(java.lang.String name,int scope)
代表各个域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE
findAttribute方法 (*重点,查找各个域中的属性)
pageContext(称之为page域)
request(称之为request域)
session(称之为session域)
servletContext(称之为application域)
这4个域对象是学习web的重点,也是笔试经常考察的知识点。
明确如下问题:
这4个对象的生命周期?
什么是域?为什么把这4个对象叫做域对象呢?
哪种情况下用哪种域对象。
传递给这些方法的资源路径都只能是相对路径,如果路径以“/”开头,表示相对于当前WEB应用程序的根目录,否则,表示相对于当前JSP所映射到的访问路径。
JSP标签库
Sun公司允许用户开发自定义标签封装页面的java代码,以便jsp页面不出现一行java代码。当然sun公司在jsp页面中也内置了一些标签(这些标签叫做jsp标签),开发人员使用这些标签可以完成页面的一些常用业务逻辑。
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在JSP页面中提供业务逻辑功能。
<jsp:forward>标签
<jsp:param>标签
1.<jsp:include>标签
语法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。
<jsp:include page="/head.jsp"></jsp:include>
<jsp:include page="/foot.jsp"></jsp:include>
我们到tomcat的work目录中看一下:
<%@ include file="/head.jsp" %>
这时候我们发现work目录中并不会还单独翻译head.jsp页面了,同时我们看看MyJsp页面翻译的servlet代码:
include指令:使用file属性指定被引入资源。
假设myweb应用的根目录下有一个a.jsp文件 如果将a.jsp页面映射成了如下地址:
http://localhost:8080/myweb/dir1/a.html
在a.jsp页面中使用了如下语句引入b.jsp文件:
<jsp:include page="b.jsp" />
请问:b.jsp要位于什么位置,上面的include才不会出错?
http://localhost:8080/myweb/b.jspf
http://localhost:8080/myweb/dir1/b.jspf
http://localhost:8080/myweb/dir1/a.html
在a.jsp页面中使用了如下语句引入b.jspf文件:
<%@ include file=“b.jspf”%>
请问: b.jspf要位于什么位置,上面的include才不会出错?
http://localhost:8080/myweb/b.jspf
http://localhost:8080/myweb/dir1/b.jspf
2.<jsp:forward>标签
语法:
<jsp:forward page="relativeURL | <%=expression%>" />
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。
3.<jsp:param>或者<jsp:params>标签
语法1:
<jsp:include page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
语法2:
<jsp:forward page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
<jsp:param>标签的name属性用于指定参数名,value属性用于指定参数值。在<jsp:include>和<jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数。
4.<jsp:useBean>标签,<jsp:setProperty>标签,<jsp:getProperty>标签
<body>
<!-- 找到就直接用,找不到实例化 scope默认是page域-->
<jsp:useBean id="person" class="com.weijia.domain.Person" scope="page"/> <!-- 直接设置属性值(8种基本类型的转换) -->
<jsp:setProperty name="person" property="name" value="xxx"/> <!-- 用请求参数给属性赋值(8中基本类型的转换) -->
<jsp:setProperty name="person" property="name" param="name"/> <!-- 获取Person中的属性name的值 -->
<jsp:getProperty name="person" property="name"/>
</body>
同时我们定义了一个Bean对象:com.weijia.domain.Person,其中有一个name属性(一定要有get/set方法才叫属性)
<jsp:setProperty name="person" property="birthday" value="1990-08-01"/>
这样系统会报类型转化错误,
<jsp:setProperty name="person" property="birthday" value="<%=new Date()%>"/>
<jsp:setProperty name="person" property="name" param="name"/>
我们访问:http://localhost:8080/JspDemo/bean.jsp?name=jiangwei,这样就可以进行属性赋值
<servlet>
<servlet-name>SimpleJspServlet</servlet-name>
<jsp-file>/jsp/simple.jsp</jsp-file>
<load-on-startup>1</load-on-startup >
</servlet>
……
<servlet-mapping>
<servlet-name>SimpleJspServlet</servlet-name>
<url-pattern>/xxx/yyy.html</url-pattern>
</servlet-mapping>
Jsp中怎么排查错误
JSP页面中的JSP语法格式有问题,导致其不能被翻译成Servlet源文件,JSP引擎将提示这类错误发生在JSP页面中的位置(行和列)以及相关信息。
JSP页面中的JSP语法格式没有问题,但被翻译成的Servlet源文件中出现了Java语法问题,导致JSP页面翻译成的Servlet源文件不能通过编译,JSP引擎也将提示这类错误发生在JSP页面中的位置(行和列)以及相关信息。
JSP页面翻译成的Servlet程序在运行时出现异常,这与普通Java程序的运行时错误完全一样,Java虚拟机将提示错误发生在Servlet源文件中的位(行和列)以及相关信息。