初学JSP

Posted yumiaoxa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初学JSP相关的知识,希望对你有一定的参考价值。

一. 基本了解

    JSP是应用最广泛的表现层技术,它和Servlet是Java EE的两个基本成员。JSP和Servlet本质是一样的,因为JSP最终编译成ServLet才能运行。

1.1 web应用和web.xml文件

对于web应用而言,WEB-INF是一个特殊的文件夹。web容器会包含该文件夹下的内容,客户端浏览器无法访问WEB-INF路径下的任何内容。

web.xml配置的管理内容

  • 配置JSP
  • 配置和管理Listener
  • 配置和管理Filter
  • 配置标签库
  • 配置JSP属性

除此之外,web.xml还负责配置、管理如下常用内容

  • 配置和管理JAAS授权验证。
  • 配置和管理资源引用
  • web应用首页

web.xml文件的根元素是<web-app.../>元素,在servlet 3.0规范中,该元素新增了如下属性。

metadata-complete:该属性接受true和false两个属性值。当该属性值为true时,web应用将不会加载注解配置的Web组件(如Servlet,Filter,Listener等)

在web.xml文件中配置首页使用welcome-file-list元素,该元素能包含多个welcome-file子元素,其中每个welcome-file子元素配置一个首页。例如

 <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

 二. JSP原理

2.1 JSP页面的内容有两部分组成

  • 静态部分:标准的HTML标签,静态的页面内容,这些内容与静态HTML页面相同
  • 动态部分:受Java程序控制的内容,这些内容由Java脚本动态生成

jsp的本质是servlet,使用jsp其实还是使用servlet,因为web应用中的JSP页面都会由Servlet容器生成对应的servlet,对Tomcat而言,JSP页面生成的Servlet放在work路径对应的Web应用下。当启动Tomcat后,Tomcat根据JSP页面生成对应的Servlet的Java文件和class文件。

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/8.5.31
 * Generated at: 2018-05-12 09:10:41 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;


public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private static final java.util.Set<java.lang.String> _jspx_imports_packages;

  private static final java.util.Set<java.lang.String> _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = new java.util.HashSet<>();

  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {

    final java.lang.String _jspx_method = request.getMethod();
    if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
      return;
    }

    final javax.servlet.jsp.PageContext pageContext;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                  null, false, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      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("\\r\\n");
      out.write("\\r\\n");
      out.write("\\r\\n");
      out.write("<html>\\r\\n");
      out.write("<head>\\r\\n");
      out.write("    <meta charset=\\"UTF-8\\">\\r\\n");
      out.write("    <title>首页</title>\\r\\n");
      out.write("</head>\\r\\n");
      out.write("<body>\\r\\n");

    

      out.write("\\r\\n");
      out.write("Java 中国\\r\\n");
      out.write("</body>\\r\\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

可以看出,去掉前缀之后,该Java类主要包含三个方法

  • initial():初始化JSP/Servlet方法
  • destroy():销毁JSP/Servlet之前的方法
  • service():对用户请求生成响应的方法

  2.3 JSP的工作原理四个结论

  1. JSP文件必须在JSP服务器内运行(Tomcat)
  2. JSP文件必须生成servlet才能运行
  3. 每个JSP页面的第一个访问者速度很慢,必须等待JSP编译成Serlet
  4. JSP页面的访问者无需安装任何客户端,甚至不需要可以运行Java的运行环境,因为JSP页面输送到客户端的是标准的HTML页面

   2.4 JSP的4种基本语法

  1. JSP注释:<%-- jsp注释部分 --%> 对应的是HTML注释<!-- html注释部分 -->
  2. JSP声明:<%! 声明部分 %>,可以声明Java变量甚至方法,编译成Java脚本后,生命部分内容成为Servlet类的全局变量和方法
  3. 输出JSP表达式:<%=表达式%>
  4. JSP脚本 <%JSP脚本部分%>,脚本代码可以控制页面的静态内容,它的编译内容放在Servlet的service方法体内,声明的变量是局部变量,不能使用访问控制修饰符修饰。

   2.5 JSP的三个编译指令

JSP的编译指令是通知JSP的引擎的消息,它不直接生成输出。编译指令都有默认值。

常见的编译指令有如下三个。

  • page:该指令是针对当前页面的指令
  • include:用于指定包含另一个页面
  • taglib:用于定义和自定义标签库

2.5.1 page指令

page指令通常位于JSP页面的顶端,一个JSP页面可以使用多条page指令,page指令的语法格式<%@page %>

page指令的各属性值如下:

  • language:声明当前JSP页面使用的脚本语言的种类,默认是java,所以通常无需设置
  • extends:指定JSP页面编译所产生的Java类所继承的父类,或所实现的接口。
  • import:用来导入包。默认导入的包有java.lang.*,java.servlet.*,java.servlet.jsp.*,javax.servlet.http.*
  • session:设定这个JSP页面是否需要HTTP Session
  • buffer:指定输出缓冲区的大小。输出缓冲区的JSP内部对象:out用于缓存JSP页面对客户浏览器的输出,默认8kb,可以设置为none,也可以设置为其他值,单位KB。
  • autoFlush:当输出缓冲区即将溢出时,是否需要强制输出缓冲区的内容。设置为true时为正常输出;如果设置为false,则会在buffer溢出时产生一个异常。
  • info:设置该JSP程序的信息说明,,可以通过getServletInfo()获取该值。
  • errorPage:指定错误处理页面。如果本页面产生了异常和错误,而该JSP页面没有对应的处理代码,则会自动调用该属性指定的页面
  • isErrorPage:设置本JSP页面是否为错误处理程序。如果该页面本身已是错误处理页面,则通常无需指定isErrorPage属性
  • contentType:用于设定生成网页的文件格式和编码字符集,即MINE类型和页面字符值类型。默认是text/html和ISO-8859-1.
  • pageEncoding:指定生成网页的字符编码集。

2.5.2 include 指令

使用include指令,可以将一个外部文件嵌入到当前的JSP文件中,同时解析这个页面的中的JSP语句(如果有的话),这是个静态的include语句,它会把目标页面的其他编译指令也包含进来,但动态include则不会。

include既可以包含静态的文本,也可以包含动态的JSP页面。静态的include编译指令会将被包含的页面融入本页面。因此被包含页面甚至不需要是一个完整的页面。

include编译指令的语法如下:

<%@include file="relativeURLSpec" %>

如果被嵌入的文件经常需要改变,建议使用<jsp:include>操作指令,因为他是动态的include语句。

需要指出的是,静态包含还会将被包含页面的编译指令也包含进来,如果两个页面的编译指令发生冲突,那么页面就会报错。

  2.6 JSP的7个动作指令

jsp:forward  执行页面转向,将请求的处理转发到下一个页面。

jsp:param  用于传递参数,必须与其他支持参数的标签一起使用。

jsp:include  用于动态引入一个JSP页面

jsp:plugin  用于下载JavaBean或Applet到客户端执行

jsp:useBean  创建一个JavaBean的实例

jsp:setProperty  设置JavaBean实例的属性值

jsp:getProperty  输出JavaBean实例的属性值

2.6.1 forward指令

forward 指令用于将页面响应转发到另外的页面。即可以转发到静态的HTML页面,也可以转发到动态的JSP页面,或者转发到容器中的Serclet。

JSP的forward指令格式如下:

对于JSP 1.0,使用如下语法:

<jsp:forward page="{relativeURL | <%=expression%>}">

对于JSP 1.1以上规范,可使用如下语法

<jsp:forward page="{relativeURL | <%=expression%>}">
     {<jsp:param.../>}
</jsp:forward>

第二种语法用于转法时增加额外的参数。增加的请求参数的值可以通过HttpServletRequest类的getParameter()方法获取,看下面实例

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
 <jsp:forward page="forward-result.jsp">
      <jsp:param name="age" value="29"/>
 </jsp:forward>
</body>
</html>

 在forward-result.jsp中,可以使用request内置对象获取请求的参数

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>forward结果页</title>
</head>
<body>
 <%=request.getParameter("age")%>
</body>
</html>

执行效果如图:

从图中可以看出,执行forward指令时,用户请求的地址依然没有改变,而页面内容全部被为forward目标页的内容

执行forward指令转发请求时,客户端请求参数不会丢失。实际上,重定向forward指令并没有向新页面发送请求,只是完全采用了新页面来对用户生成响应——请求依然是一次请求,所以请求参数,请求属性都不会丢失。

2.6.2 include 指令

include指令是一个动态的include指令,也用于包含页面,它不会导入被include页面的编译指令,仅仅将被导入页面的body内容插入本页面。

include动作指令的语法格式:

<jsp:include page="{relativeURL | <%=expression%>}" flush="true"/>

或者

<jsp:include page="{relativeURL | <%=expression%>}" flush="true">
     <jsp:param name="parameterName" value="parameterValue"/>
</jsp:include>

flush属性用于指定输出缓存是否转移到被导入文件中。如果指定为true,则包含在被导入文件中;如果指定为false,则包含在源文件中。动态导入的关键是插入目标页面的内容而不是将目标页面融入本页面中。

归纳起来,静态导入和动态导入有三点区别

  1. 静态导入是将目标页面代码完全融入,两个页面完全融合成一个servlet;而动态导入则在Servlet中使用include方法来引入得导入的内容。
  2. 静态导入时被引入的编译指令会起作用;而动态导入时被导入的页面的编译指令则失去作用,只是插入被导入页面的body内容
  3. 动态还可增加额外的参数

实际上forward和include指令语法和作用十分相似,forward是拿目标页面代替原有页面,而include则拿目标页面插入原有页面

2.6.3 useBean,setProperty,getProperty指令

这三个指令都是与JavaBean相关的指令,其中useBean指令用于在JSP页面中初始化一个Java实例;setProperty指令用于JavaBean实例设置属性值;getProperty指令用于获取JavaBean实例的属性值。

useBean的语法格式如下:

<jsp:useBean id="name" class="classname" scope="page | request | session | application"/>

 

scope属性用于指定JavaBean实例的作用范围

setProperty的语法格式

<jsp:setProperty name="BeanName" property="propertyName" value="propertyValue"/>

 

getProperty的语法格式

<jsp:setProperty name="BeanName" property="propertyName" value="propertyValue"/>

下面的jsp页面示范了这三个动作指令的使用

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JavaBean 测试</title>
</head>
<body>
<jsp:useBean id="p1" class="java.Person" scope="page"/>
<jsp:setProperty name="p1" property="name" value="sherman"/>
<jsp:setProperty name="p1" property="age" value="23"/>
<jsp:getProperty name="p1" property="name" /><br>
<jsp:getProperty name="p1" property="age"/>
</body>
</html>

事实上,当页面使用setProperty和getProperty标签时,系统底层就是调用Bean类的setter和getter方法。上面代码可以换成Java脚本代替,如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page import=""%>
<html>
<head>
    <title>JavaBean 测试</title>
</head>
<body>
<%
Person p1 = new Person();
p1.setName("sherman");
p1.setAge(23);
p1.getName();
p1.getAge();
%>
</body>
</html>

通常还需要将JavaBean放入指定的scope中

pageContext.setAttribute("p1",p1);
request.setAttribute("p1",p1);
session.setAttribute("p1",p1);
application.setAttribute("p1",p1);

2.6.4 plugin指令

plugin指令主要用于下载服务器端的JavaBean和Applet到客户端执行。由于程序在客户端执行,因此客户端必须安装虚拟机。这个指令很少使用。

2.6.5 param指令

param指令本身不能单独使用,通常与include指令,forward指令,plugin指令介个使用。

  2.7 JSP脚本的九个内置对象

JSP脚本中包含9个内置对象,这九个内置对象都是Servlet API接口的实例,只是JSP规范对他们进行了初始化,也就是说它们本身就是对象,可以直接使用,它们依次如下:

  1. application:Javax.servlet.ServletContext的实例,该实例代表JSP页面所属的web应用本身,可用于JSP页面,Servlet之间交换信息,主要方法有setAttribute()和setAttribute()等。
  2. config:javax.servlet.ServletConfig 的实例,该实例代表JSP的配置信息。常用的方法有getInitParameter(String parameter)和getInitparameterNames()等方法。事实上,JSP页面通常无需配置,因此该对象更多的在servlet中有效。
  3. exception:java.lang.Throwable的实例,该例代表其他页面中的异常和错误,只有当页面时错误处理页面,即编译指令page的isErrorPage为true时,该对象才可以使用。常用的方法有getMessage()和printStackTrace()等。
  4. out:javax.servlet.jsp.Writer的实例,该实例代表JSP页面的输出流,用于输出内容,形成HTML页面
  5. page:代表该页面本身,通常没多大用处,相当于Servlet的this
  6. pageContext:javax.servlet.jsp.pageContext 的实例,该对象代表JSP页面的上下文,使用该对象可以访问页面中的共享数据。常用的方法有getServletContext()和getServletConfig()等。
  7. request:javax.servlet.http.HttpServletRequest的实例,该对象封装了一次请求,客户端的请求参数都被封装在这个对象里。这是一个常用的对象,获取客户端请求的参数必须使用该对象,常用方法有getParameter(String parameterName),getParameterValues(String parameterName),setAttribute(String attrName,Object attrValue),getAttribute(String attrName)和setCharacterEncoding(String env)等。
  8. response:javax.servlet.http.HttpServletResponse的实例,代表服务器对客户端的响应。通常很少使用该对象直接响应,而是使用out对象,除非需要生成非字符响应。而response常用于重定向,常用的方法有getOutputStream(),sendRedirect(java.lang.String location)等。
  9. session:javax.servlet.http.HttpSession的实例,该对象代表一次会话。当客户端浏览器请求与站点建立连接时,会话开始;当客户端关闭浏览器时,会话结束。常用的方法有:getAttribute(String attrName),setAttribute(String attrName,Object attrValue)等。

JSP内置对象的实质:他们要么是_jspService()方法的形参,要么是该方法的局部变量,它们都是在_jspService()内完成初始化的,所以可以在JSP脚本中调用这些对象,无需创建它们。而且注意的是只能在JSP脚本和JSP表达式中使用这些对象,千万不能再JSP声明中使用它们,否则系统会找不到这些变量。

2.7.1 application对象

对于浏览器而言,它通常负责完成三件事情。

  1. 向远程服务器发送请求。
  2. 读取远程服务器返回的字符串数据。
  3. 负责根据字符串数据渲染出一个丰富多彩的页面。

对于服务器而言,它的主要工作是

  1. 启动单独的线程
  2. 使用I/O流读取用户请求的二进制流数据
  3. 从请求数据中解析参数
  4. 处理用户请求
  5. 生成响应数据
  6. 使用I/O流向客户端发送请求数据。

提示:最新版的Tomcat已经不需要对每个用户请求都启用单独的线程,使用普通I/O读取用户请求的数据,最新的Tomcat使用的是异步IO,具有更高的性能。

几乎所有的web服务器都会提供4个类似的Map结构,分别是application,session,request,page,并允许JSP,Servlet将数据保存在这4个结构中,并从这4个结构读取参数,这四个结构只是作用范围不同。JSP中的application,session,request,pageContext这四个内置对象分别用于操作这四个结构的参数。

application 对象代表web应用本身,通常有两个作用。

  • 利用setAttribute()方法和getAttribute()方法在整个web应用的多个JSP,Servlet之间共享数据,
  • 访问web应用的配置参数。

看如下JSP页面,该页面访问数据库,但访问数据库的驱动,URL,用户名及密码都在web.xml中给出。

%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
String driver = application.getInitParameter("driver");
String url = application.getInitParameter("url");
String user = application.getInitParameter("user");
String password = application.getInitParameter("password");
%>
</body>
</html>

 

 

web应用的配置参数在web.xml文件中<context-param.../>配置,每个<context-param.../>配置一个参数,每个配置参数对整个应用有效。

2.7.2 config对象

config对应当前的JSP页面配置信息,但JSP页面通常无需配置,因此也不存在配置信息,JSP页面较少用到这个对象。但在Servlet中则用处比较大,因为Servlet需要在web.xml中进行配置,可以指定配置参数。

2.7.3 Exception 对象

exception对象代表JSP脚本中产生的错误和异常,是JSP页面异常机制的一部分。JSP脚本的代码已经处于try块中,一旦try块捕捉到JSP脚本的异常,就会为该对象处理异常。如果该页面的page指令指定了errorPage属性,则将请求forward到errorPage属性指定的页面,否则使用系统页面来输出异常信息。

在JSP的异常处理机制中,一个异常处理页面可以处理多个JSP页面脚本部分的异常。异常处理页面通过page指令的errorPage属性指定。

例如下面是个简单的异常处理页面

<%@ page contentType="text/html;charset=UTF-8" language="java"  isErrorPage="true" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%=exception.getClass()%>
<%=exception.getMessage()%>
</body>
</html>

可以看出,当page指令的属性isErrorPage的值为true是,该页面就会提供exception对象

2.7.4 out对象

out对象代表一个页面的输出流

2.75 pageContext对象

pageContext对象是pageContext类的实例,它提供了如下两个方法来访问page,request,seesion,application范围的变量

  • getAttribute(String name) 取得page范围内的属性
  • getAttribute(Strig name,int scope) 取得指定范围内的属性,其中scope有如下四个值
  • pageContext.PAGE_SCOPE
  • pageContext.REQUEST_SCOPE
  • pagaContext.SESSION_SCOPE
  • pageContext.APPLICATION_SCOPE

与getAttribute()方法对应,pageConext也提供两个setAttribute()方法来设置四个范围的变量 。

不仅如此,page_Context还可用于获取其他内置对象

  • ServletRequest getRequest():获取request对象
  • ServletResponse getResponse():获取response对象
  • ServletConfig getConfig():获取Config对象
  • ServletContext getContext():获取application对象
  • HttpSession getSession():获取session对象

2.7.6 request对象

request对象时JSP的重要对象,每个request对象都封装一个用户的请求,并且所有的请求参数都被封装在request中。

除此之外,request可代表 本次请求范围。可用于操作request范围的属性。

1.获取请求头/请求参数

  • String getPrarameter(string paramName):获取paramName的请求参数
  • Map getParameterMap():获取所有请求参数名和参数值的map对象
  • Enumeration getParameterNames():获取所有请求参数名的Enumeration对象。
  • String[] getParameterValues(String name):paramName请求参数的值,当该请求参数有多个值时,该方法将返回多个值所组成的数组

HttpServletRequest提供多个方法获取请求头。

  • String getHeader(String name):根据指定请求头的值
  • java.util.Enumeration<String> getHeaderNames():获取所有请求头的名称
  • java.util.Enumeration<String> getHeaders(String name):获取指定请求头的多个值
  • int getIntHeader(String name):获取指定请求头的值,并将该值转化为整数值。

请求头和请求参数都是用户发送到服务器的数据。区别在于请求头通常由浏览器自动添加,因此一次请求总是包含若干请求头,而请求参数则通常需要开发人员控制添加,让客户端发送请求通常有两种方式