上周在工作上遇到了一件糗事。在写jsp的过程中,经常会遇到将jsp文件模块化,然后通过include的方式将一个个模块进行组装。这个过程中,经常会在各个模块之间共享变量,那么变量的范围是什么呢?自己因为忘记这个知识点而调试了好长时间。知识点虽然简单,但是这个是Java
web经典的问题,即对于include,Java web到底是怎么实现的?

一.JSP基本原理

jsp 的本质就是servlet,当用户向指定的servlet
发送请求时,Servlet利用输出流动态的生成html页面
jsp页面有两部分组成:

  • 静态部分:标准的html标签,静态的页面内容
  • 动态部分:受java程序控制的内容,这些内容有java程序动态生成
    jsp页面必须放在web应用中才有效,所有编写jsp页面之前应该先构建一个web应用,每个jsp页面就是一个servlet实例———JSP页面由系统编译成servlet,servlet在负责响应用户请求,
    每个JSP的第一个访问者是很慢的,因为必须先等待JSP编译成Servlet
    当启动Tomcat之后,可以在Tomcat的workCatalinalocalhost目录下找到编译成的test_jsp.java和test_jsp.class文件,这两个文件都是有tomcat自动生成的,是与jsp相对应的

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.35
 * Generated at: 2017-04-27 14:39:39 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 test_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("rn");
      out.write("rn");
      out.write("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">rn");
      out.write("<html>rn");
      out.write("  <head>rn");
      out.write("    <base href="");
      out.print(basePath);
      out.write("">rn");
      out.write("    rn");
      out.write("    <title>My JSP 'test.jsp' starting page</title>rn");
      out.write("    rn");
      out.write("t<meta http-equiv="pragma" content="no-cache">rn");
      out.write("t<meta http-equiv="cache-control" content="no-cache">rn");
      out.write("t<meta http-equiv="expires" content="0">    rn");
      out.write("t<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">rn");
      out.write("t<meta http-equiv="description" content="This is my page">rn");
      out.write("t<!--rn");
      out.write("t<link rel="stylesheet" type="text/css" href="styles.css">rn");
      out.write("t-->rn");
      out.write("rn");
      out.write("  </head>rn");
      out.write("  rn");
      out.write("  <body>rn");
      out.write("    This is my JSP page. <br>rn");
      out.write("  </body>rn");
      out.write("</html>rn");
    } 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);
    }
  }
}

该java类主要包含三个方法:

  • init() 初始化JSP/Servlet的方法
  • destroy() 销毁JSP/Servlet之前的方法
  • service() 对用户请求生成响应的方法

前面讲了servlet入门实践现在开始介绍jsp入门实践,开发环境的搭建请参考我前面的tomcat的文章,jsp入门教程分为上下两部分,第一部分简单讲解:jsp语法的规范,以及三大编译指令,七个动作指令和九大内置对象,生命周期讲解等。

jsp文件到底是什么?

图片 1jsp-servlet-tomcat.png

  • JSP是Servlet的一种特殊形式,每个JSP页面就是一个Servlet实例
  • JSP页面由系统编译成Servlet,Servlet再负责响应用户请求
  • Tomcat负责执行Servlet文件
二.JSP声明

JSP声明用于声明变量和方法,JSP声明将会转换成对应的Servlet的成员变量或成员方法,JSP声明变量或方法可以使用private,public
等访问控制修饰符,也可以使用static修饰,但不能使用abstract修饰声明部分方法
。JSP声明的语法格式如下:

<%! 声明部分%>

全部代码下载:链接

准备实验

因此在我机器上实验了一把,实验的环境如下:

名称 配置
系统 Mac OS
IDE IntelliJ IDEA 2017.2 Help
容器 Tomcat 7.04

项目的文件路径如下所示:

图片 2项目路径.png

在Mac OS下,Intellij
IDEA将jsp文件编译成相应的servlet文件,那么该文件的位置是:/Users/garybhwang/Library/Caches/IntelliJIdea2017.2/tomcat然后根据相应的项目名称找到相应的java文件:/Unnamed_项目名/work/Catalina/localhost/_/org/apache/jsp相应的目录结构如下所示:

图片 3jsp-servlet目录.png

三.JSP表达式

JSP提供了一种输出表达式的简单方式,但表达式语法后不能有分号,语法格式如下:

<%= 输出表达式 %>

1.jsp简介:

JSP全名为Java Server
Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它[1]
是由Sun
Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准。JSP技术有点类似ASP技术,它是在传统的网页HTML(标准通用标记语言的子集)文件(.htm,.html)中插入Java程序段(Scriptlet)和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。
用JSP开发的Web应用是跨平台的,既能在Linux下运行,也能在其他操作系统上运行。
第一个jsp程序:

  1. 在eclipse中建立动态web工程
  2. 在WebContent上右击建立jsp文件
  3. 在jsp文件中输入如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
  out.print("hello peace");
%>
</body>
</html>
  1. 在浏览器中输入如下:http://localhost:8080/工程名/NewFile.jsp
    显示如下:

    01

  2. JSP作为servlet的变体,jsp的文件会被翻译成servlet文件:文件位置为Tomcat按住给你目录:apache-tomcat-7.0.64/work/Catalina/localhost/stuJsp/org/apache/jsp

02



打开java文件可以看到该文件根servlet的文件类似:有_jspInit(),jspDestroy(),_jspService()方法。并将jsp文件中的代码生成了_jspService()方法中的代码



03

实验方式

common.jsp 页面去包含header.jsp 页面,header.jsp 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>header</title></head><body> <h2>This is header page!</h2></body></html>
三.JSP脚本

语法格式:

<% java 代码 %>

2.JSP语法:

include三种方式

  • jsp页面的include指令<%@ include file="/WEB-INF/header.jsp"%>common.jsp
    页面的代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>common</title></head><body> <h1>This is common page!</h1> <%@ include file="/WEB-INF/header.jsp"%></body></html>

那么common.jsp 编译成为servlet文件只有的内容如下:

图片 4(common_jsp.java).png

从上面的源码可以看到,<%@ include file = "*******" %>最终是将header.jsp
文件进行编译,然后将其内容包含进来。因此include指令包含的是页面的内容

  • jsp页面的include动作元素<jsp:include page="/WEB-INF/header.jsp"></jsp:include>common.jsp
    页面的代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>common</title></head><body> <h1>This is common page!</h1> <jsp:include page="/WEB-INF/header.jsp"></jsp:include></body></html>

那么按照这种包含的方式,common.jsp 编译的servlet源码如下所示:

图片 5(common_jsp.java).png

从common_jsp.java的servlet文件中可以看到
<jsp:include page="*****"></jsp:include>,并不是像include指令那样包含内容,而是通过Tomcat容器中的JspRuntimeLibrary类的include方法进行包含。查看Tomcat容器的源码可以看到,可以看到:

图片 6tomcat
源码.png

从Tomcat源码可以分析出,<jsp:include page="*****"></jsp:include>的实质是,在common_jsp.java
文件中,执行 RequestDispatcher的include方法,而这个方法使得
common_jsp.java 中的request
对象、response对象和header.jsp中的response对象、request对象是共享的。

  • RequestDispatcher类的include方法request.getRequestDispatcher("/WEB-INF/header.jsp").include(request,response);同上分析。
四.JSP 的三个编译指令

JSP 的编译指令是通知JSP引擎的消息,它不直接生成输出

  • page 该指令针对当前页面的指令
  • include 用于指定包含另一个页面‘
  • taglib 用户定义和访问自定义标签

page指令 各属性的意义:

  • language 声明当前JSP页面使用的脚本语言的种类
  • extends 指定JSP页面编译所产生的java所继承的父类
  • import 用来导入包
  • session 设定这个JSP 页面是否需要HTTP session
  • buffer 指定输出缓冲区的大小
  • autoFlush 当输出缓冲区即将溢出时,是否需要强制输出缓冲区的内容
  • info 设置jsp 页面信息
  • 指定错误页面

include 指令
使用include
指令可以将一个外部文件嵌入到当前的jsp文件中,融合成一个页面。这个是静态的include语句,它会把目标页面的其他编译指令也包含进来,而动态的include则不会。
如果被嵌入的文件经常需要改,建议使用<jsp:include>操作指令

2.1.JSP脚本:

脚本程序,就是为了在HTML中穿插java代码,可以包含任意语法真确的java语句,变量,方法或表达式。生成servlet源码时该处的代码被放到_jspService()方法中
  1. 脚本程序的语法:
    <% 代码片段 %>
    就像第一个jsp程序那样

<%
  out.print("hello peace");
%>
  1. 注意:脚本中不能出现标签和jsp元素,记住只能写java就行;但是可以穿插写:如下:

<%
  for(int i=0;i<5;i++)
  {
    out.print("hello peace"+i);
%>
  <br/>
 <%
   out.print("换行符穿插在中间了,一样会被循环输出");
  }//for循环结束
 %>

include和共享对象

对象 作用
request 页面和页面构成forward以及include关系,达成共享
pageContext 在一个页面中共享
session 在整个会话期间共享

因此,有上述的分析可以得出这样的结论:

  • 对于 <%@ include file %>
    而言,子页面只是将内容包含在父页面之中,因此只有子页面和父页面之间的request对象以及pageContext对象是共享的
  • 对于
    <jsp:include path ></jsp:include>而言,父页面通过RequestDispatcher的include方法将子页面包含进来,因此只有父页面和子页面之间request对象是共享的
include方式 父子页面共享对象
<%@ include file %> pageContext对象以及request对象
<jsp:include path ></jsp:include><c:import></c:import> request对象,不共享pageContext对象
RequestDispatcher类的include方法 request对象,不共享pageContext对象
五.JSP的七个动作指令

动作指令与编译指令不同,编译指令是通知Servlet引擎的处理消息,而动作指令只是运行时的动作,编译指令再将JSP编译成Servlet时起作用

  • jsp:forward 执行页面转向,将请求的处理转发到下一个页面
  • jsp:param 用于传递参数,必须与其他支持参数的标签一起使用
  • jsp:include 用于动态引入一个jsp文件
  • jsp:plugin 用于下载javabean 或Applet到客户端执行
  • jsp:userBean 创建一个javaBean的实例
  • jsp:setProperty 用于设置javabean实例的属性值
  • jsp:getProperty 输出javabean实例的属性值

2.2.JSP声明:

一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们。生成servlet源码时该处的代码成为类的属性和方法;
  1. JSP声明的语法:
    <%! declaration; [ declaration; ]+ … %>
  2. 演示如下:

<%! private int i=10; %> 
 <%!
    public void test(){
     int a=0;
     int b=2;
     a=a+b;
     System.out.print(a);
 }
 %>
  1. 查看生成的servlet文件可以知道刚才在jsp声明的变量和方法,成为了相应的属性和方法
六.forward和redirect对比
转发(forward) 重定向(redirect)
执行forward后依然是上一次请求 执行redirect后生产第二次请求
forward目标也可以访问原有 的请求参数,因为依然是同一个请求 redirect的目标业面不能访问原来的请求参数,因为这是第二次请求了
地址栏里请求的URL不会改变 地址栏改为重定向的目标URL,相当于在地址栏里重新输入了URL

2.3JSP表达式:

一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方,作用相当于脚本中的out(输出)

由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签。
表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式。
生成servlet源码时该处的代码被放到_jspService()方法中

  1. JSP表达式的语法格式
    <%= 表达式 %>
  2. 演示如下:

<%--在脚本处声明的变量是局部变量不能带有修饰符 --%>
    <%
      String nick="sisi";
      int a=10,b=10;
    %>
    3.jsp表达式:<br/>
    <%--表达式可以输出相当于out.write 不需要分号结束--%>
      <%=(a-b) %>
      <%=nick %>
      <hr/>
七.JSP 的九个内置对象

JSP脚本中包含九个内置对象,这九个内置对象都是Servlet API
接口的实例,只是JSP规范对他们进行了初始化,也就是说它们已经是对象了,可以直接使用

  • application javax.servlet.ServletContext
    的实例,该实例代表JSP所属的WEB应用实例本身
  • config javax.servlet.ServletConfig
    的实例,该实例代表JSP的配置信息
  • **exception **java.lang.Throwable
    的实例该实例代表其他页面中的异常和错误,只有当页面是错误处理页面,即编译指令page的isErrorPage属性为True时
    该对象才可以用
  • **out ** javax.servlet.jsp.JspWrite的实例
    该实例代表JSP页面的输出流
  • page 代表该页面本身 通常没多大用处
  • pageContext javax.servlet.jsp.PageContext 的实例
    该对象代表JSP页面上下文,使用该对象可以访问页面中的共享数据
  • request
    javax.servlet.http.HttpServletRequest的实例,该对象封装了一次请求,客户端的请求参数都封装在该对象里,是一个常用对象
  • response javax.servlet.http.HttpServletResponse 的实例
    代表服务器对客户端的响应
  • session javax.servlet.http.HttpSession 的实例
    该对象代表一次会话,当客户端浏览器与站点建立连接时,会话开始

2.4JSP注释:

JSP注释不会出现在html的源码中 可以用来注释jsp的代码,html注释会出现在html的源码中;
  1. JSP注释的语法格式:
    <%– 这里可以填写 JSP 注释 –%>
  2. 演示如下:

 1.jsp注释:<br/>
  <%-- 这些注释不会出现在html的源码中 可以用来注释jsp的代码--%>
八.Filter介绍

Filter可认为是Servlet
的一种加强版,他主要是对用户的请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链
Filter有如下几个用处:

  • 在HtttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest
  • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
  • 在HtttpServletResponse到达Servlet之前,拦截客户的HtttpServletResponse
  • 根据需要检查HtttpServletResponse,也可以修改HtttpServletResponse头和数据

创建Filter类
一个filter必须实现javax.servlet.Filter。
三个方法

  • voidsetFilterConfig(FilterConfig config) //设置filter 的配置对象;
  • FilterConfiggetFilterConfig() //返回filter的配置对象;
  • voiddoFilter(ServletRequest req,ServletResponse res,FilterChain
    chain) //执行filter的工作

public class EncodingFilter implements Filter {    

    private String encoding = null;    

    public void destroy() {    
        encoding = null;    
    }    

    public void doFilter(ServletRequest request, ServletResponse response,    
            FilterChain chain) throws IOException, ServletException {    
        String encoding = getEncoding();    
        if (encoding == null){    
            encoding = "gb2312";    
        }    
        request.setCharacterEncoding(encoding);// 在请求里设置上指定的编码    
        chain.doFilter(request, response);  //通过控制对chain.doFilter的方法的调用,来决定是否需要访问目标资源  
    }    

    public void init(FilterConfig filterConfig) throws ServletException {    
        this.encoding = filterConfig.getInitParameter("encoding");    
    }    

    private String getEncoding() {    
        return this.encoding;    
    }    

} 

XML 配置

<filter>    
    <filter-name>EncodingFilter</filter-name>    
    <filter-class>com.logcd.filter.EncodingFilter</filter-class>    
    <init-param>    
       <param-name>encoding</param-name>    
       <param-value>gb2312</param-value>    
    </init-param>    
</filter>    

<filter-mapping>    
   <filter-name>EncodingFilter</filter-name>    
   <url-pattern>/*</url-pattern>    
</filter-mapping> 

Filter生命周期

和Servlet一样,Filter的创建和销毁也是由WEB服务器负责。

与Servlet区别的是

  • 在应用启动的时候就进行装载Filter类而servlet是在请求时才创建(但filter与Servlet的load-on-startup配置效果相同)。
  • 容器创建好Filter对象实例后,调用init()方法。接着被Web容器保存进应用级的集合容器中去了等待着,用户访问资源。
  • 当用户访问的资源正好被Filter的url-pattern拦截时,容器会取出Filter类调用doFilter方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用doFilter方法(Filter对象常驻留Web容器了)。
  • 当应用服务被停止或重新装载了,则会执行Filter的destroy方法,Filter对象销毁

3.三大编译指令:

JSP编译指令用来设置与整个JSP页面相关的属性;
主要有三大编译指令:

<%@ page ... %>    定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等
<%@ include ... %>    包含其他文件
<%@ taglib ... %>    引入标签库的定义,可以是自定义标签

发表评论

电子邮件地址不会被公开。 必填项已用*标注