JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它是由Sun Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准。JSP技术有点类似ASP技术,它是在传统的网页HTML(标准通用标记语言的子集)文件(.htm,.html)中插入Java程序段(Scriptlet)和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。 用JSP开发的Web应用是跨平台的,既能在Linux下运行,也能在其他操作系统上运行。
Servlet是JSP的技术基础,而且大型的Web应用程序的开发需要Java Servlet和JSP配合才能完成。JSP具备了Java技术的简单易用,完全的面向对象,具有平台无关性且安全可靠,主要面向因特网的所有特点。通常在项目开发中,都是以JSP来编写展示层面,Servlet则编写逻辑层。
因为在JSP中可以写HTML代码,在Servlet中则不行,就是因为在Servlet中不能直接写HTML代码,所以我们想要生成一个页面相当麻烦,只能通过Java代码一行行的输出HTML代码。而JSP就是为了解决这种问题所发展出来的技术,在JSP中我们即可以编写HTML代码也可以编写Java代码,不过主要以页面代码为主,所以才说JSP用来写展示层而Servlet则编写逻辑层。从以上介绍就可以得知JSP与Servlet最大的区别就是:前者可以在页面代码中编写Java代码,而后者只能是在Java代码中编写页面代码。只需要想想页面代码多还是Java代码多,就知道为什么需要有JSP这个技术了233。
<br>
JSP的访问流程与原理
想要访问JSP文件很简单就好像访问HTML文件一样,直接在浏览器的地址栏上输入JSP文件的路径,一回车就可以访问了:
<br>
其实看似简单的背后,是需要经历好几个步骤的,下面我们就来看看JSP背后的运行流程原理:
首先打开Tomcat中的web.xml文件,可以找到以下配置语句:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
..........................省略中间的一大堆注释
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
从以上这段配置信息,就可以知道,JSP其实就是个Servlet,.jsp和.jspx其实就只是个web访问名称,而映射的是JspServlet这个类。所以才说Servlet是JSP的技术基础,JSP背后就是一个Servlet。
以上示例使用浏览器访问jsp文件时,浏览器发送请求到服务器上,服务器会先去这个web.xml文件中找到jsp或jspx的访问映射,就会找到被映射的JspServlet类,然后这个JspServlet就会去JSP对象池中询问是否有与访问请求相对应的JSP对象,如果有的话就拿出来调用service方法,进入服务阶段。如果没有的话,就会去工程目录下找到被访问的jsp文件,然后就会以此文件为基础生成一个java源文件,接着会把这个源文件编译成class文件,并实例化此类的对象,实例化后将对象放进对象池里,最后再拿出对象调用service方法进入服务阶段。和Servlet一样,进入服务阶段也是执行完处理请求的逻辑代码,生成响应数据后,将响应数据返回给浏览器。而且整个生命周期中,JSP对象也是只有一个。
大致流程图:
<br>
我们来看看生成的java源文件,这个文件的所在的路径你可以在Tomcat服务器启动时,在控制台中的打印信息中找到,我这里的路径如下(可能和你的不一样):
E:\Java_WebTestProject.metadata.plugins\org.eclipse.wst.server.core\tmp0
然后此目录下有个work文件夹,在此文件夹中有很多子文件夹,你需要在里面找到你的web工程名称的文件夹,找到之后点击进入,也是会有几层目录,一直点进去就会找到 index_jsp.java 源文件和一个 index_jsp.class 文件(如果你的jsp文件名叫index.jsp的话),打开这个java源文件,内容如下:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/9.0.1
* Generated at: 2017-11-27 13:45:29 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 index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
...............................以下代码省略
以上可以看到这个index_jsp 类继承了HttpJspBase,那么来看看HttpJspBase继承了谁:
package org.apache.jasper.runtime;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.jsp.HttpJspPage;
import org.apache.jasper.compiler.Localizer;
public abstract class HttpJspBase extends HttpServlet
implements HttpJspPage
{
...............................以下代码省略
从源码可以看到HttpJspBase继承了HttpServlet,所以实际上这个jsp就是继承了HttpServlet,只不过中间插了个HttpJspBase进来而已,因为这个HttpJspBase里面没什么代码,基本都是空的。从以上可以得知,jsp其实就是个Servlet,只不过是换了个形态的Servlet罢了,所以学JavaWeb的时候要先学Servlet,然后再学JSP的时候就简单多了。
既然知道jsp背后就是一个Servlet,那么我们来看看这个背后的Servlet是如何实现我们的页面代码的,从 index_jsp.java 文件中翻到差不多最后面,可以看到一段拼凑的HTML代码:
...............................以上代码省略
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>\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<h1>Hello I'm JSP!</h1>\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);
}
}
}
接着再看看index.jsp文件中的内容:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<h1>Hello I'm JSP!</h1>
</body>
</html>
对比这两个文件中的代码之后,就能明白是怎么实现页面代码的了,JSP的机制就是让我们苦逼写代码的轻松了一些,不再需要自己去手写拼凑这些页面代码,JSP的机制会自动帮我们生成这些代码,怕不是要写封感谢信给sun公司才行。
除了页面代码外,当我们在JSP文件中写的Java代码也会自动生成到此源文件中,至于如何在JSP文件中编写Java代码会在以下小节中介绍。
<br>
JSP指令
以上介绍完JSP之后,这一小节就是介绍如何在JSP文件中编写Java代码和JSP的指令。我们的Java代码就需要写在JSP指令里,因为需要通过指令来表明这是一段Java代码,而不能像HTML代码那样能直接编写在JSP文件中。JSP的指令分为两类,一类是页面指令,一类是代码指令,Java代码就需要写在代码指令里。
页面指令:
指令 | 作用 | 示例 |
---|---|---|
<%@ page %> | 用于设置页面上的参数,例如缓存、输出格式、编码格式等,<br>还能用于引入java的类包,这个指令是最常用的指令。 | <%@ page contentType="text/html" %> |
<%@ include %> | 标签库指令taglib –标签库指令描述了要使用的JSP标签库。<br>该指令需要指定一个前缀prefix(和C++的命名空间很类似)和标签库的描述URI | <%@ taglib prefix="myprefix" uri="taglib/mytag.tld" %> |
<%@ taglib %> | 包含指令include –包含指令通知JSP编译器把另外一个文件完<br>全包含入当前文件中。效果就好像被包含文件的内容直接被粘贴到当前文件中一样。这个功能和C预处理器所提供的很类似。被包含文件的<br>扩展名一般都是"jspf"(即JSPFragment,JSP碎片) | <%@ include file="somefile.jsp" %> |
下面简单的介绍一下<%@ page %>指令里的常见属性使用,因为其他两个几乎不使用,所以在这里就不做演示了:
<!-- 定义编程语言,目前仅支持Java -->
<%@ page language="java" %>
<!-- 定义响应的文件类型和编码格式 -->
<%@ contentType="text/html;charset=utf-8" %>
<!-- 定义页面的编码格式 -->
<%@ pageEncoding="utf-8" %>
<!-- 定义缓存的大小 -->
<%@ page buffer="20kb" %>
<!-- 使用此属性导入一个包 -->
<%@ page import="java.util.HashMap" %>
<!-- 定义此页面是否主动创建session对象 -->
<%@ page session="false" %>
<!-- 定义是否接收错误页面转发过来的数据,也就是定义当前是否是一个错误页面 -->
<%@ page isErrorPage="true" %>
<!-- 定义发生错误时跳转的错误页面 -->
<%@ page errorPage="error.jsp" %>
<!-- 配置信息,能够使用Servlet.getServletInfo方法得到这个配置信息 -->
<%@ page info="database handler"%>
<!-- 定义是否开启多线程 -->
<%@ page isThreadSafe="true" %>
<!-- 定义生成的java源文件中所继承的父类,一般不会去定义,而且定义的时候一定要写全名 -->
<%@ page extends="javax.servlet.http.HttpServlet" %>
<!-- 这个命令可以使jsp输出的html时去除多余的空行(jsp上使用EL和tag会产生大量的空格和空行,但是这个命令是从JSP2.1规范以后才得到支持。) -->
<%@ page trimDirectiveWhitespaces="true" %>
指令是能够像以上示例一样写多行的,一些情况下分多几行写会提高可读性。
按照以上配置,重启服务器在浏览器中刷新之前所访问的jsp页面,可能会发生405错误,但是没关系,我们主要是看一下生成的java源文件与之前的源文件发生了什么样的变化:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.HashMap;
public final class index_jsp extends javax.servlet.http.HttpServlet
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
public java.lang.String getServletInfo() {
return "database handler";
}
...............................省略以上代码
try {
response.setContentType("text/html; charset=utf-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, false, 20480, true);
...............................省略以下代码
从以上的代码片段中,可以看到继承类变为了 HttpServlet,并且导入了 java.util.HashMap 包,而且还重写了Servlet中的getServletInfo方法,此方法返回的字符串也是我们在指令中使用info属性所定义的字符串。最后面那段代码可以看到一个20480的数字,这是缓存的大小,在指令中配置的是20kb,这里显示的是字节单位,在没有配置之前默认的是8192,也就是8kb。
<br>
代码指令:
指令 | 作用 | 示例 |
---|---|---|
<% %> | 脚本指令,可以在这个指令里编写Java代码 | <% String str="test"; %> |
<%! %> | 声明指令,属性声明和方法声明都需要写在这个标签里 | <%!<br> String name="lisi"<br>public void method(){<br>} <br>%> |
<%= %> | 表达式指令,可以进行数据的输出 | <%="output data" %> |
简单演示一下这些指令的使用方式,其中<% %>指令比较常用:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<%
// 声明变量
String str="test";
if(str.equals("test")){
// 这个输出是输出到页面上
out.print(str+"<br>");
}
%>
<%!
// 声明属性和方法
String name=null;
public String getName(){
return name;
}
%>
<!-- 这个和这个语句的作用是一样的:out.print("test output"); -->
<%="test output" %>
</body>
</html>
运行结果:
<br>
JSP内置对象
JSP的内置对象共有以下九个,我们可以在JSP中通过内置对象来使用它里面的方法:
- request :实际上就是HttpServletRequest对象,详细内容参考之前介绍HttpServletRequest对象的文章
- response :实际上就是HttpServletResponse 对象,详细内容参考之前介绍HttpServletResponse 对象的文章
- pageContext :这是一个隐含对象,此对象代表jsp页面的上下文关系,能够调用、存取其他隐含对象,使用该对象可以访问页面中的共享数据,在JSP开发中并不经常使用。
- session :实际上就是HttpSession对象,详细内容参考之前介绍HttpSession对象的文章
- application :实际上就是ServletContext 对象,详细内容参考之前介绍ServletContext 对象的文章
- config :实际上就是ServletConfig对象,可以得到web.xml中的初使化参数,详细内容参考之前介绍ServletConfig对象的文章
- out :此对象用于输出数据、字节流,以上的示例中也使用到了
- page :此对象代表jsp这个实体本身,即当前页面有效,相当于java中的this
- exception :这是异常对象,代表运行时的异常
实际上我们在生成的源文件里就可以找到这些内置对象的声明:
...............................省略以上代码
// request 和 response 对象在这里
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;
...............................省略以下代码
其中还有一个exception 对象没有被声明在这里,因为这个对象需要在接收错误的JSP页面上才能获得,这些内置对象并没有什么特别的,你在Servlet中怎么用在JSP中就怎么用。
<br>
错误页面
错误页面是一个专门用来接收JSP运行过程中所发生的错误,错误信息会转发到这个错误页面上。配置错误页面的方式也很简单,首先在错误页面中通过指令的isErrorPage属性定义当前是一个错误页面,然后再非错误页面中使用errorPage属性定义跳转的错误页面即可。示例:
定义跳转的错误页面:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!-- 定义发生错误时跳转的错误页面 -->
<%@ page errorPage="error.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<%
// 故意制造一个错误
System.out.println(1 / 0);
%>
</body>
</html>
定义错误页面:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!-- 定义当前页面是一个错误页面 -->
<%@ page isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>error</title>
</head>
<body>
<!-- 在错误页面中才能使用exception对象 -->
<%=exception.toString() %>
</body>
</html>
运行结果:
你也可以在JSP中使用 try-catch 语句去捕获运行时所发生的异常:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!-- 定义发生错误时跳转的错误页面 -->
<%@ page errorPage="error.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<%
// 故意制造一个错误
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
out.print("发生 ArithmeticException 异常!");
}
%>
</body>
</html>
运行结果:
本文转自 ZeroOne01 51CTO博客,原文链接:http://blog.51cto.com/zero01/2044929,如需转载请自行联系原作者