六、JSP技术

JSP技术

JSP全名是Java Server Page,他是建立在Servlet规范上的动态网页开发技术。在JSP文件中,HTML代码与Java代码共同存在,其中,HTML代码用来显示静态内容的显示,Java代码用来实现网页中动态内容的显示。
与普通HTML有所区别,JSP文件的拓展名为.jsp。
在工程的WebRoot下创建一个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(new java.util.Date().toLocaleString());
	%>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
两次访问页面时间不同,说明此页面为动态页面。

JSP运行原理

当用户通过URL访问Servlet时,Web服务器会根据请求的URL地址在web.xml配置文件中查找匹配的<servlet-mapping>,然后将请求交给<servlet-mapping>指定的Servlet程序去处理。但是,上述例子中虽然没有在web.xml文件中找到与JSP相关的配置,但Web服务器仍然可以根据URL找到对应的JSP文件。这是因为在Tomcat服务器的web.xml文件中实现了JSP的相关配置。
在这里插入图片描述
在这里插入图片描述

这两个句配置,将所有.jsp .jspx的文件交给了JspServlet处理,Tomcat中的JSP引擎就是这个Servlet,该Servlet实现了对JSP页面的解析。
需要注意的是,JSP文件也可以像Servlet程序一样,在web.xml文件中进行注册和映射虚拟路径文件中进行注册和映射虚拟路径。注册JSP页面的方式与Servlet类似,只需将<servlet-class>元素修改为<jsp-file>元素即可。例如

  <servlet>
  	<servlet-name>SimpleJspServlet</servlet-name>
  	<jsp-file>/simple.jsp</jsp-file>
  </servlet>
  <servlet-mapping>
  	<servlet-name>SimpleJspServlet</servlet-name>
  	<url-pattern>/myjsp</url-pattern>
  </servlet-mapping>

其中<jsp-file>元素表示JSP文件,它表示的路径必须以/开始,这个/表示JSP所在的Web应用程序得到根目录
在这里插入图片描述

分析JSP所生成的Servletdaima

当用户第一次访问JSP页面时,该页面都会被JspServlet翻译成一个Servlet源文件,然后将源文件编译成.class文件。Servlet源文件和.class文件都放在”Tomcat安装目录/work/Catalina/localhost/应用名/“目录下。由JSP文件翻译成的Servlet类带有包名,包名为org.apache.jsp,因此simple.jsp生成的Servlet源文件和.class文件的目录为
在这里插入图片描述
可以看到simple.jsp文件被翻译成的class文件和Servlet源文件分别是simple_jsp.class和simple_jsp.java
打开simple_jps.java

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.109
 * Generated at: 2021-07-16 02:29:48 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.*;

public final class simple_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 volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  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\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
      out.write("<title>Insert title here</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("\t褰撳墠鏃堕棿鏄細\r\n");
      out.write("\t");

		out.print(new java.util.Date().toLocaleString());
	
      out.write("\r\n");
      out.write("</body>\r\n");
      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);
    }
  }
}

从上面的代码可以看出,simple.jsp文件翻译后的Servlet类名为simple_jsp,他没有实现Servlet接口,但继承了org.apache.jasper.runtime.HttpJspBase类。在Tomcat源文件中查看HttpJspBase类源代码,可以看到
HttpJspBase类是HttpServlet的一个子类,因此simple_jsp.java就是一个Servlet

JSP基本语法

JSP模板元素
JSP页面可以按照编写HTML页面的方式来编写,其中可以包含HTML文件的所有静态内容,在静态的HTML内容之中可以嵌套JSP的其他各种元素来产生动态内容的执行业务逻辑。JSP页面中的静态HTML内容成为JSP模板元素。JSP模板元素定义了网页的基本骨架,即定义了页面的结构和外观
JSP表达式
JSP表达式用于将程序数据输出到客户端,他将要输出的变量或者表达式直接封装在以"<%= %>"中

<%=expression%>

JSP脚本片段
JSP脚本片段是指嵌套在<% %>之中的一条或多条Java程序代码,这些Java代码必须严格遵守Java语法规范,否则编译会报错,例如

<h2>标题</h2>
<%
	int x=3;
	out.println(x);
%>

需要注意,在一个JSP页面中可以出现多个脚本片段,在两个或者多个脚本片段之间可以嵌套文本、HTML标记或JSP元素,并且这些脚本的代码可以互相访问。
demo.jsp

<%for(int i=1;i<3;i++){ %>
		<h<%=i %>><%=i %>级标题</h<%=i %>>
	<%} %>

被翻译成了demo_jsp.java
在这里插入图片描述

在这里插入图片描述

JSP声明
当JSP页面被翻译成Servlet程序时,JSP中包含的脚本片段、表达式、模板元素都将转化为Servlet中_jspService()方法的程序代码。这时,JSP脚本片段中定义的变量都将成为_jspServlet()方法,从而会出现程序的方法中再定义方法,这样的语法是错误的,为了解决这样的问题在JSP技术中提供了声明,它以"<%! %>"结束,其语法格式

<%!  %>

上述语法格式中,被声明的java代码都将被翻译到Servlet的_jspServlet()方法之外,即在JSP声明中定义的都是成员方法、成员变量、静态方法、静态变量、静态代码块。
同脚本片段一样,在一个JSP页面中可以有多个JSP声明,单个声明中的Java语句可以是不完整的,但是多个声明组合后的结果必须是完整的。
demo.jsp

<%!static {
		System.out.println("statice code block");
	}%>
	<%!
		private int i=9;
		public static java.lang.String str="www.baidu.cn";
		public void jspInit(){
			System.out.println("init method");
			System.out.println("i="+i);
			System.out.println("str="+str);
		}
	%>

demo_jsp.java

在这里插入图片描述

在这里插入图片描述
这里不仅静态代码块被执行了,jspInit()方法也被执行了,这是因为Tomcat在创建某个Servlet实例对象后,将调用init()方法进行初始化,而JSP页面所生成的Servelt的init()方法内部调用了jspInit()方法,所以当第一次访问demo.jsp页面时,都调用jspInit()方法进行JSP页面的初始化。
JSP注释

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

需要注意的是Tomcat在将JSP页面翻译成Servlet程序时,会忽略JSP页面中被注释的内容,不会将注释信息发送到客户端,但是在jsp中使用HTML注释仍可以发送到客户端。

JSP指令

JSP2.0中共定义了page、include、taglib三种指令,每种指令都定义了各自的属性。
page指令

<%@page 属性名="属性值"%>
属性名称取值范围描述
languagejava指明解释JSP文件时采用的语言,默认JAVA
extends任何类的全名编译该JSP时继承哪个类。JSP为Servlet,因此当指明继承不同类时需要实现Servlet的init()、destroy()方法
import任何包名、类名指定在JSP页面翻译成的Servlet源文件中导入的包或类。import是唯一可以声明多次的page指令属性,一个属性可以引用多个类,中间用英文逗号隔开,JSP中默认引入java.lang.* java.servlet.* java.servlet.jsp.* java.servlet.http.*
sessionture、false指明JSP中是否内置Session对象,默认ture
autoFlushture、false指明是否运行缓存。如果为ture使用out.println()等方法输出的字符串并不会立即缓存到客户端,而是暂时存在缓冲区里,缓存满或者程序执行完毕,或执行out.flush()才会到客户端,默认ture
buffernone或者数组+kb指定缓存大小,当autoFlush设为ture时有效
isThreadSafeture、false指定线程是否安全。如果为ture则多个线程同时运行,否则只运行一个线程,默认为ture
isErrorPageture、false指定该页面是否为错误处理页面,如果为ture,则该JSP内置一个Exception对象的exception,可直接使用。默认情况下,isErrorPage的值为false
errorPage某个JSP页面的相对路径指定一个错误页面,如果该JSP抛出一个未捕捉的异常,则跳转到errorPage
contentType有效 的文档类型http协议中Content-Type的值
info任意字符指明JSP的信息,该信息可以通过Servlet.getServletInfo()方法获取到
trimDirectiveWhitespaceture、flase是否去掉指令前后的空白字符,默认为ture

include指令
有时候,需要在JSP页面静态包含一个文件,例如HTML文件、文本文件等。
这是可以通过include指令

<%@include file="relativeURL"%>

file属性用于指定被引入文件的相对路径。
include.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>
	欢迎你,现在的时间是:
	<%@ include file="date.jsp" %>
</body>
</html>

date.jsp


<!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>
<style type="text/css">
	b{
		color:#ff0000
	}
</style>
</head>
<body>
<b><%out.println(new java.util.Date().toLocaleString()); %></b>
</body>
</html>

在这里插入图片描述
浏览器会忽略文件中重复的html标签、body标签、head标签、<!>声明
网站源码
在这里插入图片描述
浏览器容错之后的源码
在这里插入图片描述

JSP隐式对象

隐式对象
在JSP页面中,有一些对象需要频繁的使用,如果每次创建这些对象会非常麻烦。为此,JSP提供了9个隐式对象,他们是JSP默认创建的,可以直接在JSP页面使用。

隐式对象名称类型描述
outjavax,servlet.jsp.JspWriter用于页面输出
requestjavax.servlet.http.HttpServletRequest得到用户请求信息
responsejavax.servlet.http.HttpServletResponse服务器向客户端的回应消息
configjavax.servlet.ServletConfig服务器配置,可以取得初始化参数
sessionjavax.servlet.http.HttpSession用来保存用户的信息
applicationjavax.servlet.ServletContext所有用户的共享信息
pagejava.lang.Object指当前页面转换后的Servlet类的实例
pageContextjavax.servlet.jsp.PageContextJSP的页面容器
exceptionjava.lang.Throwable表示JSP页面所发生的异常,在错误页面中才起作用

out对象
在JSP页面中,经常需要向客户端发送文本内容,这是,可以使用out对象来实现,out对象的类型是JspWriter,它相当于一种带缓存功能的PrintWriter。
在JSP页面中,通过对象写入数据相当于将数据插入到JspWriter对象的缓冲区中,只有调用了ServletResponse.getWriter()方法,缓冲区的数据才能真正写入到Servlet引擎所提供的缓冲区中。
out.jsp

	<%="直接输出<br>" %>
	<%
		out.println("first line<br>");
		response.getWriter().println("second line<br>");
	%>

在这里插入图片描述

可以看到尽管out.println在request.getWriter().println之前但是它的输出内容却在后面。由此可以说明out对象通过print语句写入数据后,直到整个JSP页面结束,out对象中输入缓冲区的数据才真正写入Servlet引擎提供的缓冲区中,而request.getWriter().println则是直接把内容写入Servlet引擎的缓冲区。
因此request.getWriter().println会引起HTML内容错乱
在这里插入图片描述

可以看到request.getWriter().println提前与html结构输出,且如果out中的输出足够多,导致缓存满了输出到了Servlet引擎,此时request.getWriter().println可能会穿插在html文档中出现,导致结构混乱。
pageContext对象
在JSP页面中,想要获取JSP的隐式对象,可以使用pageContext对象。
获取隐式对象的方法

方法名功能描述
Jspwriter getOut()用于获取out隐式对象
Object getPage()用于获取page隐式对象
ServletRequest getRequest()用于获取request隐式对象
ServletResponse getResponse()用于获取response隐式对象
HttpSession getSession()用于获取session隐式对象
Exception getException()用于获取exception隐式对象
ServletConfig getServletConfig()用于获取config隐式对象
ServletContext getServletContext()用于获取application隐式对象

这样当传递一个pageContext对象后,就可以通过这些方法来轻松获取其他的8个隐式对象。
需要注意的是,虽然在JSP页面中可以直接使用隐式对象但是通过pageContext对象获取其他隐式对象的功能是必不可少的,在后面标签的学习中,需要将Java代码从JSP页面移除,这是JSP隐式对象也需要传递给java类中定义的方法。通常情况,只传递一个pageContext对象,然后通过它来调用其他的隐式对象。
pageContext对象不仅提供了获取隐式对象的方法,还提供了存储数据的功能。pageContext对象存储数据是通过操作属性来实现的

方法名称功能描述
void setAttribute(String name,Object value,int scope)用于设置pageContext的属性
Object getAttribute(String name,int scope)用于获取pageContext的属性
void removeAttribute(String name,int scope)删除指定范围内名称为name的属性
void removeAttribute(String name)删除所有范围内名称为name的属性
Object findAttribute(String name)从4个域对象中查找名称为name的属性

pageContext对象的作用范围有4个值
1、pageContext.PAGE_SCOPE:表示页面范围
2、pageContext.REQUEST_SCOPE:表示请求范围
3、pageContext.SESSION_SCOPE:表示会话范围
4、pageContext.APPLICATION_SCOPE:表示Web应用程序范围
需要注意的是:findAttribute会按上述顺序依次查找。
exception对象
需要注意的是,exception对象只有在错误处理页面才可以使用,即page指令中指定了属性<%@page isErrorPage=“true”%>的页面
price.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ page errorPage="execp.jsp"%>
<!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>
	<%
		String strprice = request.getParameter("price");
		double price = Double.parseDouble(strprice);
		out.println("Total price=" + price * 3);
	%>
</body>
</html>

execp.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isErrorPage="true" %>
<!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.println("exception.toString:");
	out.println("<br>");
	out.println(exception.toString());
	out.println("<p>");
	out.println("exception.getMessage():");
	out.println("<br>");
	out.println(exception.getMessage());
%>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

JSP标签

JSP页面中可以嵌套一些Java代码来完成某种功能,但是这种Java代码会使用JSP页面很乱,不利于美工调试和维护,为了减少JSP页面中的Java代码,Sun运行在JSP页面中嵌套一些标签,这些标签可以完成各种通用的JSP页面功能,被称为JSP标签。
<jsp:include>标签
在JSP页面中,为了把其他资源输出内容插入到当前JSP页面的输出内容中,JSP技术提供了<jsp:include>标签,<jsp:include>标签的具体语法格式

<jsp:include page="relativeURL" flush="ture|false"/>

include.jsp

<%@ page language="java" contentType="text/html; charset=GB2312"%>
<% Thread.sleep(5000); %>
included.jsp内的中文<br>

dynamicInclude.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>
	dynamicInclude中的中文
	<br>
	<jsp:include page="included.jsp" flush="true" />
</body>
</html>

在这里插入图片描述
在这里插入图片描述
浏览器打开发现首先输出了dynamicInclude的内容,再等待5秒钟,会再显示included的内容
注意included.jsp的contextType中设置的字符集是GB2312但是中文并没有乱码,这是因为jsp:include不会更改http的响应码和响应头。

将flush属性修改为false
在这里插入图片描述
再次访问
在这里插入图片描述

在等待5秒后两个jsp内容同时输出了出来

注:JSP可以设置虚拟路径,但是设置后,jsp:include的page属性的相对路径就不再能找到相应的文件了。
include指令和<jsp:include>标签进行比较
1、<jsp:include>标签中要引入的资源和当前JSP页面是两个彼此独立的执行实体,即被动态引入的资源必须能够被Web容器独立执行。而include指令只能引入遵循JSP格式的文件,被引入文件与当前JSP文件需要共同合并才能翻译成一个Servlet源文件。
2、<jsp:include>标签中引入的资源是在运行时才包括的,而且中包含运行结果。而include指令引入的资源是在编译时期包括的,包含的是源代码。
3、<jsp:include>标签运行原理与RequestDispatcher.include方法类似,既包含的页面不能改变响应状态码或者设置响应头,而include指令没有这方面的限制。
<jsp:forward>标签
在JSP页面中,经常需要将请求转发给另外一个资源,这时,除了RequestDispatcher接口的forward()方法可以实现外,还可以通过<jsp:forward>标签来实现。

<jsp:forword page="relativeURL">

如果直接在页面中调用pageContext.forward方法,当forward()方法后还有语句需要执行时,程序会发生异常。但是<jsp:forward>标签被翻译成了pageContext.forward并在之后调用了return语句,所以结束了service()方法的执行流程,所以不会出现异常,但是<jsp:forward>之后的语句也不会再执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值