JavaWeb学习笔记(五)之JSP

JSP基础

JSP(Java Server page)技术

静态HTML文件简洁直观表达网页外观,但无法实现交互;Servlet可以实现动态交互,但开发人员必须通过编写Java代码的方式输出HTML文档,繁琐且难以理解,尤其对于内容庞大且布局复杂的网页。JSP技术集合了HTMLServlet的优点,大大简化了动态生成网页的工作。

JSP本质
JSP本质就是一个Servlet类。
JSP页面被访问时,容器按照以下流程处理客户请求

1)查找与JSP文件对应的Servlet,如果已经存在,就调用它的服务方法。

2)如果与JSP文件对应的Servlet不存在,就解析文件系统中的JSP文件,将它翻译成Servlet源文件,再将源文件编译成.class文件,然后再创建该Servlet对象,初始化,并运行Servlet

Tomcat将由JSP翻译生成的.java文件以及.java编译之后的.class文件存放于work目录下。通常情况下,开发和调试Web应用阶段,如果修改了JSP文件,Tomcat会重新翻译JSP,编译生成的新文件(一般容器会自动检测更新,继而自动生成新的Servlet源文件并进行编译,然后再运行新生成的Servlet)。覆盖work目录下的原来旧文件。如果浏览器仍然看到的是旧网页,可手工删除work目录下相关信息,确保Tomcat重新编译修改后的JSP文件。

index.jsp文件生成的index_jsp.java文件如下。

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase

    implements org.apache.jasper.runtime.JspSourceDependent {

  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 javax.el.ExpressionFactory _el_expressionfactory;

  private org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {

    return _jspx_dependants;

  }

  public void _jspInit() {

    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();

    _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());

  }

  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 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=ISO-8859-1");

      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("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");

out.write("<html>\r\n");

      out.write("  <head>\r\n");

      out.write("    <base href=\"");

      out.print(basePath);

      out.write("\">\r\n");

      out.write("    \r\n");

      out.write("    <title>My JSP 'index.jsp' starting page</title>\r\n");

      out.write("\t<meta http-equiv=\"pragma\" content=\"no-cache\">\r\n");

      out.write("\t<meta http-equiv=\"cache-control\" content=\"no-cache\">\r\n");

      out.write("\t<meta http-equiv=\"expires\" content=\"0\">    \r\n");

      out.write("\t<meta http-equiv=\"keywords\" content=\"keyword1,keyword2,keyword3\">\r\n");

      out.write("\t<meta http-equiv=\"description\" content=\"This is my page\">\r\n");

      out.write("\t<!--\r\n");

      out.write("\t<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\">\r\n");

      out.write("\t-->\r\n");

      out.write("  </head>\r\n");

      out.write("  \r\n");

      out.write("  <body>\r\n");

      out.write("    This is my JSP page. <br>\r\n");

      out.write("  </body>\r\n");

out.write("</html>\r\n");

    } catch (java.lang.Throwable t) {

      if (!(t instanceof javax.servlet.jsp.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);

        else throw new ServletException(t);

      }

    } finally {

_jspxFactory.releasePageContext(_jspx_page_context);

    }

  }

}

 

JSP语法
JSPhtml(*.html,*.htm一般前端设计人员设计),Java程序片段(JavaScriptlet也叫Java脚本), JSP标记组成。

JSP虽然是一个Servlet,但有着不同于Java编程语言的专门的语法,该语法特点尽可能地用标记取代Java程序代码,使整个JSP文件在形式上不像Java程序,而像标记文档。

JSP语法组成:HTML文本,JSP指令,JSP声明, Java程序片段,Java表达式,JSP隐含对象,JSP注释。

Ø  HTML(查看Servlet类对应的代码)
JSP本身是将其他语法表达嵌入到HTML中。JSPHTML文本称为模板文本,最终会在对应Servlet类的service方法中,由out.write()原样输出。模板文本还可以为纯文本。可通过设置<%@page  contentType=”text/plain”%>标识JSP模板文本为纯文本。

Ø  JSP声明(查看Servlet类对应的代码)
JSP声明在<%!       %>中定义,用于声明与JSP对应的Servlet类的成员变量和方法。

    案例演示:

         注意:每个JSP声明只在当前JSP文件有效,如果希望在多个JSP文件中都包

含这些声明,可以把这些声明语句写到一个单独的JSP文件中,然后在其他JSP

文件中用include指令将该文件包含即可。

Ø  Java程序片段(查看Servlet类对应的代码)
JSP文件中,可以在<%     %>标记间直接嵌入Java程序代码。
如果在page指令中没有指定method属性,嵌入的Java代码默认在与JSP对应的Servlet类的service方法中。

     案例:

Ø  jsp表达式(查看Servlet类对应的代码)
Java表达式标记为:<%=    %>。等同于out.write
JSP中使用该标记,它能把表达式的值输出到网页上。表达式中的intfloat类型的值都自动转换成字符串进行输出。

Ø  JSP指令(Directive) (查看Servlet类对应的代码)
JSP指令语法格式: <%@ 指令名  attribute=”value”…%> 用来设置和整个JSP网页相关的属性,比如网页的编码方式和脚本语言等。一般都会把JSP指令放到JSP文件的开头(不是必须的)

JSP常用的三大指令为page, include, taglib

²  page指令
最常用的指令,涉及的属性也很多。在JSP页面中,指令都可以多次出现

1)page指令的language属性
<%@ page  language=”java”%>,指定文件中的程序代码所使用的编码语言,目前java是唯一的有效值和默认值。该指令作用于整个文件。多次使用该指令,只有第一次使用有效。

2)page指令的import属性
<%@ page import=”java.io.*,java.util.*”%>

<%@ page import=”java.io.*”%>
<%@ page import=”java.net.*”%>
指定导入的java软件包名或类名列表,该列表使用逗号分隔。可多次使用该指令导入不同的软件包。import属性是唯一可以重复出现的属性。
对应Servlet中,import语句。

3)page指令的contentTypepageEncoding属性
<%@ page  contentType="text/html;charset=utf-8"%>对应Servletresponse.setContentType("text/html; charset=utf-8")用于设置响应字符流的编码和MIME响应头
<%@ page pageEncoding=”utf-8”%>指定当前JSP页面的编码。该编码给服务器看,服务器需要知道当前JSP使用的编码,不然服务器无法正确把JSP编译成java文件。
如果两个都不出现,默认值为ISO-8859-1。该编码无法显示中文,需设置这两个属性。这两个设置其中一个,另一个值与出现的值相同。
当多次出现该指令,第一次有效。

4)page指令的errorPageisErrorPage属性
当一个JSP页面出错后,Tomcat会响应500错误页面。为了更给用户更友好的错误界面,此时可以使用page指令errorPage自定义错误页面。<%@ page errorPage=”error.jsp”%>,如果当前页面出错,会自动请求转发error.jsp页面。
error.jsp中可使用<%@ page  isErrorPage=”true”%>表示该页面为专门处理异常的页面。设置后,可以在error.jsp中使用一个exception内置对象,其他页面不能使用该内置对象。


web.xml中配置错误页面:
除了使用page指令配置错误页面,还可以配置web.xml,指定错误处理。此种方式与page指令无关。

 

<web-app>

  <error-page>

    <error-code>500</error-code>

    <location>/err500.jsp</location>

  </error-page> 

<error-page>

    <error-code>404</error-code>

    <location>/err404.jsp</location>

  </error-page>

  <!-- JSP如果抛出为配置的excpetion-type指定类型的异常时,会转到服务器端错误error500.jsp -->

  <error-page>

    <exception-type>java.lang.RuntimeException</exception-type>

    <location>/error.jsp</location>

  </error-page>

</web-app>

5)page指令的session属性
<%@ page session=”true”%>
指定JSP页是否使用Session,默认为true

6)page指令的method属性
<%@ page  method=”doPost”%>
指定Java程序片段所属的方法名。如果不指定,默认<%  %>中的代码存放在service方法中。当多次使用该指令时,第一次有效。该属性值包括:service, doGet, doPost

² include(查看Servlet类对应的代码)
JSP可通过include指令包含其他文件内容。该种包含叫静态包含,目的就是把多个JSPHTML文件合并成一个JSP文件。
include指令语法格式:
              <%@ include  file=”目标组件的绝对URL或相对
URL”%>
使用场景: 如果多数网页包含相同内容,可把相同内容单独放到一个文件中,其他JSP文件通过include指令将这个文件包含进来。提高代码复用,提高开发效率,便于维护。
本质上,include指令包含(静态包含),是在JSP翻译成Servlet前,将多个页面合并成一个文件,翻译成一个Servlet
案例:


²  taglib(查看Servlet类对应的代码)
JSP
使用第三方的标签库时,使用taglib指令导包。以后讲解


Ø  JSP隐含对象(查看Servlet类对应的代码)(记住每个对象,每个对象类型,作用)
JSP
中包含九大内置对象(JSP中无需创建即可使用的对象)。

²  request
request隐含对象的类型javax.servlet.HttpServletRequest。常用

²  response
response隐含对象的类型javax.servlet.HttpServletResponse。常用

²  pageContext(仅仅在当前页面有效)
pageContext隐含对象的类型javax.servlet.jsp.PageContext。常用,可以获得当前页面的上下文

²  application
application隐含对象的类型javax.servlet.ServletContext。常用

²  out
out隐含对象的类型javax.servlet.jsp.JspWriter。等同于response.getWriter(),向客户端发送信息。常用

²  config
config隐含对象的类型javax.servlet.ServletConfig。不常用,当前servlet对应的配置信息

²  page
相当于Java中的this,当前JSP本身。不常用

²  session
session隐含对象的类型javax.servlet.http.HttpSession。常用,后面讲。只有在<%@page session=”true”%>JSP中可用。默认为true

²  exception
exception
隐含对象的类型java.lang.Exception只有在<%@ page  isErrorPage=”true”%>JSP中可以使用

JSP对应的源码:

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)

        throws java.io.IOException, javax.servlet.ServletException {

       //隐含对象,全部都是service方法的局部变量

    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;

//从此开始,上面已经定义好了隐含对象在JSP编写代码时,相当于放在隐含对象

//定义的下面,当然可以自动使用隐含对象了。

     //JSP页面内容显示,所有的HTML代码都以out.write()输出。

  }

}

Ø  JSP注释(查看Servlet类对应的代码)
JSP注释:<%--  --%>,也可在<%   %>内使用Java注释方式。

 
<!-- HTML注释, 浏览器端不显示,解析HTML代码时,过滤 -->
<body>
<%-- 表示JSP注释,将JSP翻译成Servlet类是,就过滤了。 --%>
<%
//可用    编译后,.class文件中不包含
/* 可用  */
 %>
</body>

JSP生命周期
JSP生命周期包含以下阶段:

1) 解析阶段:Servlet容器解析JSP文件的代码,如果有语法错误,就会向客户端返回错误信息
2) 翻译阶段:Servlet容器把JSP文件翻译成Servlet源文件(.java文件就是一个Servlet类)
3) 编译阶段:Servlet容器编译Servlet源文件,生成Servlet类(.class)
4) 初始化阶段:加载与JSP对应的Servlet类,创建实例,并调用初始化方法
5) 运行时阶段:调用与JSP对应的Servlet实例的服务方法
6) 销毁阶段:调用JSP对应的Servlet实例的销毁方法,再销毁Servlet实例。

注意:JSP生命周期中,解析,翻译,编译是区别于Servlet的,这三个阶段在以下场合会执行。
    a) JSP文件被客户端首次请求访问。
    b) JSP文件更新
    c) JSP对应的Servlet类的类文件被手工删除

JSP相关接口及方法分析:
JSP对应的Servlet类实现了javax.servlet.jsp.JspPage接口,而JspPage接口继承自javax.servlet.Servlet接口。在JspPage接口中定义了jspInit,jspDestroy,作用与Servlet接口的init, destroy方法相同。在开发自己的JSP文件时,可以重写jspInit, jspDestroy方法JSP对应的Servlet类的_jspService()方法则由Servlet容器根据JSP源文件自动生成

案例分析:以下代码是否正确?

<html><head><title>life.jsp</title></head><body>

<%!

    File tempDir = null;

    publicvoid jspInit(){

                tempDir = (File)application.getAttribute("javax.servlet.context.tempdir");

}

%> 

    工作目录: <%= tempDir.getPath() %>

</body></html>

分析:容器在编译时会产生错误。因为application变量是JSP对应的_jspService方法中定义的局部变量,其他任何方法中无法调用。此时,可以在jspInit方法中,通过this.getServletConfig().getServletContext()方法得到ServletContext对象。

 

a)请求转发
JSPServlet一样,也可以进行请求转发。
JSP请求转发语法:  <jsp:forward  page=”转发的目标组件相对或绝对URL”/>
注意
<%!  %>,<%  %>,<%= %>这 三种形式叫做JSP脚本元素(scripting element)。把<jsp:forward >,<jsp:include >,<jsp:useBean  >jsp前缀开头的称为JSP动作标签,用于简化Java脚本。

Servlet请求转发的特点同样适用于JSP。即:JSP源组件和目标组件共享HttpServletRequestHttpServletResponse对象,JSP源组件中的所有输出数据都不会发送到客户端。有一点需要注意servlet请求转发forward方法之后的代码会被执行,而JSP源组件<jsp:forward>标签之后的代码不会执行。原因在于
该动作标签对应的servlet源文件中的程序代码为:
if(true){
       _jspx_page_context.forward(“target.jsp”);
      return;//因为有return,所以之后的代码不会被执行。

}

转发源组件可以通过<jsp:param>标签向转发目标组件传递额外的请求参数。在目标组件,使用getParameter方法


包含
JSP中可使用include指令和include动作标签实现包含
include指令语法:此种形式叫静态包含
<%@ include  file=”包含的目标组件的相对URL或绝对URL”%>
include动作标签语法:此种形式叫动态包含
<jsp:include page=” 包含的目标组件的相对URL或绝对URL”/>

Ø  静态包含
案例

静态包含按照以下流程响应客户端请求:
1)解析a.jsp。当解析到<%@ include file=”b.jsp” %>时,将b.jsp所有源代码融合到a.jsp中。合并后a.jsp代码如下:

<h1>including  before</h1>

<%

    int var = 1;

    request.setAttribute("username", "Tom");

   %>

<h1>output from  target</h1>

var = <%=var%>

<br/>

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

<h1>include directive  after...</h1>

2)将合并后的JSP源代码翻译为Servlet源文件,再把它翻译为Servlet类。
3)初始化a.jsp对应的Servlet,再运行它的service
注意:
1-静态包含发生在解析JSP源组件阶段,被包含的目标文件中的内容,被原封不动地添加到JSP源组件中,Servlet容器再对JSP源组件进行翻译和编译。
2-静态包含的目标组件可以为HTMLJSP文件,但不允许为Servlet。如果目标组件为JSP文件,则该目标JSP文件可以访问源组件中定义的局部变量,因为JSP源组件和JSP目标组件对应同一个Servlet
3- 实际开发中,为了区分JSP源组件及被静态包含的JSPHTML目标组件,一般会将被静态包含的JSP文件用”.jspf”作为扩展名,被静态包含的HTML文件用”.htmf”作为扩展名;或者被静态包含的JSPHTML文件都用“.inc”作为文件扩展名。
注意点:
     静态包含,目标文件如果为html,html中如果包含中,则一直会显示乱码。解决方案:

        可以在自己项目的web.xml中配置如下信息。

 
  <jsp-config>
  <jsp-property-group>
  <url-pattern>*.html</url-pattern>
  <page-encoding>utf-8</page-encoding>
  </jsp-property-group>
  </jsp-config>


Ø  动态包含
动态包含:目标组件和源组件分别对应不同的Servlet。与Servlet中学习的dispatcher.include()方法一样。

    动态包含运行流程:

1)解析a.jsp,将其翻译为Servlet源文件。<jsp:include page=””>被翻译成
   xxx.runtime.JspRuntimeLibrary.include(req,resp,”b.jsp”,out,false);
2)把Servlet源文件编译为Servlet类。
3)初始化与a.jsp对应的Servlet对象,再运行它的service方法
4a.jsp对应的Servlet方法会调用    xxx.runtime.JspRuntimeLibrary.include(req,resp,”b.jsp”,out,false);
方法,当Servlet容器执行该方法时,会解析b.jsp,把b.jsp翻译为Servlet源文件,再编译成Servlet类,生成Servlet对象,初始化该对象并调用它service方法。
5)当Servlet容器执行完xxx.runtime.JspRuntimeLibrary.include(req,resp,”b.jsp”,out,false);
方法后,继续执行a.jsp代表的Servlet服务方法中的后续代码。
注意:
1- 动态包含发生在运行JSP源组件阶段,动态包含的目标组件可以为HTML文件,JSP文件或Servlet。如果目标组件为JSPServlet容器会在运行JSP源组件的过程中,运行与JSP目标组件对应的Servlet服务方法。JSP目标组件生成的响应结果被包含到JSP源组件的响应结果中。
2- jsp:include标签还有一个flush属性,可选值为truefalse。如果为true,就表示源组件在包含目标组件前,先把已经生成的响应正文提交给客户。默认为false

3静态包含和动态包含练习:
静态包含通常用来包含不会发生变化的网页内容(HTML),而动态包含通常用来包含会发生变化的网页内容。做如下两个内容。home.jsp, product.jsp 如图


页面布局分析:除了内容部分不同,其他都相同。


改进一:以上代码有大量冗余,增加开发成本与维护成本。此时可以将两个jsp文件中相同部分放在单独的JSPhtml文件中,然后在home.jsp,product.jsp将相同部分通过静态或动态包含进来。
为了解决以上问题,分析代码结构:

 



改进二:以上代码中home.jsp,product.jsp依然包含很多重复代码。根据所学知识,可将home.jsp,product.jsp相同的部分抽取出来


JSP异常处理

JSP提供了一种异常处理机制:可以在当前JSP中通过指令<%@ page errorPage=”error.jsp” %>,指定一个专门处理异常的页面。如果JSP在运行时抛出异常,则交给error.jsp处理该异常。但前提是error.jsp页面必须通过如下指令:<%@ page  isErrorPage=”true” %>声明自己是异常处理页面,此时,在error.jsp页面中,可直接访问exception隐含对象,获得当前异常的详细信息。
注意:抛出异常的JSP文件与处理异常的JSP网页之间是请求转发关系。因此可以共享请求范围内的共享数据

发布JSP
jsp也可以向servlet一样,在web.xml中配置<servlet><servlet-mapping>,给JSP映射URL。可通过url访问JSP文件。
案例:

web.xml

  <servlet>

    <servlet-name>sum</servlet-name>

    <jsp-file>/sum.jsp</jsp-file>

  </servlet>

  <servlet-mapping>

    <servlet-name>sum</servlet-name>

    <url-pattern>/sum</url-pattern>

  </servlet-mapping>

3PageContext抽象类
javax.servlet.jsp.PageContext.PageContext类型的对象由Servlet容器创建,JSP文件中可以直接通过隐含对象pageContext使用。该类提供了很多实用的方法。应用场合主要在JSP文件中的Java程序片段,JSP文件中的自定义标签处理类。
PageContext类方法可分为以下几种:
1) 向各种范围内存取属性的方法(page, request, session, application)
getAttribute(String name):返回页面范围内的特定属性值
getAttribute(String name, int scope):返回参数scope指定范围内的特定属性值
setAttribute(String name, Object obj, int scope):向参数scope指定的范围内存放属性。
removeAttribute(String  name, int scope):从参数scope指定的范围内删除特定属性。
findAttribute(String name):依次从页面范围,请求范围,回话范围,web应用范围内寻找参数name指定的属性。如果找到,就立即返回该属性的值;如果所有的范围内都不存在该属性,返回null
int getAttributeScope(String name):返回指定的属性所属的范围,如果在所有的范围内都不存在该属性,就返回0

注意:
scope参数指定属性的范围,为PageContext类的4个静态常量。
PageContext.PAGE_SCOPE:实际值为1,表示页面范围。
PageContext.REQUEST_SCOPE:实际值为2,表示请求范围
PageContext.SESSION_SCOPE:实际值为3,表示会话范围
PageContext.APPLICATION_SCOPE:实际值为4,表示Web应用范围。

2) 获得由 Servlet 容器提供的其他对象的引用方法
PageContext 类可通过如下方法获得 Servlet 容器提供的 ServletContext HttpSession ServletRequest ServletResponse 等对象
getPage() :返回与当前JSP对应的Servlet实例   page
getRequest():返回ServletRequest对象
         request
getResponse():返回ServletResponse对象
    response
getServletConfig():返回ServletConfig对象
    config
getServletContext():返回ServletContext对象
    application
getSession():返回HttpSession对象
              session
getOut():返回一个用于输出响应正文的JspWriter对象。
    out
注意:
JSPJava程序片段中,可以直接通过隐含对象application, request, response,out等使用,而不需要上述方法再获得。但在自定义的JSP标签处理类中,无法使用这些隐含对象,此时就需要使用PageContext类相关方法得到以上对象了。

3) 请求转发和包含的方法
forward(String relativeUrlPath): 用于将请求转发给其他Web组件
include(String relativeUrlPath):用于包含其他Web组件

注意:
JSP 文件中有专门的 JSP 指令 <%@include  file=””%> 或动作标签 <jsp:include page=””> 进行请求转发和包含操作,而在自定义 JSP 标签的处理类中,无法使用 JSP 这些指令或动作。此时需要使用 PageContext 类的方法完成请     

猜你喜欢

转载自blog.csdn.net/zqq3436/article/details/80387748