zoukankan      html  css  js  c++  java
  • JavaWeb(三):JSP

    JSP是JavaServer Page的缩写,也就是服务端网页。

    一、概述

    1.1 为什么使用JSP

    在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。JSP是简化Servlet编写的一种技术,它将Java代码和HTML语句混合在同一个文件中编写,只对网页中的要动态产生的内容采用Java代码来编写,而对固定不变的静态内容采用普通静态HTML页面的方式编写。

    如果仅仅为了少量的动态内容而用Java代码来生成整个网页,会有什么不便的地方?

    程序员真的很难:

    如果使用Servlet程序来输出只有局部内容需要动态改变的网页,其中所有的静态内容也需要程序员用Java程序代码产生,整个Servlet程序的代码将非常臃肿,编写和维护都将非常困难。 对大量静态内容的美工设计和相关HTML语句的编写,并不是程序员所要做的工作,程序员对此也不一定在行。网页美工设计和制作人员不懂Java编程,更是无法来完成这样的工作。 

    使用Java生成HTML代码,这些HTML代码将会包裹在双引号当中,即字符串的形式。代码编辑器无法检查HTML代码,而且会有冗长的行结束符 ,HTML里的引号还需要进行转义。

    示例:

    PrintWriter writer = Response.getWriter();
    writer.append("<!DOCTYPE html>
    ")
          .append("<html>
    ")
          .append("    <body>
    ")
          .append("            "Hello World!"
    ")
          .append("    <ody>
    ")
          .append("</html>
    ");

    于是,可以想到一种混合解决方案,在页面中而不是后台结合Java代码和HTML标签。绝大部分的静态页面采用固定的HTML标签,在HTML标签中嵌入运行Java代码的功能,JSP技术应运而生。

    1.2 运行原理

    JSP本质上是Servlet
    Web容器(Servlet引擎)接收到以.jsp为扩展名的URL的访问请求时,它将把该访问请求交给JSP引擎去处理。Tomcat默认每个JSP 页面在第一次被访问时,JSP引擎将它翻译成一个Servlet源程序,接着再把这个Servlet源程序编译成Servlet的class类文件,然后再由Web容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。
    JSP规范也没有明确要求JSP中的脚本程序代码必须采用Java语言,JSP中的脚本程序代码可以采用Java语言之外的其他脚本语言来编写。但是,JSP页面最终必须转换成Java Servlet程序。

    如果担心第一次访问时编译JSP影响性能,可以在Web应用程序正式发布之前,将所有JSP页面预先编译成Servlet程序。

    JSP工作流程:

    • 客户端发送请求给web容器
    • web容器将jsp首先转译成servlet源代码
    • web容器将servlet源代码编译成.class 文件
    • web容器执行.class 文件
    • web容器将结果响应给客户端

    JSP的编译

    JSP会被翻译成.java放Tomcat/work/Catalina/localhost/***/org/apache/jsp/page,然后编译成.class。一个JSP页面的生成的.java文件如下:

    public class My$jsp extends HttpJspBase { 
    static {}
     public date$jsp() {}
     private static boolean _jspx_inited = falsepublic final void _jspx_init() throws org.apache.jasper.runtime.JspException {};
     public void _JSP pageservice(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { 
    JspFactory _jspxFactory = null; 
    PageContext pageContext = null;
     HttpSession session = null; 
    ServletContext application = null;
     ServletConfig config = null;
     JspWriter out = null; 
    Object page = this; 
    String _value = null;
     try { if (_jspx_inited == false) { 
    synchronized (this) {
     if (_jspx_inited == false) {
     _jspx_init(); _jspx_inited = true;
     }
     } 
    }
     _jspxFactory = JspFactory.getDefaultFactory();
    response.setContentType("text/html;charset=ISO-8859-1");
     pageContext = _jspxFactory.getPageContext(this, request, response, "", true, 8192, true);
     application = pageContext.getServletContext();
     config = pageContext.getServletConfig();
     session = pageContext.getSession();
     out = pageContext.getOut(); 
    // HTML 
    // begin
     [file="/date.jsp";
    from=(0,0);to=(7,6)] out.write("
    
    
    " + "
    
    
    " + "The date is
    ");
     // end 
    // begin
     [file="/date.jsp";from=(7,8);to=(7,57)] out.println((new java.util.Date()).toString());
     // end 
    // HTML 
    // begin
     [file="/date.jsp";from=(7,59);to=(10,7)] out.write("
     
     
    "); 
    // end 
    } catch (Throwable t) {
     if (out != null && out.getBufferSize() != 0) {
     out.clearBuffer(); 
    } if (pageContext != null) {
     pageContext.handlePageException(t); 
    } 
    } finally {
    
     if (_jspxFactory != null) { 
    
    _jspxFactory.releasePageContext(pageContext); 
    
    } 
    } 
    }
    }

    最重要的函数就是pageservice,web容器在编译好一个JSP类以后,就申请这个类的对象,并且直接调用pageservice来获得Response,最后返回给客户。

    所有的JSP页面翻译出来的class,都从HttpJspBase继承,并且命名为PageName$jsp。在第一次调用pageservice函数的时候,该class会进行一次初始化,而这个初始化函数是_jspx_init,我们还可以自定义这个函数来实现JSP页面的初始化。
    HTML代码直接被写到PrintWriter里面回馈给用户。
    为什么JSP页面有那么多省写方式,比如说session , out , page , context之类。
    这都是在pageservice里面定义的临时变量,每一次调用JSP页面,这些变量都会被重新初始化一次。当然我们也可以方便的声明自己的变量。
    指令只是一个一个的对应为response.setContentType()的语句而已。
    JSP页面转向这个语句被翻译为getServletContext().getRequestDispatcher("/List.jsp").forward(req, res);语句。

    二、创建一个JSP

    概览

    模板元素

    JSP页面中的静态HTML内容称之为JSP模板元素,在静态的HTML内容中可以嵌套JSP的其他各种元素来产生动态内容和执行业务逻辑JSP模板元素定义了网页的基本骨架,即定义了页面的结构和外观。

    所有JSP中已经隐含式地包含了一个标签库,那就是JSP标签库(前缀为jsp),使用它不用在JSP中添加taglib指令(不过需要为jsp标签添加XMLNS声明)。

    XMLNS https://baike.baidu.com/item/xmlns/5866334?fr=aladdin

    几个JSP常用标签:

      <jsp:include>标签
      <jsp:forward>标签
      <jsp:param>标签
      <jsp:useBean>标签
      <jsp:setProperty>标签
      <jsp:getProperty>标签

    后面会介绍Java标准标签库

    标签语法:

    <prefix:tagname[ attribute=value[ attribute=value [...] ] ] />
    
    <prefix:tagname[ attribute=value[ attribute=value [...] ] ] >
        content
    </prefix:tagname>

    prefix表示JSP标签库前缀,也被称为命名空间(XML术语),tagname是TLD中定义的标签名称。特性值将使用单引号或者双引号括起来,特性之间需要空白。

    几种结构

    <%@ 这是一个指令 %>
    <%! 这是一个声明 %>
    <% 这是一个脚本 %>
    <%= 这是一个表达式 %>

    指令用于指示JSP解释器执行某个操作(例如设置内容类型)或者对文件作出假设(例如使用的是哪种脚本语言)、导入类、在转换时包含其他JSP或者包含JSP标签库。

    声明用于在JSP Servlet类的范围内声明一些东西,例如可以定义实例变量、方法或声明标签中的类。要记住:这些声明都将出现在自动生成的JSP Servlet类中,所以声明中定义的类实际上是JSP Servlet类的内部类。声明中的代码将在转换时被复制到JSP Servlet类的主体中,并且它们可用于声明某些字段、类型或方法。

    脚本中也包含Java代码,但不同于声明,脚本有不同的作用域,脚本被复制到_jspService方法(Tomcat 8.0)中,而非Servlet类的主体中(即和_jsService方法地位相同)。_jspService方法中的所有局部变量都可以在脚本中使用,任何在该方法体中合法的代码在脚本中也是合法的。所以,脚本中定义的是局部字段而非实例字段。还以在脚本中使用条件语句、操作对象和执行数学计算,这些在声明中都无法完成。我们甚至可以在脚本中定义类,这些类是_jspService方法中有效。声明中定义的类、方法或变量都可以在脚本中使用,但脚本中定义的类或变量不能在声明中使用

    表达式包含了一些简单的Java代码,可用于向客户端输出一些内容,它将把代码的返回值变量输出到客户端。任何赋值表达式的右侧都可以用在表达式中,表达式的作用域与脚本相同,如同脚本一样,表达式也将被复制到_jspServlet方法中

    映射JSP

    JSP就是一个Servlet,因此我们在web.xml文件中也可以使用<servlet>标签和<servlet-mapping>标签来映射JSP,使JSP能有一个虚拟目录(对外访问路径)。 

     <servlet>
       <servlet-name>myjsp</servlet-name>
       <jsp-file>/MyJsp.jsp</jsp-file>
     </servlet>
    
     <servlet-mapping>
       <servlet-name>myjsp</servlet-name>
       <url-pattern>/myhtml.html</url-pattern>
     </servlet-mapping>

    注释 

    在JSP中可以使用4种注释:

    • XML注释:  <!-- 这是注释 -->
    • 传统的Java注释:  // 这是注释
    • 传统的Java块注释:  /* 这是注释 */
    • JSP注释: <%-- 这是JSP注释 --%>

    2.1 声明

    JSP声明将Java代码封装在<%!和 %>之中,它里面的代码将被插入进Servlet的_jspService方法的外面,所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法
    多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
    JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。

    <%!
    void test(){}
    %>

    几乎不用

    2.2 脚本——在JSP中使用Java代码(不推荐)

    JSP脚本片断(scriptlet)是指嵌套在<% 和 %>之中的一条或多条Java程序代码。
    在JSP脚本片断中,可以定义变量、执行基本的程序运算、调用其他Java类、访问数据库、访问文件系统等普通Java程序所能实现的功能。
    JSP脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService方法中,所以,JSP脚本片断之中只能是符合Java语法要求的程序代码,除此之外的任何文本、HTML标记、其他JSP元素都必须在脚本片断之外编写。
    JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每条命令执行语句后面必须用分号(;)结束。
    在一个JSP页面中可以有多个脚本片断(每个脚本片断代码嵌套在各自独立的一对<% 和 %>之间),在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。

    <%
        int x = 3;
    %>
    <p>这是一个HTML段落</p>
    <%
        out.println(x);
    %>

    for

    <%for (int i=1; i<5; i++) {%>
      <H<%=i%>>www.it315.org</H<%=i%>>
    <%}%>

    if...else...

    <%if(java条件表达式){%>
      其他元素
    <%}else{%>
      其他元素
    <%}%>

    隐含变量

    隐含变量是不用定义或声明就能使用的变量,可以在脚本和表达式中使用request、response、pageContext、session、application、config、out、page这8个隐含对象,实际上还可以使用一个叫exception的隐含对象。

    JSP规范要求JSP的转换器和编译器提供这些变量,并且名字也要完全相同。这些隐含变量实际定义在JSP执行的Servlet方法(Tomcat 8.0中是_jspServlet方法)的开头。因为隐式的变量定义在_jspServlet中,所以定义在类中的声明不能使用它们。表达式也在_jspServlet中,可以使用。

    下面我们新建了一个空白的JSP文件,访问该文件,在Tomcat中生成了java文件和编译的.class文件

    tomcat_homeworkCatalinalocalhostproject_nameorgapache找到java文件:

      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;
        javax.servlet.http.HttpSession session = null;
        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, true, 8192, true);
          _jspx_page_context = pageContext;
          application = pageContext.getServletContext();
          config = pageContext.getServletConfig();
          session = pageContext.getSession();
          out = pageContext.getOut();
          _jspx_out = out;
    
          out.write("
    ");
          out.write("
    ");
          out.write("<!DOCTYPE html>
    ");
          out.write("<html>
    ");
          out.write("<head>
    ");
          out.write("<meta charset="UTF-8">
    ");
          out.write("<title>Insert title here</title>
    ");
          out.write("</head>
    ");
          out.write("<body>
    ");
          out.write("
    ");
          out.write("    ");
    
          Person person = new Person();
          System.out.println(person.getPersonInfo());
        
          out.write("
    ");
          out.write("    
    ");
          out.write("</body>
    ");
          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);
        }
      }

    request:HttpServletRequest的一个对象
    response:HttpServletRespons的一个对象,在JSP页面中几乎不用
    pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取到其他8个隐含对象,也可以从中获取到当前页面的其他信息,后面自定义标签时使用

    HttpServletRequest req = pageContext.getRequest();
    System.out.println(req == request);

    结果为true

    session:代表浏览器和服务器的一次会话,是HttpSession的一个对象。
    获得会话的ID

    System.out.println(session.getId());

    application:代表当前Web应用,是ServletContext对象
    config:是当前JSP对应的Servlet的ServletConfig对象,几乎不用

    out:JspWriter对象,调用out.println()可以直接把字符串打印到浏览器上

    out.println("hello");
    out.println("<br>"); 换行
    out.println("world");

    page:指向当前JSP对应的Servlet对象的引用,但为Objetc类型,只能调用Object类的方法,几乎不用

    exception有下面的声明才能用
    <%@ page isErrorPage="true" %>

    pageContext、request、 session、 application 对属性的作用域的范围从小到大

    在application、session、request、pageContext对象中都可以调用setAttribute方法getAttribute方法来设置和检索各自域范围内的属性。这四个对象也称为域对象。
    存储在application对象中的属性可以被同一个Web应用程序中的所有Servlet和JSP页面访问。
    存储在session对象中的属性可以被属于同一个会话的所有Servlet和JSP页面访问,浏览器打开直到关闭是一次会话(会话未失效的前提下)。
    存储在request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问,例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。
    存储在pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。
    PageContext类中还提供了对各个域范围的属性进行统一管理的方法,以简化对各个域范围内的属性的访问。
    setAttribute方法 设置属性

    public void setAttribute(java.lang.String name,java.lang.Object value)
    public void setAttribute(java.lang.String name,java.lang.Object value,int scope)

    常量

    PageContext.APPLICATION_SCOPE
    PageContext.SESSION_SCOPE
    PageContext.REQUEST_SCOPE
    PageContext.PAGE_SCOPE

    getAttribute方法 获取指定的属性

    public java.lang.Object getAttribute(java.lang.String name)
    public java.lang.Object getAttribute(java.lang.String name,int scope)

    removeAttribute方法

    public void removeAttribute(java.lang.String name)
    public void removeAttribute(java.lang.String name,int scope)

    getAttributeNamesInScope方法
    findAttribute方法 (*)

    设置

    <%
    pageContext.setAttribute("pageContextAttr", "pageContextValueAttr");
    request.setAttribute("requesAttr", "requesAttr");
    session.setAttribute("sessionAttr", "sessionAttr");
    application.setAttribute("applicationAttr", "applicationAttr");
    %>

    获取

    pageContextAttr:<%= pageContext.getAttribute("pageContextValueAttr"); %>
    requestAttr:<%= request.getAttribute("requesAttr"); %>
    sessionAttr:<%= session.getAttribute("sessionAttr"); %>
    applicationAttr:<%= application.getAttribute("applicationAttr"); %>

    2.3 使用指令

    JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分
    JSP指令的基本语法格式

    <%@ 指令 属性名="值" %>

    举例:

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

    注意:属性名部分是大小写敏感的
    在JSP 2.0中,定义了pageincludetaglib这三种指令,每种指令中又都定义了一些各自的属性。
    如果要在一个JSP页面中设置同一条指令的多个属性,可以使用多条指令语句单独设置每个属性,也可以使用同一条指令语句设置该指令的多个属性。
    第一种方式:

    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="java.util.Date"%>

    第二种方式:

    <%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%> 

    2.3.1 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" ] 指定当前JSP页面的响应类型(前面word文档方式的例子),实际调用的是respons.setContentType()方法,对于JSP而言一般取text/html;charset=UTF-8 ,即html类型文件,字符编码为utf-8
    
    [ pageEncoding="characterSet | ISO-8859-1" ] 指定当前JSP页面的字符编码,通常与contentType中charset一致。
    
    [ isELIgnored="true | false" ] 指当前JSP页面是否可以使用EL表达式,通常取值为true
    %>

    导入类

    无论何时在JSP包中包含直接使用类的Java代码,该JSP要么使用完全限定类名,要么在JSP文件中添加一条导入命令。

    对于不产生输出的JSP标记、指令、声明和脚本,它们将会在客户端输出一行空白。所以,如果在变量声明和脚本之前有许多导入类的page指令,那么将会在输出中显示出数行空白。为了解决这个问题,通常会将一个标记的尾部与另一个标记的头部连接在一起:

    <%@ page import="java.util.Map"
    %><%@ page import="java.util.List"
    %><%@ page import="java.util.IOException" %>

    错误页

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

    当前页面如果出错,会自动把error.jsp当作出错页面
    在erro.jsp这个错误页面里可以添加如下,以显示错误信息,用到了转发,因为错误信息实际上是在跳转到error.jsp之前的那个页面的

    Error Message:<%= exception.getMessage() %>

    但是,要想使用上面的exception这个隐含对象,必须在网页里添加

    <%@page isErrorPage="true" %>

    errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面。
    可以在web.xml文件中使用<error-page>元素为整个WEB应用程序设置错误处理页面,其中的<exception-type>子元素指定异常类的完全限定名,<location>元素指定以“/”开头的错误处理页面的路径。

    <error-page>
    <!--指定出错的代码:404没有指定的资源 500内部错误-->
        <error-code>404<error-code>
        <!--指定响应页面的位置-->
        <location>/WEB-INF/erro.jsp</location>
    </error-page>
    
    <error-page>
    <!--指定异常的类型-->
        <excepttion-type>java.lang.ArithmeticException<error-code>
        <location>/WEB-INF/erro.jsp</location>
    </error-page>

    如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。

    在响应error.jsp时,JSP引擎使用的是请求转发的方式,
    如何时客户不直接访问某一个页面?一般情况下,对于Tomcat服务器而言,WEB-INF下的文件是不能通过在浏览器中直接输入来访问的,但通过请求的转发是可以的

    contentType

    JSP引擎会根据page指令的contentType属性生成相应的调用ServletResponse.setContentType方法的语句。
    page指令的contentType属性还具有说明JSP源文件的字符编码的作用。

    2.3.2 包含其他JSP

    include指令用于通知JSP引擎在翻译当前JSP页面时将其他文件中的内容合并进当前JSP页面转换成的Servlet源文件中,这种在源文件级别进行引入的方式称之为静态引入,当前JSP页面与静态引入的页面紧密结合为一个Servlet。
    语法:

    <%@ include file="relativeURL"%>

    其中的file属性用于指定被引入文件的相对路径。
    b.jsp

    <body>
    <h4>BBB PAGE</h4>
    </body>

    a.jsp

    <body>
    <h4>AAA PAGE</h4>
    <!--在a.jsp中包含b.jsp-->
    <%@ include file="b.jsp" %>
    </body>

    访问a.jsp,结果如下:

    被引入的文件必须遵循JSP语法,其中的内容可以包含静态HTML、JSP脚本元素、JSP指令和JSP行为元素等普通JSP页面所具有的一切内容。
    被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。
    在将JSP文件翻译成Servlet源文件时,JSP引擎将合并被引入的文件与当前JSP页面中的指令元素(设置pageEncoding属性的page指令除外)。所以,除了import和pageEncoding属性之外,page指令的其他属性不能在这两个页面中有不同的设置值。
    除了指令元素之外,被引入的文件中的其他元素都被转换成相应的Java源代码,然后插入进当前JSP页面所翻译成的Servlet源文件中,插入位置与include指令在当前JSP页面中的位置保持一致。
    引入文件与被引入文件是在被JSP引擎翻译成Servlet的过程中进行合并,而不是先合并源文件后再对合并的结果进行翻译。当前JSP页面的源文件与被引入文件的源文件可以采用不同的字符集编码,即使在一个页面中使用page指令的pageEncoding或contentType属性指定了其源文件的字符集编码,在另外一个页面中还需要用page指令的pageEncoding或contentType属性指定其源文件所使用的字符集 。
    Tomcat 5.x在访问JSP页面时,可以检测它所引入的其他文件是否发生了修改,如果发生了修改,则重新编译当前JSP页面
    file属性的设置值如果使用相对路径,表示相对于当前文件所在目录开始定位包含文件。如果以“/”开头,使用绝对路径,表示从Web应用程序的根目录开始定位该文件。

    举例

    下面都是相对路径,因此都是a.jsp同级目录下找b.jsp

    假设myweb应用程序的根目录下有一个a.jsp文件,其一般的访问路径形式为:http://localhost:8080/myweb/a.jsp
    在a.jsp页面中使用了如下语句引入b.jsp文件:

    <%@ include file=“b.jsp”%>

    这时候JSP引擎调用的b.jsp文件的完整URL路径为什么?

    http://localhost:8080/myweb/b.jsp

    如果将a.jsp页面映射为如下地址:http://localhost:8080/myweb/dir1/a.html
    这时候JSP引擎调用的b.jspf文件的完整URL路径为:
    http://localhost:8080/myweb/dir1/b.jsp

    2.3.3 包含标签库

     如果希望JSP中使用标签库中定义的标签,使用taglib指令引用该标签库即可。

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

    2.4 使用<jsp>标签

    JSP还提供了一种称之为Action的元素,在JSP页面中使用Action元素可以完成各种通用的JSP页面功能,也可以实现一些处理复杂业务逻辑的专用功能。 Action元素采用XML元素的语法格式,即每个Action元素在JSP页面中都以XML标签的形式出现。JSP规范中定义了一些标准的Action元素,这些元素的标签名都以jsp作为前缀,并且全部采用小写,例如,<jsp:include>、<jsp:forward>等等。

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

    <jsp:include>标签与include指令

    <jsp:include>标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。
    语法:

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

    page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
    flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。

    <!--在a.jsp中包含b.jsp-->
    <jsp:inclucde page="b.jsp"></jsp:include>

    动态引入:并不像include指令生成一个Servlet源文件,而是生成两个Servlet源文件,然后通过一个方法把目标页面包含进来。

    <jsp:include>标签是在当前JSP页面的执行期间插入被引入资源的输出内容,当前JSP页面与被动态引入的资源是两个彼此独立的执行实体,被动态引入的资源必须是一个能独立被WEB容器调用和执行的资源。include指令只能引入遵循JSP格式的文件,被引入文件与当前JSP文件共同合被翻译成一个Servlet的源文件。
    使用<jsp:include>标签和include指令都可以把一个页面的内容分成多个组件来生成,开发者不必再把页眉和页脚部分的相同HTML代码复制到每个JSP文件中,从而可以更轻松地完成维护工作,但是都应注意最终的输出结果内容应遵循HTML语法结构,例如,如果当前页面产生了<html>、</html>、<body>、</body>等标记,那么在被引入文件中就不能再输出<html>、</html>、<body>、</body>等标记。
    <jsp:include>标签对JSP引擎翻译JSP页面的过程不起作用,它是在JSP页面的执行期间才被调用,因此不会影响两个页面的编译。由于include指令是在JSP引擎翻译JSP页面的过程中被解释处理的,所以它对JSP引擎翻译JSP页面的过程起作用,如果多个JSP页面中都要用到一些相同的声明,那么就可以把这些声明语句放在一个单独的文件中编写,然后在每个JSP页面中使用include指令将那个文件包含进来。

    <jsp:include>标签使用page属性指定被引入资源的相对路径,而include指令使用file属性指定被引入资源的相对路径。

     

    <jsp:include>和<%@include%>的区别

    <jsp:forward>标签

    用于把请求转发给另外一个资源。
    语法:

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

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

    <jsp:forward page="/include/b.jsp"></jsp:forward>

    相当于

    <%
    request.getRequestDispatcher("/include/b.jsp").forward(request, response);
    %>

    RequestDispatcher.forward方法、PageContext.forward方法、<jsp:forward>标签的区别
    调用RequestDispatcher.forward方法的JSP脚本代码的前后不能有JSP模版内容。
    调用PageContext.forward方法的JSP脚本代码的后面不能有JSP模版内容。
    <Jsp:forward>标签的前后都能有JSP模版内容。

    <jsp:param>标签

    当使用<jsp:include>和<jsp:forward>标签引入或将请求转发给的资源是一个能动态执行的程序时,例如Servlet和JSP页面,那么,还可以使用<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:forward>

    <jsp:param>标签的name属性用于指定参数名,value属性用于指定参数值。在<jsp:include>和<jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数。

    a.jsp

    <jsp:forward page="/include/b.jsp">
    <jsp:param name="username" value="abcd" />
    </jsp:forward>

    b.jsp

    <%= request.getParameter("username") %>

    b.jsp可以获取a.jsp的username参数

    三、结合使用Servlet和JSP

    3.1 转发和重定向

    本质区别:请求的转发只发出了一次请求,而重定向则发出了两次请求。
    请求的转发地址栏是初次发出请求的地址,重定向地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址。
    请求转发:在最终的Servlet中,request对象和中转的那个request是同一个对象,而请求的重定向不是同一个对象。

    Servlet实例

    转发Servlet

    public class ForwardServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
            System.out.println("ForwardServlet's doGet.");
    //        请求的转发:
    //        1.调用HttpServletRequest的getRequestDispatcher()方法获取RequestDispatcher对象
    //        调用getRequestDispatcher()需要转入要转发的地址
    //        2.调用HttpServletRequest的forward(request, response)进行请求的转发
            String path = "testServlet";
            RequestDispatcher requestDispatcher = request.getRequestDispatcher("/"+path);
            requestDispatcher.forward(request, response);
        }
    }

    重定向Servlet

    public class RedirectServlet extends HttpServlet {
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
            System.out.println("RedirectServlet's doGet");
            //执行重定向,直接调用response.sendRedirect(path)方法
            //path为重定向的地址
            String path = "testServlet";
            response.sendRedirect(path);
        }
    }

    定义一个用于测试的Servlet

    public class TestServlet extends HttpServlet {
        
        private static final long serialVersionUID = 1L;
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("TestServlet's doGet 方法. ");
        }
    
    }

    配置web.xml

    <servlet>
        <servlet-name>forwardServlet</servlet-name>
        <servlet-class>com.atguigu.javaweb.ForwardServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>forwardServlet</servlet-name>
        <url-pattern>/forwardServlet</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>redirectServlet</servlet-name>
        <servlet-class>com.atguigu.javaweb.RedirectServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>redirectServlet</servlet-name>
        <url-pattern>/redirectServlet</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>com.atguigu.javaweb.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/testServlet</url-pattern>
    </servlet-mapping>

    HTML

    test.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <a href="loginServlet">Test</a>
    <br><br>
    
    
    <a href="forwardServlet">Forward</a>
    <a href="redirectServlet">Redirect</a>
    </body>
    </html>

    test.html浏览器页面如下

     点击Forward,地址栏是该按钮指向的地址

    控制台打印:

    ForwardServlet's doGet.
    TestServlet's doGet方法

    说明执行了TestServlet

    点击Redirect,地址栏不是该按钮指向的地址

    控制台打印:

    ForwardServlet's doGet.
    TestServlet's doGet方法

    说明执行了TestServlet

    都执行了TestServlet
    但是定向没有跳转到TestServlet对应的网址
    而重定向跳转到了TestServlet对应的网址

    RequestDispatcher实例对象

    RequestDispatcher实例对象是由Servlet引擎创建的,它用于包装一个要被其他资源调用的资源(例如,Servlet、HTML文件、JSP文件等),并可以通过其中的方法将客户端的请求转发给所包装的资源。 

    RequestDispatcher接口中定义了两个方法:forward方法和include方法。
    forward和include方法接收的两个参数必须是传递给当前Servlet的service方法的那两个ServletRequest和ServletResponse对象,或者是对它们进行了包装的ServletRequestWrapper 或ServletResponseWrapper对象。
    获取RequestDispatcher对象的方法:
    ServletContext.getRequestDispatcher (参数只能是以“/”开头的路径)
    ServletContext.getNamedDispatcher
    ServletRequest.getRequestDispatcher (参数可以是不以“/”开头的路径)

    用sendRedirect方法实现请求重定向
    sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,它还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。如果传递给sendRedirect 方法的相对URL以“/”开头,则是相对于整个WEB站点的根目录,而不是相对于当前WEB应用程序的根目录。


    请求重定向与请求转发的比较
    RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
    如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
    调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
    HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。
    RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。

    3.2 JSP属性和JSP属性组 

    3.2 将Servlet请求转发给JSP

     由Servlet接收请求,实现业务逻辑处理以及必需的数据存储或读取,创建可以由JSP轻松处理的数据模型,最终将请求转发给JSP。

  • 相关阅读:
    ES6模块
    遍历数组和对象的方法
    JVM知识(六):linux环境下查看JVM内存大小
    WebSocket实时消息推送
    SpringBoot中基于Mybatis多数据源操作
    浅谈Redis中的雪崩和穿透及击穿。
    Hibernate与Mybatis的区别
    Java动态代理和反射机制
    JSON对象和JSON字符串的区别
    JVM知识(五):垃圾回收算法
  • 原文地址:https://www.cnblogs.com/aidata/p/11980665.html
Copyright © 2011-2022 走看看