(参考http://www.cnblogs.com/xdp-gacl/p/3764991.html 点击打开链接,以此为模板 自己做了整理、修改)
1.1 JSP概念
JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。
JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套Java代码,为用户提供动态数据。
1.2 原理
1.2.1 Web服务器是如何调用并执行一个jsp页面的?
浏览器向服务器发请求,不管访问的是什么资源,其实都是在访问Servlet,所以当访问一个jsp页面时,其实也是在访问一个Servlet,服务器在执行jsp的时候,首先把jsp翻译成一个Servlet,所以我们访问jsp时,其实不是在访问jsp,而是在访问jsp翻译过后的那个Servlet。
例如下面的代码:
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
First Jsp
<%
out.print("Hello Jsp");
%>
当我们通过浏览器访问index.jsp时,服务器首先将index.jsp翻译成一个index_jsp.class,在Tomcat服务器的work\Catalina\localhost\项目名\org\apache\jsp目录下可以看到index_jsp.class的源代码文件index_jsp.java 。
index_jsp.java的代码如下:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;
public final class index_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');
out.write('\n');
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" First Jsp \r\n");
out.write("\t\r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" ");
out.print("Hello Jsp");
out.write("\r\n");
out.write(" \r\n");
out.write("\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);
}
}
}
我们可以看到,index_jsp这个类是继承 org.apache.jasper.runtime.HttpJspBase这个类的,通过查看Tomcat服务器的源代码,可以知道在apache-tomcat-6.0.20-src\java\org\apache\jasper\runtime目录下存HttpJspBase这个类的源代码文件,如下图所示:
我们可以看看HttpJsBase这个类的源代码,如下所示:
/*
* 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;
}
HttpJspBase类是继承HttpServlet的,所以HttpJspBase类是一个Servlet,而index_jsp又是继承HttpJspBase类的,所以index_jsp类也是一个Servlet,所以当浏览器访问服务器上的index.jsp页面时,其实就是在访问index_jsp这个Servlet,index_jsp这个Servlet使用_jspService()这个方法处理请求。
1.2.2 Jsp页面中的html排版标签是如何被发送到客户端的?
浏览器接收到的这些数据
First Jsp
Hello Jsp
都是在_jspService()方法中使用如下的代码输出给浏览器的:
out.write('\r');
out.write('\n');
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" First Jsp \r\n");
out.write("\t\r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" ");
out.print("Hello Jsp");
out.write("\r\n");
out.write(" \r\n");
out.write("\r\n");
在jsp中编写的java代码和html代码都会被翻译到_jspService()方法中去,在jsp中编写的java代码会原封不动地翻译成java代码,如<%out.print("Hello Jsp");%>直接翻译成out.print("Hello Jsp");,而在jsp页面中编写的html排版标签都是以out.write("\r\n");的形式输出到浏览器,浏览器拿到html代码后才能够解析执行html代码。
1.2.3 Jsp页面中的java代码服务器是如何执行的?
在jsp中编写的java代码会被翻译到_jspService()方法中去,当执行_jspService()方法处理请求时,就会执行在jsp 编写的java代码了,所以Jsp 页面中的java代码服务器是通过调用_jspService()方法处理请求时执行的。
1.2.4 Web服务器在调用jsp时,会给jsp提供一些什么java对象?
查看_jspService()方法可以看到,Web服务器在调用jsp时,会给Jsp提供如下的 8个java对象
PageContext pageContext;
HttpSession session;
ServletContext application;
ServletConfig config;
JspWriter out;
Object page = this;
HttpServletRequest request,
HttpServletResponse response
其中page对象,request和response已经完成了实例化,而其它 5个没有实例化的对象通过下面的方式实例化
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
session = pageContext.getSession();
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
out = pageContext.getOut();
这8个java对象在Jsp页面中是可以直接使用的,如下所示 :
<%
session.setAttribute("name", "session对象");//使用session对象,设置session对象的属性
out.print(session.getAttribute("name")+"
");//获取session对象的属性
pageContext.setAttribute("name", "pageContext对象");//使用pageContext对象,设置pageContext对象的属性
out.print(pageContext.getAttribute("name")+"
");//获取pageContext对象的属性
application.setAttribute("name", "application对象");//使用application对象,设置application对象的属性
out.print(application.getAttribute("name")+"
");//获取application对象的属性
out.print("Hello Jsp"+"
");//使用out对象
out.print("服务器调用index.jsp页面时翻译成的类的名字是:"+page.getClass()+"
");//使用page对象
out.print("处理请求的Servlet的名字是:"+config.getServletName()+"
");//使用config对象
out.print(response.getContentType()+"
");//使用response对象
out.print(request.getContextPath()+"
");//使用request对象
%>
1.2.5 JSP 最佳实践
JSP 最佳实践就是jsp技术在开发中该怎么去用。
不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。其原因为,程序的数据通常要美化后再输出:让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。
1.2.6 Tomcat服务器的执行流程
第一次执行:
1)客户端通过电脑连接服务器,因为请求是动态的,所以所有的请求交给WEB容器来处理
2)在容器中找到需要执行的*.jsp文件
3)之后*.jsp文件通过转换变为*.java文件
4)*.java文件经过编译后,形成*.class文件
5)最终服务器要执行形成的*.class文件
第二次执行:
1)因为已经存在了*.class文件,所以不在需要转换和编译的过程
修改后执行:
1)源文件已经被修改过了,所以需要重新转换,重新编译。
任何语言都有自己的语法,Java中有,JSP虽然是在Java上的一种应用,但是依然有其自己扩充的语法,而且在JSP中,所有的Java语句都可以使用 。
2.1 JSP模版元素
JSP页面中的HTML内容称之为JSP 模版元素。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
2.2 JSP表达式
JSP 脚本表达式(expression)用于将程序数据输出到客户端
语法:<%= 变量或表达式 %>
举例:输出当前系统时间:
<%= new java.util.Date() %>
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;),脚本片段中必须和Java一致,要有分号。
2.3 JSP 脚本片段
JSP脚本片段(scriptlet)用于在JSP页面中编写多行Java代码。语法:
<%
多行java代码
%>
在<% %>中可以定义变量、编写语句,不能定义方法。
案例,在JSP脚本片段Scriptlet中定义变量、编写语句。代码如下:
<%
int sum=0;//声明变量
/*编写语句*/
for (int i=1;i<=100;i++){
sum+=i;
}
out.println("Sum="+sum+"
");
%>
注意事项:
JSP脚本片段中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片段中的Java代码原封不动地放到Servlet的_jspService()方法中。
JSP脚本片段中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。区别于,JSP脚本表达式中的变量或表达式后面不能有分号(;)。
在一个JSP页面中可以有多个脚本片段,在两个或多个脚本片段之间可以嵌入文本、HTML标记和其他JSP元素。
<%
int x = 10;
out.println(x);
%>
这是JSP页面文本
<%
int y = 20;
out.println(y);
%>
多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<% %>之中的情况。如:out.println(x);
单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:
<%
for (int i=1; i<5; i++)
{
%>
http://localhost:8080/JavaWeb_Jsp_Study_20140603/
<%
}
%>
2.4 JSP 声明
JSP页面中编写的所有代码,默认会翻译到servlet的service()方法中, 而JSP声明中的java代码被翻译到_jspService()方法的外面。语法:
<%!
java代码
%>
所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。
多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
JSP隐式对象的作用范围仅限于Servlet的_jspService()方法,所以在JSP声明中不能使用这些隐式对象。
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!");
}
%>
2.5 JSP注释
在JSP中,注释有两大类:
显式注释:直接使用HTML风格的注释:
隐式注释:直接使用JAVA的注释://、/*……*/
JSP自己的注释:<%- - 注释内容- -%>
这三种注释的区别
<%
//Java中的单行注释,这个注释在浏览器中查看源文件时不可见
/*
Java中的多行注释,这个注释在浏览器中查看源文件时不可见
*/
%>
<%--JSP自己的注释,这个注释在浏览器中查看源文件时不可见--%>
HTML的注释在浏览器中查看源文件的时候是可以看得到的,而Java注释和JSP注释在浏览器中查看源文件时是看不到注释的内容的,这就是这三种注释的区别。
3.1 JSP 指令简介
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。
在JSP 2.0规范中共定义了三个指令:
1)page指令
2)Include指令
3)taglib指令
JSP指令的基本语法格式:<%@ 指令 属性名="值" %>
例如:
<%@ page contentType="text/html;charset=gb2312"%>
如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>
或者写成
<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>
3.2 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" ]
%>
3.2.1 page指令的import属性
在Jsp页面中,Jsp引擎会自动导入下面的包
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*
可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号(,)分隔。例如:
<%@ page import="java.util.*,java.io.*,java.sql.*"%>
上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类。例如:
<%@ page import="java.util.Date"%>
<%@ page import="java.io.*" %>
<%@ page import="java.sql.*" %>
3.2.2 page指令的errorPage属性
类似于Java中的异常处理。errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面可以在web.xml文件中使用
如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。
案例,使用errorPage属性指明出错后跳转的错误页面。比如Test.jsp 页面有如下的代码:
<%@ page language="java" import="java.util.*" errorPage="/ErrorPage/error.jsp" pageEncoding="UTF-8"%>
测试page指令的errorPage属性
<%
//这行代码肯定会出错,因为除数是0,一运行就会抛出异常
int x = 1/0;
%>
在Test.jsp中,page指令的errorPage属性指明了出错后跳转到"/ErrorPage/error.jsp",error.jsp页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
错误信息友好提示页面
对不起,出错了,请联系管理员解决!
运行结果如下:
3.2.3 在web.xml中使用<error-page>标签为整个web应用设置错误处理页面
比如,使用
web.xml的代码下:
index.jsp
404
/ErrorPage/404Error.jsp
404Error.jsp代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
404错误友好提示页面
3秒钟后自动跳转回首页,如果没有跳转,请点击这里
当访问一个不存在的web资源时,就会跳转到在web.xml中配置的404错误处理页面404Error.jsp,如下图所示:
关于在web.xml中使用
这里需要注意的是,如果错误页面比较小,那么当访问服务器上不存在的web资源或者访问服务器出错时在IE浏览器下是无法跳转到错误页面的,显示的是IE 自己的错误页面,而在火狐和google浏览器下(其他浏览器没有测试过)是不存在注意的问题的。
我们可以通过下面的实验来证明,在web.xml中配置500错误时的错误友好提示页面。
500
/ErrorPage/500Error.jsp
500Error.jsp页面的代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
500(服务器错误)错误友好提示页面
3秒钟后自动跳转回首页,如果没有跳转,请点击这里
500Error.jsp页面的字节大小
在IE8浏览器下的运行结果:
在IE下访问Test.jsp出现500错误后,显示的是IE 自己的错误页面,而不是我们定制的那个500错误页面,而在google和火狐下却是可以正常跳转到我们自己定制的那个500错误页面的,如下图所示:
很多人遇到这个问题,而解决这个问题的办法有两种:
1)方法一,修改IE浏览器的设置(不推荐)
操作步骤:在IE【工具】->【Internet选项】->【高级】中勾掉【显示友好http错误提示】
经过这样的设置之后,访问服务器出错后就可以直接跳转到我们定制的500错误页面了,如下图所示:
这种做法需要修改客户端浏览器的配置,不推荐这样的方式。
2)方法二,不修改IE浏览器的设置下确保定制的错误页面的大小>1024字节
修改500Error.jsp,多添加一些内容,让页面的字节数大一些,修改后的500Error.jsp的代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
500(服务器错误)错误友好提示页面
3秒钟后自动跳转回首页,如果没有跳转,请点击这里
也就多加了几个中文,让500Error.jsp多了几个字节,500Error.jsp现在的字节数如下:
在IE下访问,当服务器出错时,就可以正常跳转到500Error.jsp这个定制的错误页面了,如下图所示:
经过测试,当定制的错误页面的size=617bytes时,在IE8下已经可以跳转到定制的错误页面了,其他版本的IE浏览器没有经过测试,不过为了保险起见,定制的错误页面的size最好超过1024bytes。
3.2.4 使用page指令的的isErrorPage 属性显式声明页面为错误页面
如果某一个jsp页面是作为系统的错误处理页面,那么建议将page指令的isErrorPage属性(默认为false)设置为"true"来显式声明这个Jsp页面是一个错误处理页面。
例如:将error.jsp页面显式声明为错误处理页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isErrorPage="true"%>
错误信息友好提示页面
对不起,出错了,请联系管理员解决!
将error.jsp页面显式声明为错误处理页面后,有什么好处呢,好处就是Jsp引擎在将jsp页面翻译成Servlet的时候,在Servlet的 _jspService()方法中会声明一个exception对象,然后将运行jsp出错的异常信息存储到exception对象中,如下所示:
由于Servlet的_jspService()方法中声明了exception对象,那么就可以在error.jsp页面中使用exception对象,这样就可以在Jsp页面中拿到出错的异常信息了,如下:
如果没有设置isErrorPage="true",那么在jsp页面中是无法使用exception对象的,因为在Servlet的_jspService()方法中不会声明一个exception对象,如下所示:
Jsp有9大内置对象,而一般情况下exception对象在Jsp页面中是获取不到的,只有设置page指令的isErrorPage属性为"true"来显式声明Jsp页面是一个错误处理页面之后才能够在Jsp页面中使用exception对象。
3.3 include指令
在JSP中对于包含有两种语句形式:
1) @include指令
2)
3.3.1 @include指令
@include可以包含任意的文件,当然,只是把文件的内容包含进来。
include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。
语法:<%@ include file="relativeURL"%>,其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。
include指令细节注意问题:
1) 被引入的文件必须遵循JSP语法。
2) 被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments(片段))作为静态引入文件的扩展名。
3) 由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。
include指令使用范例:
新建head.jspf页面和foot.jspf页面,分别作为jsp页面的头部和尾部,存放于WebRoot下的jspfragments文件夹中,代码如下:
head.jspf代码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
网页头部
foot.jspf代码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
网页尾部
在WebRoot文件夹下创建一个IncludeTagTest.jsp页面,在IncludeTagTest.jsp页面中使用@include指令引入head.jspf页面和foot.jspf页面,IncludeTagTest.jsp页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
jsp的Include指令测试
<%--使用include标签引入引入其它JSP页面--%>
<%@include file="/jspfragments/head.jspf" %>
网页主体内容
<%@include file="/jspfragments/foot.jspf" %>
运行结果如下:
我们查看一下jsp引擎将IncludeTagTest.jsp翻译成IncludeTagTest_jsp类之后的源代码,找到Tomcat服务器的work\Catalina\localhost\JavaWeb_Jsp_Study_20140603\org\apache\jsp目录下找到IncludeTagTest_jsp.java,如下图所示:
打开IncludeTagTest_jsp.java,里面的代码如下所示:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;
import java.util.*;
import java.util.*;
public final class IncludeTagTest_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;
static {
_jspx_dependants = new java.util.ArrayList(2);
_jspx_dependants.add("/jspfragments/head.jspf");
_jspx_dependants.add("/jspfragments/foot.jspf");
}
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("\r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" jsp的Include指令测试 \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" \r\n");
out.write(" ");
out.write("\r\n");
out.write("网页头部
\r\n");
out.write("\r\n");
out.write(" 网页主体内容
\r\n");
out.write(" ");
out.write("\r\n");
out.write("网页尾部
\r\n");
out.write("\r\n");
out.write(" \r\n");
out.write("\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);
}
}
}
可以看到,head.jspf和foot.jspf页面的内容都使用out.write输出到浏览器显示了。
使用@include可以包含任意的内容,文件的后缀是什么都无所谓。这种把别的文件内容包含到自身页面的@include语句就叫作静态包含,作用只是把别的页面内容包含进来,属于静态包含 。
3.3.2 jsp:include指令
jsp:include指令: 为动态包含,如果被包含的页面是JSP,则先处理之后再将结果包含,而如果包含的是非*.jsp文件,则只是把文件内容静态包含进来,功能与@include类似。后面再具体介绍。
4.1 JSP运行原理
每个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大对象的引用。
4.2 认识JSP的九个内置对象
request,response,session,application,config这些对象在前面都已经作了详细的介绍,这里重点介绍一下剩下的pageContext对象,out对象,page对象。
4.3 内置对象使用说明
4.3.1 page对象
page对象表示当前一个JSP页面,可以理解为一个对象本身,即,把一个JSP当作一个对象来看待。page对象在开发中几乎不用,了解一下即可。
4.3.2 out对象
out对象用于向客户端发送文本数据。
out对象是通过调用pageContext对象的getOut()方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。 只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter()方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
设置page指令的buffer属性关闭了out对象的缓存功能
out对象的缓冲区已满
整个JSP页面结束
out对象的工作原理图
4.3.3 pageContext对象
pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象(容器),可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。
1)通过pageContext获得其他对象
getException() 方法返回exception隐式对象
getPage() 方法返回page隐式对象
getRequest() 方法返回request隐式对象
getResponse() 方法返回response隐式对象
getServletConfig() 方法返回config隐式对象
getServletContext() 方法返回application隐式对象
getSession() 方法返回session隐式对象
getOut() 方法返回out隐式对象
2)pageContext封装其它8大内置对象的意义
如果在编程过程中,把pageContext对象传递给一个普通Java对象,那么这个java对象将可以获取8大隐式对象,此时这个java对象就可以和浏览器交互了,此时这个Java对象就成为了一个动态web资源了,这就是pageContext封装其它8大内置对象的意义,把pageContext传递给谁,谁就能成为一个动态web资源,那么什么情况下需要把pageContext传递给另外一个java类呢?什么情况下需要使用这种技术呢?在比较正规的开发中,jsp页面是不允许出现java代码的,如果jsp页面出现了java代码,那么就应该想办法把java代码移除掉,我们可以开发一个自定义标签来移除jsp页面上的java代码,首先围绕自定义标签写一个java类,jsp引擎在执行自定义标签的时候就会调用围绕自定义标签写的那个java类,在调用java类的时候就会把pageContext对象传递给这个java类,由于pageContext对象封装了对其它8大隐式对象的引用,因此在这个java类中就可以使用jsp页面中的8大隐式对象(request,response,config,application,exception,Session,page,out)了,pageContext对象在jsp自定义标签开发中特别重要。
3)pageContext作为域对象
pageContext对象可以作为容器来使用,因此可以将一些数据存储在pageContext对象中。
pageContext对象的常用方法 :
1 public void setAttribute(java.lang.String name,java.lang.Object value)
2 public java.lang.Object getAttribute(java.lang.String name)
3 public void removeAttribute(java.lang.String name)
4 public java.lang.Object findAttribute(java.lang.String name)
重点介绍一下findAttribute()方法,这个方法是用来查找各个域中的属性的,查看这个方法的API可以看到关于这个方法的描述:
Searches for the named attribute in page, request, session (if valid), and application scope(s) in order and returns the value associated or null.
当要查找某个属性时,findAttribute()方法按照查找顺序"page→request→session→application"在这四个对象中去查找,只要找到了就返回属性值,如果四个对象都没有找到要查找的属性,则返回一个null。
案例,使用pageContext的findAttribute()方法查找属性值。代码如下:
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3
4
5 pageContext的findAttribute方法查找属性值
6
7 <%
8 pageContext.setAttribute("name1", "孤傲苍狼");
9 request.setAttribute("name2", "白虎神皇");
10 session.setAttribute("name3", "玄天邪帝");
11 application.setAttribute("name4", "灭世魔尊");
12 %>
13 <%
14 //使用pageContext的findAttribute()方法查找属性,由于取得的值为Object类型,因此必须使用String 强制向下转型,转换成String类型
15 //查找name1属性,按照顺序"page→request→session→application"在这四个对象中去查找
16 String refName1 = (String)pageContext.findAttribute("name1");
17 String refName2 = (String)pageContext.findAttribute("name2");
18 String refName3 = (String)pageContext.findAttribute("name3");
19 String refName4 = (String)pageContext.findAttribute("name4");
20 String refName5 = (String)pageContext.findAttribute("name5");//查找一个不存在的属性
21 %>
22 pageContext.findAttribute方法查找到的属性值:
23 pageContext对象的name1属性:<%=refName1%>
24 request对象的name2属性:<%=refName2%>
25 session对象的name3属性:<%=refName3%>
26 application对象的name4属性:<%=refName4%>
27 查找不存在的name5属性:<%=refName5%>
28
29 使用EL表达式进行输出:
30 pageContext对象的name1属性:${name1}
31 request对象的name2属性:${name2}
32 session对象的name3属性:${name3}
33 application对象的name4属性:${name4}
34 不存在的name5属性:${name5}
运行结果:
EL表达式语句在执行时,会调用pageContext.findAttribute()方法,用标识符为关键字,分别从page、request、 session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)。
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访问其它域。代码如下:
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3
4
5 pageContext访问其它域
6
7 <%
8 //此时相当于往session对象中存放了一个name属性,等价于 session.setAttribute("name","孤傲苍狼");
9 pageContext.setAttribute("name","孤傲苍狼",PageContext.SESSION_SCOPE);
10 %>
11 <%
12 //取得session对象的属性,使用pageContext对象获取
13 String refName1 = (String)pageContext.getAttribute("name",PageContext.SESSION_SCOPE);
14 //由于取得的值为Object类型,因此必须使用String强制向下转型,转换成String类型
15 String refName2 = (String)session.getAttribute("name");
16 %>
17 取出存放在session对象中的属性值:
18 第一种做法:使用pageContext.getAttribute("attributeName",PageContext.SESSION_SCOPE);去取出session对象中值
19 姓名:<%=refName1%>
20 第二种做法:使用session.getAttribute("attributeName");去取出session对象中值
21 姓名:<%=refName2%>
4)PageContext引入和跳转到其他资源
PageContext类中定义了一个forward()方法(用来跳转页面)和两个include方法(用来引入页面)来分别简化和替代RequestDispatcher.forward方法和include方法。
方法接收的资源如果以“/”开头, “/”代表当前web应用。
案例,使用pageContext的forward方法跳转到其他页面。代码如下:
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3
4
5 使用pageContext的forward方法跳转页面
6
7 <%
8 //使用pageContext的forward方法跳转到pageContextDemo05.jsp页面,/代表了当前的web应用
9 pageContext.forward("/pageContextDemo05.jsp");
10 //使用pageContext.forward(relativeUrlPath)替代RequestDispatcher.forward(relativeUrlPath)
11 //使用RequestDispatcher的forward方法实现的跳转方式
12 //pageContext.getRequest().getRequestDispatcher("/pageContextDemo05.jsp").forward(request, response);
13 %>
运行结果如下图:
pageContext.forward("/pageContextDemo05.jsp");
这种写法是用来简化和替代
pageContext.getRequest().getRequestDispatcher("/pageContextDemo05.jsp").forward(request, response);这种写法的。在实际开发中,使用pageContext.forward(relativeUrlPath)方法跳转页面用得不多,主要是因为要在Jsp页面中嵌套java代码,所以这种做法简单了解一下即可,在开发中,要想从一个Jsp页面采用服务器端跳转的方式跳转到另一个Jsp页面,那么一般会使用
案例,使用pageContext的include方法引入资源。代码如下:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2
3
4 使用pageContext的include方法引入资源
5
6 <%
7 pageContext.include("/jspfragments/head.jsp");
8 %>
9 使用pageContext的include方法引入资源
10 <%
11 pageContext.include("/jspfragments/foot.jsp");
12 %>
13
14 <%--
15
16 使用jsp:include标签引入资源
17
18 --%>
运行结果,如下图:
在实际开发中,使用pageContext的include()方法引入页面这种做法也很少用,一般都使用jsp:include标签引入资源,因此这种做法了解一下即可。
所谓的属性范围就是一个属性设置之后,可以经过多少个其他页面后仍然可以访问的保存范围。
5.1 JSP属性范围
JSP中提供了四种属性范围,四种属性范围分别指以下四种:
1) 当前页:一个属性只能在一个页面中取得,跳转到其他页面无法取得
2) 一次服务器请求:一个页面中设置的属性,只要经过了服务器跳转,则跳转之后的页面可以继续取得。
3) 一次会话:一个用户设置的内容,只要是与此用户相关的页面都可以访问(一个会话表示一个人,这个人设置的东西只要这个人不走,就依然有效)
4) 上下文中:在整个服务器上设置的属性,所有人都可以访问
5.2 属性的操作方法
既然JSP中提供了四种属性范围,则四种属性范围中都将包含以下的属性操作方法。
属性的操作无外乎就是增加、取得和删除这个几个操作。
单词Attribute的意思是“属性”,setAttribute(String name,Object value)从单词的组合来看就可以知道是这个方法的是设置属性,设置属性的名字和属性的值,名字(name)为String类型,值(value)为Object类型,由于值为Object类型,这表示可以设置任意类型的数据作为值,因为所有的类都是从Object类型继承而来。因此设置属性值的时候可以是任意类型的数据。getAttribute(String name)方法是根据属性的名字取得属性,removeAttribute(String name)方法是根据属性的名字删除属性。
5.3 JSP四种属性范围的具体介绍
5.3.1 page属性范围(pageContext)
page属性范围相对好理解一些:在一个页面设置的属性,跳转到其他页面就无法访问了。但是在使用page属性范围的时候必须注意的是,虽然习惯上将页面范围的属性称为page范围,但是实际上操作的时候是使用pageContext内置对象完成的。
pageContext属性范围操作流程图
pageContext从字面上的定义,可以发现,是表示一个页面(page)的上下文(Context),可以表示一个页面中的所有内容。
从操作流程图来看,在第一个页面设置的属性经过服务器端跳转到第二个页面以后,在第二个页面是无法取得在第一个页面中设置的属性的,就好比现在坐着的桌子上有一支笔,但一旦离开了这张桌子,坐到别的桌子上时,笔就没有了。
下面通过代码来观察此范围的属性
案例,pageContextDemo01.jsp,在页面中设置两个属性。代码如下:
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //此时设置的属性只能够在本页中取得
5 pageContext.setAttribute("name","孤傲苍狼"); //设置属性
6 pageContext.setAttribute("date",new Date()); //设置属性
7 //注意:这里设置的两个属性的名字分别为name和date,这两个是字符串类型的数据,但对应的属性值MLDN和new Date这个两个值却不是字符串类型,而是两个Object类型的数据。
8 %>
9 <%
10 //取得设置的属性
11 String refName = (String)pageContext.getAttribute("name");
12 //由于取得的值为Object类型,因此必须使用String强制向下转型,转换成String类型
13 Date refDate = (Date)pageContext.getAttribute("date");
14 %>
15 姓名:<%=refName%>
16 日期:<%=refDate%>
注意:pageContext.getAttribute("name") 取到的值是Object类型。
运行结果,如下图:
这说明了在本页设置的pageContext范围属性在本页确实可以取得,下面使用跳转语句,观察跳转之后是否还可以取得属性。
范例:pageScopeDemo02.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 pageContext.setAttribute("name","孤傲苍狼");
5 pageContext.setAttribute("date",new Date());
6 %>
7 <%--使用jsp:forward标签进行服务器端跳转--%>
8
范例:pageScopeDemo03.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 String refName = (String)pageContext.getAttribute("name");
5 Date refDate = (Date)pageContext.getAttribute("date");
6 %>
7 姓名:<%=refName%>
8 日期:<%=refDate%>
在以上程序中的pageScopeDemo02.jsp只是设置了两个属性,跳转到pageScopeDemo03.jsp之后再在pageScopeDemo03.jsp中取在pageScopeDemo02.jsp设置的page属性。此时,运行结果如下:
使用了服务器端跳转,但是发现内容并不能取得,证明page范围的属性只能在本页中取得,跳转到其他页面之中不能取得。如果现在希望跳转到其他页面之中,依然可以取得,则可以扩大属性范围,使用request属性范围即可。
5.3.2 request属性范围
request属性范围表示在一次服务器跳转中有效,只要是服务器跳转,则设置的request属性可以一直传递下去。
范例:requestScopeDemo01.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 request.setAttribute("name","孤傲苍狼");
5 request.setAttribute("date",new Date());
6 %>
7 <%--使用jsp:forward标签进行服务器端跳转--%>
8
范例:requestScopeDemo02.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //取得requestScopdemo01.jsp设置的属性
5 String refName = (String)request.getAttribute("name");
6 Date refDate = (Date)request.getAttribute("date");
7 %>
8 姓名:<%=refName%>
9 日期:<%=refDate%>
运行结果,如下图:
从运行结果来看,程序跳转了,但是与page范围相比,内容可以向下继续传递,即在第一个页面设置的属性跳转到第二个页面后在第二个页面中依然可以取得第一个页面设置的属性。如果现在有第三个页面了,则也可以继续向后传递
范例:修改requestScopeDemo02.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%--使用jsp:forward标签进行服务器端跳转--%>
4
范例:requestScopeDemo03.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //取得requestScopdemo01.jsp设置的属性
5 String refName = (String)request.getAttribute("name");
6 Date refDate = (Date)request.getAttribute("date");
7 %>
8 姓名:<%=refName%>
9 日期:<%=refDate%>
以上的结果依然可以访问,但是如果,此时使用了超链接的方式传递的话,则属性是无法向下继续传递的。
范例:修改requestScopeDemo03.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //取得requestScopdemo01.jsp设置的属性
5 String refName = (String)request.getAttribute("name");
6 Date refDate = (Date)request.getAttribute("date");
7 %>
8 姓名:<%=refName%>
9 日期:<%=refDate%>
10
11 <%--使用超链接的形式跳转,这是客户端跳转,URL地址会改变--%>
12 跳转到requestScopeDemo04.jsp
13
此时使用了超链接跳转,一旦跳转之后,地址栏改变,所以此种跳转也可以称为客户端跳转。
requestScopeDemo04.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //取得requestScopdemo01.jsp设置的属性
5 String refName = (String)request.getAttribute("name");
6 Date refDate = (Date)request.getAttribute("date");
7 %>
8 姓名:<%=refName%>
9 日期:<%=refDate%>
运行结果,如下图:
requestScopeDemo04.jsp页面显示的结果是null。这说明了在requestScopeDemo01.jsp这个页面设置的属性经过超链接这种客户端跳转到别的页面时别的页面是无法取得requestScopeDemo01.jsp中设置的属性的。
如果还想进一步扩大属性范围,则可以使用session范围属性
5.3.3 session属性范围
session设置的属性不管如何跳转,都可以取得的。当然,session只针对一个用户
在第一个页面上设置的属性,跳转(服务器跳转/客户端跳转)到其他页面之后,其他的页面依然可以取得第一个页面上设置的属性。
下面通过代码来观察session属性范围
范例:sessionScopeDemo01.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 //此时设置的属性只能够在与本页相关的任何页面中取得
5 session.setAttribute("name","孤傲苍狼"); //设置属性
6 session.setAttribute("date",new Date());
7 %>
8 <%--使用服务器端跳转--%>
9
这里使用的是服务器端跳转 sessionScopeDemo02.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 String refName = (String)session.getAttribute("name");
5 Date refDate = (Date)session.getAttribute("date");
6 %>
7 姓名:<%=refName%>
8 日期:<%=refDate%>
9 <%--使用超链接这种客户端跳转--%>
10 sessionScopeDemo03
这里使用的是超链接这种客户端跳转运行程序sessionScopeDemo01.jsp结果如下所示:
sessionScopeDemo03.jsp
1 <%@page contentType="text/html;charset=UTF-8"%>
2 <%@page import="java.util.*"%>
3 <%
4 String refName = (String)session.getAttribute("name");
5 Date refDate = (Date)session.getAttribute("date");
6 %>
7 姓名:<%=refName%>
8 日期:<%=refDate%>
点击超链接sessionScopeDemo03,跳转到了sessionScopeDemo03.jsp这个页面,此时程序的运行结果如下:
这说明了即使是采用客户端跳转,在别的页面依然可以取得第一个页面中设置的session属性。但是,如果,此时新开了一个浏览器,则sessionScopeDemo03.jsp肯定无法取得sessionScopeDemo01.jsp中设置的session对象的属性,因为session只是保留了一个人的信息。如果一个属性想让所有的用户都可以访问,则可以使用最后一种属性范围:application范围。
5.3.4 application属性范围
因为application属性范围是在服务器上设置的一个属性,所以一旦设置之后任何用户都可以浏览到此属性。
下面通过代码来观察application属性范围
范例:applicationScopeDemo01.jsp
1 <%@ page contentType="text/html;charset=GBK"%>
2 <%@ page import="java.util.*"%>
3 <%
4 //此时设置的属性任何用户都可以取得
5 application.setAttribute("name","孤傲苍狼"); //设置属性
6 application.setAttribute("date",new Date());
7 %>
8 applicationScopeDemo02
范例:applicationScopeDemo02.jsp
1 <%@ page contentType="text/html;charset=GBK"%>
2 <%@ page import="java.util.*"%>
3 <%
4 String refName = (String)application.getAttribute("name");
5 Date refDate = (Date)application.getAttribute("date");
6 %>
7 姓名:<%=refName%>
8 日期:<%=refDate%>
观察页面的运行效果:
开启多个浏览器窗口,运行applicationScopeDemo02.jsp时,都可以显示出上图所示的结果,因为属性范围设置在了服务器中,所以只要是连接到此服务器的任意用户都可以取得此属性,当然,如果服务器关闭的话,则此属性肯定消失。
如把Tomcat服务器先关闭后再重新启动,打开浏览器窗口运行applicationScopeDemo02.jsp时,得到的结果如下图所示:
注意:如果在服务器上设置了过多的application属性,则会影响到服务器的性能。
5.3.5 关于pageContext属性范围的进一步补充
之前所讲解的四种属性范围,实际上都是通过pageContext属性范围设置上的。打开pageContext所在的说明文档。
PageContext类继承了JspContext类,在JspContext类中定义了setAttribute()方法,如下:
public abstract void setAttribute(String name,Object value,int scope)
此方法中存在一个scope的整型变量,此变量就表示一个属性的保存范围。
PageContext类继承了JspContext类,所以在PageContext类中实现了抽象的setAttribute()方法:
public abstract void setAttribute(String name,Object value,int scope)
这个setAttribute()方法如果不写后面的int类型的scope参数,则此参数默认为PAGE_SCOPE,则此时setAttribute()方法设置的就是page属性范围,如果传递过来的int类型参数scope为REQUEST_SCOPE,则此时setAttribute()方法设置的就是request属性范围,同理,传递的scope参数为SESSION_SCOPE和APPLICATION_SCOPE时,则表示setAttribute()方法设置的就是session属性范围和application属性范围。
下面通过代码来观察此四种属性范围常量的作用,以:request为例
范例:pageScopeDemo04.jsp
1 <%@page contentType="text/html;charset=GBK"%>
2 <%@page import="java.util.*"%>
3 <%
4 pageContext.setAttribute("name","孤傲苍狼",PageContext.REQUEST_SCOPE); //设置属性,并指明属性范围
5 pageContext.setAttribute("date",new Date(),PageContext.REQUEST_SCOPE); //设置属性,并指明属性范围
6 %>
7
pageScopeDemo05.jsp
1 <%@page contentType="text/html;charset=GBK"%>
2 <%@page import="java.util.*"%>
3 <%
4 //使用request对象获取属性
5 String refName = (String)request.getAttribute("name");
6 Date refDate = (Date)request.getAttribute("date");
7 //也可以使用pageContext对象获取属性,只要在获取时指明对象的属性范围即可
8 String refName2 = (String)pageContext.getAttribute("name", PageContext.REQUEST_SCOPE);
9 Date refDate2 = (Date)pageContext.getAttribute("date", PageContext.REQUEST_SCOPE);
10 %>
11 使用request对象获取属性:
12 姓名:<%=refName%>
13 日期:<%=refDate%>
14 使用pageContext对象获取属性:
15 姓名:<%=refName2%>
16 日期:<%=refDate2%>
运行结果:
从运行结果可以看到:在pageScopeDemo04.jsp使用的是pageContext对象调用setAttribute()方法设置的属性范围是request的属性范围,因此在调用此方法时,把一个int类型的scope范围常量REQUEST_SCOPE传递了进来,这个REQUEST_SCOPE属性范围常量的作用就是告诉pageContext对象现在要设置的属性范围是request的属性范围,所以pageScopeDemo05.jsp这个页面中可以直接使用request.getAttribute();方法获取在pageScopeDemo04.jsp设置的属性。
5.4 jsp四种属性范围的使用场合
1) request:如果客户向服务器发请求,产生的数据,用户看完就没用了,像这样的数据就存在request域,像新闻数据,属于用户看完就没用的。
2) session:如果客户向服务器发请求,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,像购物数据,用户需要看到自己购物信息,并且等一会儿,还要用这个购物数据结帐。
3) application(servletContext):如果客户向服务器发请求,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在application(servletContext)域中,像聊天数据。
6.1 JSP标签简介
JSP标签,也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写Java代码,造成jsp页面难以维护。
JSP的常用标签有以下三个:
6.2 JSP常用标签具体介绍
6.2.1
语法:
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。
范例:使用jsp:include标签引入资源
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2
3
4
5 jsp的jsp:include标签测试
6
7
8
9 <%--使用jsp:include标签引入其它JSP页面--%>
10
11 网页主体内容
12
13
14
运行结果,如下图:
下面分几点,具体介绍下
1)
而include指令是静态引入,涉及到的2个JSP页面会被翻译成一个servlet,其内容是在源文件级别进行合并。
通过下面的例子来说明
demo.jsp:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <%!
3 int i=1000;
4 %>
5 demo.jsp中i的值为:<%=i%>
分别使用include指令和
范例:使用@include包含内容
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <%!
3 int i=10;
4 %>
5 JspIncludeTagDemo01.jsp中i的值为:<%=i%>
6 <%@include file="/jspfragments/demo.jsp"%>
此时在编译jsp时就已经提示出错了,如下所示:
这个错误说的是变量i已经重复定义了。运行JspIncludeTagDemo01.jsp,结果如下:
运行后发现出现了重复定义变量i 的错误提示信息,因为静态包含是将全部内容包含进来之后,再进行处理,属于先包含后处理。由于被包含进来的页面demo.jsp中定义了一个变量i,而包含页面JspIncludeTagDemo01.jsp本身又定义了一个变量i,所以服务器在处理JspIncludeTagDemo01.jsp这个页面时就会发现里面有两个重复定义的变量i,因此就会报错。
而如果现在使用的是
范例:使用动态包含
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 JspIncludeTagDemo02.jsp
3 <%!
4 int i=10;
5 %>
6
7 JspIncludeTagDemo02.jsp中i的值为:<%=i%>
8
运行结果,如下图:
发现结果已经可以正确地显示,而且不会互相影响,这是因为使用jsp:include属于动态包含,动态包含就是指先将各个页面分别处理,处理完之后再将处理后的结果包含进来。
不管是
2) *.jspf扩展名文件在jsp:include、@include和c:import中的区别
JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。今天无意中发现,把一个JSP文件命名为jspf扩展名,然后include到另一个jsp文件中的,发现只有用"@include"指令的时候,jspf文件的内容才会被解析并执行其中的jsp指令和tag,而使用"jsp:include"和JSTL的"c:import"都没有用,jspf文件被当作纯文本文件处理了。
比如现在有一个head.jspf页面和foot.jspf页面
head.jspf
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
网页头部
foot.jspf
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
网页尾部
首先使用"@include"指令将"head.jspf和foot.jspf" include到IncludeTagTest.jsp页面,如下所示:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2
3
4
5 jsp的Include指令测试
6
7
8
9 <%--使用include标签引入引入jspf页面--%>
10 <%@include file="/jspfragments/head.jspf" %>
11 网页主体内容
12 <%@include file="/jspfragments/foot.jspf" %>
13
14
运行IncludeTagTest.jsp页面,运行结果如下:
jspf文件的内容会被解析并执行其中的jsp指令和tag,查看浏览器解析JspIncludeTagTest.jsp页面生成的源代码,如下所示:
然后再使用
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2
3
4
5 jsp的jsp:include标签测试
6
7
8
9 <%--使用jsp:include标签引入其它JSPf页面--%>
10
11 网页主体内容
12
13
14
运行JspIncludeTagTest.jsp页面,运行结果如下:
查看浏览器解析JspIncludeTagTest.jsp页面生成的源代码,如下所示:
可以看到,head.jspf和foot.jspf中的<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>没有解析执行,而是原封不动地作为文本内容输出到页面上了,在IE下看不到<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>的输出,在google和火狐浏览器下运行可以看到页面上输出<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>,如下所示:
这说明jspf文件Tomcat服务器被当作纯文本文件处理了,没有当作jsp页面来解析执行,那么该如何解决这个问题呢?如何让tomcat服务器能够解析执行*.jspf文件中的java代码和标签呢,有如下的几种解决办法:
解决办法一:修改web.xml文件,添加对扩展名为*.jspf文件的映射
如下所示:
1
2
3 jsp
4 *.jspf
5
6
7
8 jsp
9 *.jsp
10
上面的配置方式也可以简写成这样:
1
2 jsp
3 *.jsp
4
5 *.jspf
6
两种写法的效果都是一样的。
添加这样的配置信息后,此时tomcat服务器就可以正常解析执行*.jspf文件了,如下所示:
解决办法二:修改Tomcat服务器的web.xml文件,添加对扩展名为*.jspf文件的映射
找到tomcat服务器的web.xml文件,如下所示:
找到名字为jsp的那个Servlet,如下所示:
1
2 jsp
3 org.apache.jasper.servlet.JspServlet
4
5 fork
6 false
7
8
9 xpoweredBy
10 false
11
12 3
13
然后根据Servlet名找到对应的servlet-mapping配置,如下所示:
1
2
3 jsp
4 *.jsp
5 *.jspx
6
在这里可以看到,名字为jsp的那个Servlet只支持*.jsp和*.jspx两种扩展名,因此可以在这个地方添加多一个
1
2
3 jsp
4 *.jsp
5 *.jspx
6 *.jspf
7
经过这样的配置之后,Tomcat服务器就可以正常解析和执行*.jspf文件了。
6.2.2
语法:
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。
范例:使用
forwarddemo01.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%--使用标签跳转到forwarddemo02.jsp--%>
forwarddemo02.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
跳转之后的页面!!
运行结果如下:
从页面的显示效果来看,页面已经完成了跳转,但地址栏没有变化,地址栏中显示的地址还是forwarddemo01.jsp,但页面显示的内容却是forwardemo02.jsp中的内容。因为此跳转属于服务器端跳转。只要是服务器端跳转,则地址栏永远没有变化。
6.2.3
当使用
语法1:
语法2:
范例:使用
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 JspIncludeTagDemo03.jsp
3
4
5
6
7
Inc.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 接收从JspIncludeTagDemo03.jsp页面中传递过来的参数:
3 <%=request.getParameter("parm1")%>
4 <%=request.getParameter("parm2")%>
在JspIncludeTagDemo03.jsp页面中使用
范例:使用
forwarddemo03.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <%--使用标签跳转到forwarddemo04.jsp--%>
3 <%--使用标签向forwarddemo04.jsp传递参数--%>
4
5
6
7
forwarddemo04.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 跳转之后的页面!!
3 接收从forwarddemo03.jsp传递过来的参数:
4 ref1:<%=request.getParameter("ref1")%>
5 ref2:<%=request.getParameter("ref2")%>
运行结果,如下图:
----------------------------------------------- 我是低调的分隔线 -----------------------------------------------