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。

原文地址:https://www.cnblogs.com/aidata/p/11980665.html