jsp详解

from:jsp详解

今天我们来看一下Jsp的相关知识,首先来看看一下Jsp的相关定义:

简介:


JSP全称是JavaServer Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。
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页面的内容:


  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  3. <html>
  4.   <head>

  5.     <title>My JSP 'MyJsp.jsp' starting page</title>

  6.   </head>

  7.   <body>
  8.     This is my JSP page. <br>
  9.   </body>
  10. </html>


下面是翻译之后的源码:



  1. package org.apache.jsp;


  2. import javax.servlet.*;

  3. import javax.servlet.http.*;

  4. import javax.servlet.jsp.*;

  5. import java.util.*;


  6. public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase

  7.     implements org.apache.jasper.runtime.JspSourceDependent {


  8.   private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();


  9.   private static java.util.List _jspx_dependants;


  10.   private javax.el.ExpressionFactory _el_expressionfactory;

  11.   private org.apache.AnnotationProcessor _jsp_annotationprocessor;


  12.   public Object getDependants() {

  13.     return _jspx_dependants;

  14.   }


  15.   public void _jspInit() {

  16.     _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();

  17.     _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());

  18.   }


  19.   public void _jspDestroy() {

  20.   }


  21.   public void _jspService(HttpServletRequest request, HttpServletResponse response)

  22.         throws java.io.IOException, ServletException {


  23.     PageContext pageContext = null;

  24.     HttpSession session = null;

  25.     ServletContext application = null;

  26.     ServletConfig config = null;

  27.     JspWriter out = null;

  28.     Object page = this;

  29.     JspWriter _jspx_out = null;

  30.     PageContext _jspx_page_context = null;



  31.     try {

  32.       response.setContentType("text/html;charset=utf-8");

  33.       pageContext = _jspxFactory.getPageContext(this, request, response,

  34.                 nulltrue8192true);

  35.       _jspx_page_context = pageContext;

  36.       application = pageContext.getServletContext();

  37.       config = pageContext.getServletConfig();

  38.       session = pageContext.getSession();

  39.       out = pageContext.getOut();

  40.       _jspx_out = out;


  41.       out.write("\r\n");

  42.       out.write("\r\n");

  43.       out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");

  44.       out.write("<html>\r\n");

  45.       out.write("  <head>\r\n");

  46.       out.write("    \r\n");

  47.       out.write("    <title>My JSP 'MyJsp.jsp' starting page</title>\r\n");

  48.       out.write("    \r\n");

  49.       out.write("  </head>\r\n");

  50.       out.write("  \r\n");

  51.       out.write("  <body>\r\n");

  52.       out.write("    This is my JSP page. <br>\r\n");

  53.       out.write("  </body>\r\n");

  54.       out.write("</html>\r\n");

  55.     } catch (Throwable t) {

  56.       if (!(t instanceof SkipPageException)){

  57.         out = _jspx_out;

  58.         if (out != null && out.getBufferSize() != 0)

  59.           try { out.clearBuffer(); } catch (java.io.IOException e) {}

  60.         if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);

  61.       }

  62.     } finally {

  63.       _jspxFactory.releasePageContext(_jspx_page_context);

  64.     }

  65.   }

  66. }




我们看到,这个类继承了org.apache.jasper.runtime.HttpJspBase,要想看到这个类的源码,我们需要下载tomcat的源码,然后找到这个类,源码如下:


  1. /*

  2.  * Licensed to the Apache Software Foundation (ASF) under one or more

  3.  * contributor license agreements.  See the NOTICE file distributed with

  4.  * this work for additional information regarding copyright ownership.

  5.  * The ASF licenses this file to You under the Apache License, Version 2.0

  6.  * (the "License"); you may not use this file except in compliance with

  7.  * the License.  You may obtain a copy of the License at

  8.  * 

  9.  *      http://www.apache.org/licenses/LICENSE-2.0

  10.  * 

  11.  * Unless required by applicable law or agreed to in writing, software

  12.  * distributed under the License is distributed on an "AS IS" BASIS,

  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  14.  * See the License for the specific language governing permissions and

  15.  * limitations under the License.

  16.  */


  17. package org.apache.jasper.runtime;


  18. import java.io.IOException;


  19. import javax.servlet.ServletConfig;

  20. import javax.servlet.ServletException;

  21. import javax.servlet.http.HttpServlet;

  22. import javax.servlet.http.HttpServletRequest;

  23. import javax.servlet.http.HttpServletResponse;

  24. import javax.servlet.jsp.HttpJspPage;

  25. import javax.servlet.jsp.JspFactory;


  26. import org.apache.jasper.compiler.Localizer;


  27. /**

  28.  * This is the super class of all JSP-generated servlets.

  29.  *

  30.  * @author Anil K. Vijendran

  31.  */

  32. public abstract class HttpJspBase

  33.     extends HttpServlet

  34.     implements HttpJspPage



  35. {


  36.     protected HttpJspBase() {

  37.     }


  38.     public final void init(ServletConfig config)

  39.     throws ServletException

  40.     {

  41.         super.init(config);

  42.     jspInit();

  43.         _jspInit();

  44.     }


  45.     public String getServletInfo() {

  46.     return Localizer.getMessage("jsp.engine.info");

  47.     }


  48.     public final void destroy() {

  49.     jspDestroy();

  50.     _jspDestroy();

  51.     }


  52.     /**

  53.      * Entry point into service.

  54.      */

  55.     public final void service(HttpServletRequest request, HttpServletResponse response)

  56.     throws ServletException, IOException

  57.     {

  58.         _jspService(request, response);

  59.     }


  60.     public void jspInit() {

  61.     }


  62.     public void _jspInit() {

  63.     }


  64.     public void jspDestroy() {

  65.     }


  66.     protected void _jspDestroy() {

  67.     }


  68.     public abstract void _jspService(HttpServletRequest request,

  69.                      HttpServletResponse response)

  70.     throws ServletException, IOException;

  71. }




好吧,看到了,继承了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元素。
举例:



  1. <%

  2.     int x = 10;

  3.     out.println(x);

  4. %>

  5. <p>这是JSP页面文本</p>

  6. <%

  7.     int y = 20;

  8.     out.println(y);

  9. %>
多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);
单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:

  1. <%
  2.     for (int i=1; i<5; i++)
  3.     {
  4. %>
  5.     <H1>www.it315.org</H1>
  6. <%
  7.     }
  8. %>

Jsp的声明


JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。语法:
<%!
java代码
%>

所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。
多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。

  1. <%!

  2. static

  3. {

  4.     System.out.println("loading Servlet!");

  5. }

  6. private int globalVar = 0;

  7. public void jspInit()

  8. {

  9.     System.out.println("initializing jsp!");

  10. }

  11. %>

  12. <%!

  13. public void jspDestroy()

  14. {

  15.     System.out.println("destroying jsp!");

  16. }

  17. %>

Jsp注释


JSP注释的格式:
<%-- 注释信息 --%>
JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。 

Jsp指令


JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP2.0规范中共定义了三个指令:

page指令

Include指令

taglib指令

JSP指令的基本语法格式:
<%@ 指令 属性名="值" %>

首先我们来看一下page指令的用法


page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。

JSP 2.0规范中定义的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" ]
%>


1.看一下通过page指令设置页面的编码:
举例:<%@ page contentType="text/html;charset=gb2312"%>

这个指令的作用就相当于response.setContentType("text/html;charset=gb2312");

但是这个指令和

<%@ page pageEncoding="gb2312"%>

的区别是:

pageEncoding是jsp文件本身的编码
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就可以实现正确转码了。


因为我们使用了MyEclipse开发工具,只要我们设置其中一个编码就会将该文件的编码以及页面访问编码都设置好了,例如:

我们使用指令:

<%@ page contentType="text/html;charset=gb2312"%>

我们看一下翻译之后的servlet代码:



我们在来看一下这个文件的编码:


可以看到都是gb2312的编码,


同样我们在使用指令:

<%@ page pageEncoding="utf-8"%>

效果和上面一样的,会同时将文件的编码和页面浏览的编码都设置成了utf-8编码,这些动作都是得益于MyEclipse的智能,如果你使用txt文本开发的话,就不会有这样的结果了。


那么当我们同时使用这两个命令的话是什么样的结果呢:

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page pageEncoding="utf-8"%>

这时候我就会发现,文件的编码是utf-8,而页面浏览的编码是gb2312,所以说上面的一条指令是专门设置页面浏览的编码的,下面的指令是专门设置文件的保存编码的。出现以上的结果都是因为使用了MyEclipse开发工具。


2.通过page指令导入java包:

JSP 引擎自动导入下面的包:
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*


可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号分隔:
<%@ page import="java.util.Date,java.sql.*,java.io.*"%>
上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类:
<%@ page import="java.util.Date"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.io.*"%>


3.下面在来看一下buffer属性:

<%@ page buffer="4kb" %>

使用这个属性是指定out对象的缓存大小,关于out对象,我们后面会说到,比如我们这里设置了out的缓冲区是4kb,我们可以看一下翻译后的代码:


如果我们想关闭缓冲区的话,只需要设置值为none就可以了


4.下面在来看一下isThreadSafe属性:

这个属性见名知意,是设置是否线程安全的,我们在之前讨论Servlet的时候,说到了Servlet是个单例对象,是线程不安全的,我们那时候可以通过实现一个接口来实现线程安全,这里只需要设置这个属性值就可以控制Jsp翻译之后的Servlet时候线程安全:

<%@ page isThreadSafe="true|false" %>  默认值为true
isThreadSafe=false模式表示它是以Singleton模式运行。
该模式implements了接口SingleThreadMode,
该模式同一时刻只有一个实例,不会出现信息同步与否的概念。
若多个用户同时访问一个这种模式的页面,
那么先访问者完全执行完该页面后,后访问者才开始执行。
isThreadSafe=true模式表示它以多线程方式运行。
该模式的信息同步,需访问同步方法(用synchronized标记的)来实现。

我们将值设置成false之后发现翻译Jsp之后的Servlet:


实现了SingleThreadModel接口。


5.下面在来看一下session属性:

<%@ page session="false|true"%> 默认值是true

是指不能在本页使用session.也就是在本页面禁用了session

就是在将Jsp翻译成Servlet的时候不会传递Session对象了,比如我们将他设置成false,查看翻译后的Servlet源码:

我们发现并没有HttpSession对象,所以我们也不能在Jsp页面中使用session对象了


6.下面来看一下errorPage属性

这个属性是设置服务器端出错之后的错误页面的,比如我们在编写服务器代码的时候,突然抛出异常,那么会返回一个500,这样给用户的感觉就很恶心,所以我们要做的人性化一点,就是通过这个属性值,设置一个错误页面,当服务器发生错误的时候都会跳转到这个页面中,比如:

<%@ page errorPage="/error.jsp"%>

然后我们可以在Jsp中添加一个脚本片段:
  1. <%
  2.     int x = 1/0;
  3. %>
这样就会抛出异常,我们访问这个Jsp看一下效果:


转到了错误页面,同时我们观察地址栏可以发现这里面使用的是转发技术,所以我们在书写url地址的时候,那个开始的斜杠代表是当前应用,因为这个地址是给服务器用的(这个原则我们在之前说过了)


这里在拓展一下,如果我们想给应用配置一个全局的错误页面我们该怎么配置呢?

这个想一想肯定是在web.xml文件中做配置:
  1. <!-- 错误页面问题同时jsp中的errorPage命令的优先级比较高 -->

  2.     <error-page>

  3.         <exception-type>Exception</exception-type>

  4.         <location>/error.jsp</location>

  5.     </error-page>


  6.     <error-page>

  7.         <error-code>404</error-code>

  8.         <location>/error.jsp</location>

  9.     </error-page>


  10.     <error-page>

  11.         <error-code>500</error-code>

  12.         <location>/error.jsp</location>

  13.     </error-page>

我们看到这里可以配置抛出的异常类型所对应的错误页面,也可以配置状态码对应的错误页面,而且这里配置的结果的优先级没有errorPage属性配置错误页面的优先级高。


7.同时还有一个属性就是isErrorPages:

<@ page isErrorPages="false|true"%> 默认值是true

是否开启错误页面,就是控制上面的errorPage属性的作用开关的


8.属性isELIgnored

<@ page isELIgnored="false|true"%> 默认值是false

是否在该页面中忽视EL表达式的功能,这个我们一般都不去做设置,因为我们在页面中肯定会使用到EL表达式的
注意:如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:

  1. <%@ page contentType="text/html;charset=gb2312"%>

  2. <%@ page import="java.util.Date"%>
也可以写作:
  1. <%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>

下面在来看一下include指令


include指令很简单,就是实现页面包含的,我们之前在介绍request的时候,说到使用代码进行页面包含,那时候我们说到了,这个代码来实现页面包含是动态包含,而使用include指令来实现页面包含是静态包含,关于静态包含和动态包含,我们在下面介绍jsp:include标签的时候在详细说明

最后来看一下taglib指令


这个指令作用也是很简单的,就是引入标签,这个之后再我们后面接收JSTL的内容的时候在作介绍

Jsp中内置的9个隐式对象

每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。
由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。
JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
这9个对象分别是哪些,以及作用也是笔试经常考察的知识点。


request
response
config
application
exception
Session
page
out
pageContext
request:HttpServletRequest
response:HttpServletResponse
session: HttpSession
application: servletContext
config:servletConfig
out:JspWriter
exception
page:this
pageContext


上面其实很多对象我们都接触过了,之前介绍servlet的时候都介绍过了,关于那个exception是个异常对象,只有当我们的Jsp页面中抛出异常的时候,才会有这个对象的产生,否则是不会传递这个对象的,至于page对象,这个很简单就是当前对象,即jsp翻译后的servlet对象,那么下面就来详细解释一下out对象和pageContext对象了。


out隐式对象用于向客户端发送文本数据。
out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至可以关闭它的缓存。
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:

1.设置page指令的buffer属性关闭了out对象的缓存功能
2.out对象的缓冲区已满
3.整个JSP页面结束


下面来看一下实例:
  1. <%

  2.         out.print("aaa");

  3.         response.getWriter().write("bbb");

  4. %>

我们访问一下MyJsp.jsp页面:




我们看到了,我们是先向浏览器中输出aaa,然后在输出bbb,但是结果是相反的,原因就是out对象是JspWriter,是带有缓冲的。看下图解释:



所以我们以后再给浏览器输出数据的时候一定要注意,最好不要同时使用这两个对象,一般我们只是用out对象进行输出数据的。


pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象(之前我们介绍了三个域对象:ServletContext,Session,Request),这个域对象的生命周期最短,作用域最小,他的作用域就是当前的jsp页面,当然它可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。


getException方法返回exception隐式对象
getPage方法返回page隐式对象
getRequest方法返回request隐式对象
getResponse方法返回response隐式对象
getServletConfig方法返回config隐式对象
getServletContext方法返回application隐式对象
getSession方法返回session隐式对象
getOut方法返回out隐式对象


pageContext封装其它8大内置对象的意义,思考:如果在编程过程中,把pageContext对象传递给一个普通java对象,那么这个java对象将具有什么功能?

这个我们在后面会介绍自定义标签的时候,这个用途就体现出来了,我们只需要传递一个pageContext对象,就可以操作其他多个对象了,很方便的


pageContext对象中的操作域中数据的方法
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 


下面这个方法是非常重要的,因为这个方法是在所有的域中查找数据,查找顺序是:pageContext->request->session->ServletContext,如果查找不到相应的数据的话,就返回一个空字符串,这个方法和之后要说到的el表达式的功能是一样的
findAttribute方法  (*重点,查找各个域中的属性)


到此为止,web开发接触到了4个域对象:
pageContext(称之为page域)
request(称之为request域)
session(称之为session域)
servletContext(称之为application域)
这4个域对象是学习web的重点,也是笔试经常考察的知识点。
明确如下问题:
这4个对象的生命周期?
什么是域?为什么把这4个对象叫做域对象呢?
哪种情况下用哪种域对象。

PageContext类中定义了一个forward方法和两个include方法来分别简化和替代RequestDispatcher.forward方法和include方法

传递给这些方法的资源路径都只能是相对路径,如果路径以“/”开头,表示相对于当前WEB应用程序的根目录,否则,表示相对于当前JSP所映射到的访问路径。

JSP标签库


虽然我们希望JSP页面仅用作数据显示模块,不要嵌套任何java代码引入任何业务逻辑,但在实际开发中不引入一点业务逻辑是不可能的,但引入业务逻辑会导致页面出现难看java代码,怎么办?
Sun公司允许用户开发自定义标签封装页面的java代码,以便jsp页面不出现一行java代码。当然sun公司在jsp页面中也内置了一些标签(这些标签叫做jsp标签),开发人员使用这些标签可以完成页面的一些常用业务逻辑。
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在JSP页面中提供业务逻辑功能。


<jsp:include>标签
<jsp:forward>标签
<jsp:param>标签

<jsp:useBean>标签

<jsp:setProperty>标签

<jsp:getProperty>标签

1.<jsp:include>标签

<jsp:include>标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。
语法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。


<jsp:include>标签是动态引入(和使用代码进行include一样),<jsp:include>标签涉及到的2个JSP页面会被翻译成2个servlet,这2个servlet的内容在执行时进行合并。 而include指令是静态引入(编译时引入),涉及到的2个JSP页面会被翻译成一个servlet,其内容是在源文件级别进行合并。不管是<jsp:include>标签,还是include指令,它们都会把两个JSP页面内容合并输出,所以这两个页面不要出现重复的HTML全局架构标签,否则输出给客户端的内容将会是一个格式混乱的HTML文档。

例子:

使用<jsp:include>标签来实现包含页面:
  1. <jsp:include page="/head.jsp"></jsp:include>

  2. <jsp:include page="/foot.jsp"></jsp:include>

我们到tomcat的work目录中看一下:







我们看到,会将head.jsp和foot.jsp单独翻译成servlet,这个就是动态包含

下面在看一下使用include指令实现页面包含:
  1. <%@ include file="/head.jsp" %>
这时候我们发现work目录中并不会还单独翻译head.jsp页面了,同时我们看看MyJsp页面翻译的servlet代码:


我们看到在代码中使用静态代码块实现静态页面包含的。

<jsp:include>标签:使用page属性指定被引入资源。
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

假设myweb应用程序的根目录下有一个a.jsp文件,如果将a.jsp页面映射为如下地址:
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>标签用于把请求转发给另外一个资源。
语法:
<jsp:forward page="relativeURL | <%=expression%>" /> 

page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。

3.<jsp:param>或者<jsp:params>标签

当使用<jsp:include>和<jsp:forward>标签引入或将请求转发给其它资源时,可以使用<jsp:param>标签向这个资源传递参数。
语法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>标签

这三个标签是一起用来操作bean对象的,<jsp:useBean>是用来初始化bean对象的,<jsp:setProperty>标签是用来设置bean对象中的属性值,<jsp:getProperty>标签是用来获取bean对象中的属性值的,例子:
  1. <body>

  2.     <!-- 找到就直接用,找不到实例化 scope默认是page域-->

  3.     <jsp:useBean id="person" class="com.weijia.domain.Person" scope="page"/>


  4.     <!-- 直接设置属性值(8种基本类型的转换) -->

  5.     <jsp:setProperty name="person" property="name" value="xxx"/>


  6.     <!-- 用请求参数给属性赋值(8中基本类型的转换) -->

  7.     <jsp:setProperty name="person" property="name" param="name"/>


  8.     <!-- 获取Person中的属性name的值 -->

  9.     <jsp:getProperty name="person" property="name"/>

  10.   </body>

同时我们定义了一个Bean对象:com.weijia.domain.Person,其中有一个name属性(一定要有get/set方法才叫属性)

对于标签<jsp:useBean>他可以指定在哪个域中创建这个bean对象,他的规则是首先在这个域中查找有没有这个对象,没有就创建,有就直接拿来使用。

对于<jsp:setProperty>标签,可以直接使用value属性设置属性的值,这里面他内部是有一个类型转换的,可以将字符串值转化成8中基本类型的值,其他对象类型的转化是会报错的,比如我们现在Person类中有一个属性birthday是Date型的,那么我们这里就不能直接写成这样:
  1. <jsp:setProperty name="person" property="birthday" value="1990-08-01"/>
这样系统会报类型转化错误,

当然我们可以直接使用脚本表达式给这个属性值传递一个Date类型的对象,这样就不会又错了:
  1. <jsp:setProperty name="person" property="birthday" value="<%=new Date()%>"/>

当然我们也可以使用请求参数来设置属性值:
  1. <jsp:setProperty name="person" property="name" param="name"/>
我们访问:http://localhost:8080/JspDemo/bean.jsp?name=jiangwei,这样就可以进行属性赋值.同时这里的name属性中的填的是<jsp:useBean>标签中的id值,所以说,当我们在使用get/setProperty标签的时候一定是在useBean标签之后
关于取出属性值的标签<jsp:getProperty>的话就简单了。

下面来看一下怎么配置jsp的访问路径
  1. <servlet>
  2.     <servlet-name>SimpleJspServlet</servlet-name>
  3.     <jsp-file>/jsp/simple.jsp</jsp-file>
  4.     <load-on-startup>1</load-on-startup >
  5. </servlet>

  6.     ……

  7. <servlet-mapping>
  8.     <servlet-name>SimpleJspServlet</servlet-name>
  9.     <url-pattern>/xxx/yyy.html</url-pattern>
  10. </servlet-mapping>

Jsp中怎么排查错误


JSP页面中的JSP语法格式有问题,导致其不能被翻译成Servlet源文件,JSP引擎将提示这类错误发生在JSP页面中的位置(行和列)以及相关信息。
JSP页面中的JSP语法格式没有问题,但被翻译成的Servlet源文件中出现了Java语法问题,导致JSP页面翻译成的Servlet源文件不能通过编译,JSP引擎也将提示这类错误发生在JSP页面中的位置(行和列)以及相关信息。
JSP页面翻译成的Servlet程序在运行时出现异常,这与普通Java程序的运行时错误完全一样,Java虚拟机将提示错误发生在Servlet源文件中的位(行和列)以及相关信息。