介绍

JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。

JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。

JSP原理

JSP运行的流程图如下:

jsp中动态 调用java方法_java


当客户端浏览器向服务器请求一个 JSP 页面时,服务器收到该请求后,首先检查所请求的这个JSP 文件内容 ( 代码 ) 是否已经被更新,或者是否是 JSP 文件创建后的第一次被访问,如果是,那么,这个 JSP 文件就会在服务器端的 JSP 引擎作用下转化为一个 Servlet 类的 Java 源代码文件。紧接着,这个 Servlet 类会在 Java 编译器的作用下被编译成一个字节码文件,并装载到 jvm 解释执行。剩下的就等同于 Servlet 的处理过程了。如果被请求的 JSP 文件内容 ( 代码 ) 没有被修改,那么它的处理过程也等同于一个 Servlet 的处理过程。即直接由服务器检索出与之对应的 Servlet 实例来处理。


需要注意的是,JSP 文件不是在服务器启动的时候转换成 Servlet 类的。而是在被客户端访问的时候才可能发生转换的 ( 如 JSP 文件内容没有被更新等,就不再发生 Servlet 转换 )。就 Tomcat 而言,打开目录 %Tomcat%/work/%您的工程文件目录%,然后会看到里面有 3个子目录:org/apache/jsp,若没有这 3 个目录,说明项目的 JSP 文件还没有被访问过,打开进到 jsp 目录下,会看到一些 *_jsp.java 和 *_jsp.class 文件,这就是 JSP 文件被转换成Servlet 类的源文件和字节码文件了。


验证上述结论

我们可以自己做个试验看看,a.jsp示例代码如下:


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'a.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
    当前时间:
    <%
    Date date = new Date();
    out.print(date.toLocaleString());
     %> 
  </body>
</html>



然后打开目录 %Tomcat%/work/%您的工程文件目录%,进到 jsp 目录下,截图如下:

jsp中动态 调用java方法_Java_02


打开a_jsp.java 文件,源码如下:


/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.53
 * Generated at: 2014-04-07 10:57:40 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.*;
import java.util.*;

public final class a_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=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('\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 'a.jsp' starting page</title>\r\n");
      out.write("    \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("\r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    当前时间:\r\n");
      out.write("    ");

    Date date = new Date();
    out.print(date.toLocaleString());
     
      out.write(" \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);
    }
  }
}



查看Tomcat源码,我们会发现org.apache.jasper.runtime.HttpJspBase是继承HttpServlet的一个子类,到这里基本上就明白了,JSP 本质是一个 Servlet,它的运行也需要容器的支持。


JSP最佳实践
不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。
其原因为,程序的数据通常要美化后再输出;让JSP既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各得,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。这也比较符合MVC架构模式。


JSP语法

JSP语法大体上包含以下部分:

  • JSP模版元素 
  • JSP表达式 
  • JSP脚本片断 
  • JSP注释
  • JSP指令
  • JSP标签 
  • JSP内置对象
  • 如何查找JSP页面中的错误 

JSP模版元素 
JSP页面中的HTML内容称之为JSP模版元素。 
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。

JSP脚本表达式 
JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:<%= 变量或表达式 %>
举例:当前时间:<%= new java.util.Date() %> 
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;)。

它其实就相当于 out.print();

JSP脚本片断
a、JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:

<% 
		多行java代码 
%>



注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。 

JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。

b、在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。

举例:


<%
     for(int i=0;i<100;i++){
     
      %>
     <td>aa</td>
     <%
     }
      %>



多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);

原则:正规开发中的JSP中不应出现一行java脚本。

c、标签封装单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:


<%
     for(int i=0;i<100;i++){
     
      %>
     <td>aa</td>
     <%
     }
      %>



JSP声明 

JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。

作用:定义成员变量和方法

语法:
<%! 
java代码 
%>

所以:

  • JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。 
  • 多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
  • JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。 

JSP注释 

JSP注释的格式:

<%-- 注释信息 --%>

HTML中的注释

<!-- 注释信息 -->

JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容(不会被JSP引擎翻译)。被HTML中的注释掉的脚本会被JSP引擎翻译,只是会把输出结果注释。

注意:注释不允许嵌套

JSP指令 
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP 2.0规范中共定义了三个指令:

  • page指令
  • Include指令
  • taglib指令

JSP指令简介 

JSP指令的基本语法格式:

<%@ 指令 属性名="值" %>
举例:<%@ page contentType="text/html;charset=gb2312"%>
 如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>
也可以写作:
<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>

Page指令 


page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。

JSP 2.0规范中定义的page指令的完整语法:

属性:

language:指示使用的脚本语言。目前只支持java。默认也是java

extends:指示生成的Servlet的父类。不需要更改

*import:导入java脚本中使用到的类或者包。同java代码中的package

JSP引擎会自动导入一下包:

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
写法形式:
<%@ page import="java.util.Date,java.util.UUID"%>
或者
<%@ page import="java.util.Date"%>
<%@ page import="java.util.UUID"%>

Eclipse中可以使用Alt+/引入类,自动导入该类

*session:指示是否生成HttpSession对象。true|false默认值是true

buffer:配置输出流的缓存的大小none|8kb|sizeKb 默认值是8Kb。不需要改

autoFlush:缓存满了是否自动刷新。true|false 默认值是true。不需要改

isThreadSafe:指示生成的servlet是否继承SingleThreadModel接口。true|false false是才继承该接口。默认值是true

*errorPage:指示错误提示页面的URL地址。地址如果以"/'"表示绝对路径。

配置全局错误提示页面另外一种方式:

修改web.xml

<error-page>
   <exception-type>java.lang.ArithmeticException</exception-type>
   <location>/error.jsp</location>
   </error-page>
   <error-page>
   <error-code>404</error-code>
   <location>/404.jsp</location>
   </error-page>

*isErrorPage:指示jsp对应的Servlet是否生成Exception对象。true|false

默认值是false

*contentType:指示生成的html的MIME类型。text/html;charset=UTF-8 等同于ServletResponse.setContentType();

*pageEncoding:指示JSP引擎在翻译JSP所生成的Servlet使用的编码。(代替contentType)

*isELIgnored:是否忽略EL表达式。true|false。true是忽略。默认是false


使用page指令解决JSP中文乱码

JSP程序存在有与Servlet程序完全相同的中文乱码问题


  • 输出响应正文时出现的中文乱码问题 
  • 读取浏览器传递的参数信息时出现的中文乱码问题

JSP引擎将JSP页面翻译成Servlet源文件时也可能导致中文乱码问题 


  • JSP引擎将JSP源文件翻译成的Servlet源文件默认采用ISO8859-1编码,而JSP开发人员可以采用各种字符集编码来编写JSP源文件,因此,JSP引擎将JSP源文件翻译成Servlet源文件时,需要进行字符编码转换。 
  • 如果JSP文件中没有说明它采用的字符集编码,JSP引擎将把它当作默认的ISO8859-1字符集编码处理。

如何解决JSP引擎翻译JSP页面时的中文乱码问题 


  • 通过page指令的contentType属性说明JSP源文件的字符集编码
  • page指令的pageEncoding属性说明JSP源文件的字符集编码

include指令 

include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。

语法:

<%@ include file=“被包含组件的绝对URL或相对URL"%>

其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。


注:

静态包含:<%@includefile=””%> 多个页面对应一个Servlet
动态包含:<jsp:include page=”8.jsp”/>  每个页面对应一个Servlet

taglib指令
Taglib指令用于在JSP页面中导入标签库,讲自定义标签技术时讲。


JSP运行原理和九大隐式对象
每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。
由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。
J SP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
这9个对象分别是:

类型      引用名称(隐式对象)
1、HttpServletRequest   request
2、HttpServletResponse  response
3、HttpSession session  (不一定有:page指令session必须为true)
4、ServletContext        application
5、ServletConfig        config
6、Object               page(指示Servlet对象本身,就如同用this)
7、Throwable exception   (不一定有:page指令isErrorPage必须为true)
8、JspWriterout   同HttpServletResponse的getWriter返回的PrintWriter
9、PageContext  pageContext

out隐式对象
out隐式对象用于向客户端发送文本数据。 
out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。 
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。 
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:

  • 设置page指令的buffer属性关闭了out对象的缓存功能
  • out对象的缓冲区已满
  • 整个JSP页面结束

out隐式对象的工作原理图

jsp中动态 调用java方法_jsp中动态 调用java方法_03


示例:


<%
      out.print("aaa");
      response.getWriter().print("bbb");
       %>

输出结果:bbb aaa


pageContext对象
pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。 
通过pageContext获得其他对象 

  • getException方法返回exception隐式对象 
  • getPage方法返回page隐式对象
  • getRequest方法返回request隐式对象 
  • getResponse方法返回response隐式对象 
  • getServletConfig方法返回config隐式对象
  • getServletContext方法返回application隐式对象
  • getSession方法返回session隐式对象 
  • getOut方法返回out隐式对象

 


pageContext作为域对象 

pageContext对象的方法 


  • public void setAttribute(java.lang.String name,java.lang.Object value)
  • public java.lang.Object getAttribute(java.lang.String name)
  • public void removeAttribute(java.lang.String name) 删除所有域中的所有名字为name的对象

pageContext对象中还封装了访问其它域的方法


  • public java.lang.Object getAttribute(java.lang.String name,int scope)
  • public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
  • public void removeAttribute(java.lang.String name,int scope) 删除指定范围内的域对象

代表各个域的常量


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

findAttribute方法    (*重点,查找各个域中的属性) EL表达式

注意:findAttribute方法查找范围page -> request -> session -> application,只要查找到就停止。

重点:

到此为止,web开发接触到了4个域对象,这4个域对象是学习web的重点,也是笔试经常考察的知识点
pageContext(称之为page域) 
request(称之为request域)
session(称之为session域)
servletContext(称之为application域)
明确如下问题:
什么是域?
这4个对象的生命周期?
哪种情况下用哪种域对象。
1、request:如果客户向服务器发请求,产生的数据,用户看完就没用了,像这样的数据就存在request域,像新闻数据,属于用户看完就没用的
2、session:如果客户向服务器发请求,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,像购物数据,用户需要看到自己购物信息,并且等一会儿,还要用这个购物数据结帐
3、servletContext:如果客户向服务器发请求,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在servletContext域中,像聊天数据


引入和跳转到其他资源 

PageContext类中定义了一个forward方法和两个include方法来分别简化和替代RequestDispatcher.forward方法和include方法。

方法接收的资源如果以“/” 表示绝对路径,“/”代表当前web应用。

JSP内置标签
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护。

JSP常用标签

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

<jsp:include>标签

<jsp:include>标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。
语法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。  
<jsp:include>与include指令的比较
<jsp:include>标签是动态引入, <jsp:include>标签涉及到的2个JSP页面会被翻译成2个servlet,这2个servlet的内容在执行时进行合并。 
而include指令是静态引入,涉及到的2个JSP页面会被翻译成一个servlet,其内容是在源文件级别进行合并。
不管是<jsp:include>标签,还是include指令,它们都会把两个JSP页面内容合并输出,所以这两个页面不要出现重复的HTML全局架构标签,否则输出给客户端的内容将会是一个格式混乱的HTML文档。

<jsp:forward>标签
<jsp:forward>标签用于把请求转发给另外一个资源。
语法:
<jsp:forward page="relativeURL | <%=expression%>" /> 
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。

<jsp:param>标签 
当使用<jsp:include>和<jsp:forward>标签引入或将请求转发给其它资源时,可以使用<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:include>


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