初学JSP

时间:2024-01-22 15:20:43

一. 基本了解

    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):获取指定请求头的值,并将该值转化为整数值。

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

  • GET 方式的请求:直接在浏览器地址栏输入访问地址所发送的请求或提交表单时没有指定method属性为post发送的请求。GET方式的请求会将请求参数的名和值转换成字符串,并附加在原URL之后,因此可以在地址栏中看到请求参数名和值。且GET请求传送的数据量较小,一般不能大于2KB。
  • POST方式的请求:这种方式通常用于提交表单的方式发送,且需要设置form元素的method属性为post.POST方式发送的数据比较大。而且post方式发送的请求参数以及对应的值放在HTML HEADER中传输,用户不能在地址栏看到请求参数值,安全性相对较高。

表单域和请求参数的关系

  • 每个有name属性的表单域对应一个请求参数
  • 如果有多个表单域有相同的name属性,则多个表单域只生成多个值的请求参数
  • 表单域的name属性指定请求参数名,value指定请求参数值
  • 如果某个表单设置了disabled = "diasbled" 属性,则该表单与不在生成请求参数。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>收集参数的表单页</title>
</head>
<body>
<form id="form1" method="post" action="request1.jsp">
    用户名:<br/>
    <input type="text" name="name"/><hr/>
    性别:<br>
    男:<input type="radio" name="gender" value="男"/>
    女:<input type="radio" name="gender" value="女"/><hr/>
    喜欢的颜色:<br/>
    红:<input type="checkbox" name="color" value="红"/>
    绿:<input type="checkbox" name="color" value="绿"/>
    蓝:<input type="checkbox" name="color" value="蓝"/><hr/>
    来自的国家:<br/>
    <select name="county">
        <option value="中国">中国</option>
        <option value="美国">美国</option>
        <option value="俄罗斯">俄罗斯</option>
    </select><hr/>
    <input type="submit" value="提交"/>
    <input type="reset" value="重置"/>
</form>
</body>
</html>

上面的表单页面向request.jsp发送请求,request.jsp页面代码如下

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>获取请求头/请求参数</title>
</head>
<body>
<%
    Enumeration<String> headerNames = request.getHeaderNames();
    while(headerNames.hasMoreElements()){
        String headerName = headerNames.nextElement();
        out.println(headerName +"-->"+request.getHeader(headerName)+"<br/>");
    }
    out.println("<hr/>");
    request.setCharacterEncoding("UTF-8");
    String name = request.getParameter("name");
    String gender = request.getParameter("gender");
    String[] color = request.getParameterValues("color");
    String country = request.getParameter("country");
%>
     您的名字:<%=name%><hr/>
     您的性别:<%=gender%><hr/>
     您喜欢的颜色:<%=gender%><hr/>
     您的来自的国家:<%=country%><hr/>
</body>
</html>

结果图如下:

 如果表单西欧字符,可借助java.net.URLDecoder的decode()方法来解码。

2.操作request范围的属性

 主要依靠setAttribute(String attriName,Object attriValue)方法和Object getAttribute(String attriName)两个方法。

3.执行forward和include

例如:

getRequestDispatcher("/a.jsp").forward(request,response);
getRequestDispatcher("/a.jsp").include(request,response);

forward用户请求时,用户请求参数和request不会丢失,即forward还是原来的请求。

使用request的getDispatcher(String path)方法时,path参数必须以“/”开头

2.7.7 response对象

response代表服务器的响应。大部分时候,程序无需使用response响应客户端请求。完全有个更简单的out对象,是writer的实例,他代表输出流。但是writer是字符流。无法输出非字符内容。

还可以使用response来重定向请求,以及用于向客户端增加cookie.

1.response生成非字符响应

response对象实现了HTTPServletResponse接口,该接口提供了一个getOutputStream()方法,该方法返回响应输出字节流

<%@ page contentType="image/png" language="java" %>
<%@ page import="java.awt.image.BufferedImage" %>
<%@ page import="java.awt.*" %>
<%@ page import="javax.imageio.ImageIO" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    BufferedImage image = new BufferedImage(340,160,BufferedImage.TYPE_INT_BGR);
    Graphics g = image.getGraphics();
    g.fillRect(0,0,400,400);
    g.setColor(new Color(255,0,0));
    g.fillArc(20,20,100,100,30,120);
    g.setColor(new Color(0,255,0));
    g.fillArc(20,20,100,100,150,120);
    g.setColor(new Color(0,0,255));
    g.fillArc(20,20,100,100,270,120);
    g.setColor(new Color(0,0,0));
    g.setFont(new Font("Arial Black",Font.PLAIN,16));

    g.drawString("red:climb",200,60);
    g.drawString("green:swim",200,100);
    g.drawString("blue:jump",200,140);
    g.dispose();

    ImageIO.write(image,"png",response.getOutputStream());


%>
</body>
</html>

当然也可以使用img标签来显示这张图片,如下

<img src="img.jsp">

2.重定向

 重定向是response另一个用处,与forward不同的是,重定向会丢失所有的请求参数和request范围的属性,因为重定向将生成第二次请求,与前一次请求不在同一个request范围内,所以发送一次的请求参数和request范围将会丢失。

HTTPServletResponse 提供了一个sendRedirect(String path)方法,该方法用于重定向到path资源,即重新向path发送请求。

执行重定向是地址栏的URL会编程目标页的URL

3.增加cookie

cookie用于网站记录用户的的某些信息,比如客户的用户名及用户的喜好等等。一旦用户下次登录,网站可以获取到用户的喜好,为客户提供更好的服务。与session不同的是:session会随浏览器的关闭而失效,但cookie会一直存放在客户端机器上,除非超出cookie的生命期限

出于安全性的原因,使用Cookie客户端浏览器必须支持cookie才可以,客户端浏览器完全可以设置禁用cookie

增加cookie也是使用response内置对象完成的,response提供了如下方法

void addCookie(Cookie cookie):增加cookie

在增加cookie之前,必须先创建cookie对象,按如下步骤:

  1. 创建Cookie实例,Cookie的构造器为Cookie(String name,String Value).
  2. 设置Cookie的生命期限,即Cookie在多长时间内有效。否则Cookie会随浏览器的关闭自动消失
  3. 向客户端写Cookie

访问客户端cookie使用request对象,request提供了getCookies()方法,该方法返回客户端机器上所有Cookie组成的数组,遍历数组,找到希望访问的cookie即可

默认情况下,Cookie不允许出现中文字符,如果需要中文字符的Cookie,可以借助于java.net.URLEncoder先对字符串编码,然后将编码后的结果设为cookie值,当成需要读取Cookie时,则应该先读取,然后使用java.net.URLEncoder对其进行解码。

<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
Cookie c = new Cookie("cnName",java.net.URLDecoder.encode("孙悟空","gbk"));
c.setMaxAge(24*3600);
response.addCookie(c);

Cookie[] cookies = request.getCookies();
    for (Cookie cookie:cookies) {
        out.println(java.net.URLDecoder.decode(cookie.getValue()));

    }

%>
</body>
</html>

2.7.8 session对象

session对象代表一次用户会话。一次用户会话的含义是用户客户端连接服务器开始,到客户端浏览器与服务器断开为止,这个过程就是一次会话。

session会话用于跟踪用户的会话信息,如判断用户是否登陆系统,或者在购物车应用中,用于跟踪用户购买的商品等。session范围的属性可以在多个页面跳转之间共享。一旦关闭浏览器,即Session结束,session范围内的属性将全部丢失。

session常用的方法有setAtttribute()和getAttribute();

关于session的一点还需要指出,session机制通常用于保存客户端的状态信息,这些状态信息需要保存到Web服务器的硬盘上,所以要求session里的属性值必须是可序列化的,否则引发不可序列化的异常。