初学者的 JSP 示例教程
欢迎阅读面向初学者的 JSP 示例教程。在最近的几篇文章中,我写了很多有关 Java Servlet 的内容,并得到了读者的积极反响。因此,我开始了另一系列 JSP 教程,这是该系列的第一篇文章。
JSP 示例教程
在本 JSP 示例教程中,我们将研究 JSP 的基础知识、JSP 相对于 Servlet 的优势、JSP 的生命周期、JSP API 接口和类以及我们可以将 JSP 文件放在 Web 应用程序中的什么位置。我们还将简要介绍 JSP 注释、Scriptlet、指令、表达式、声明和 JSP 属性。其中一些主题非常重要,我们将在以后的文章中更详细地介绍它们。
JSP 教程
- 什么是 JSP 以及为什么我们需要 JSP?
- JSP 相对于 Servlet 有哪些优势?
- JSP页面的生命周期
- JSP 的生命周期方法
- 使用 Eclipse 和 Tomcat 的简单 JSP 示例
- Web 应用程序 WAR 文件中的 JSP 文件位置
- JSP API 接口和类
- JspPage 接口
- HttpJspPage 接口
- JspWriter 抽象类
- JspContext 抽象类
- PageContext 抽象类
- JspFactory 抽象类
- JspEngineInfo 抽象类
- ErrorData 最终类
- JspException 类
- JspTagException 类
- SkipPageException 类
-
什么是 JSP 以及为什么我们需要 JSP?
JSP (JavaServer Pages)是一种用于创建动态Java Web 应用程序的服务器端技术。JSP 可以看作是 servlet 技术的扩展,因为它提供了轻松创建用户视图的功能。JSP 页面由 HTML 代码组成,并提供包含动态内容的 Java 代码的选项。由于 Web 应用程序包含大量用户屏幕,因此 JSP 在 Web 应用程序中被广泛使用。为了弥合 JSP 中 Java 代码和 HTML 之间的差距,它提供了附加功能,例如 JSP 标记、表达式语言、自定义标记。这使得它易于理解并帮助 Web 开发人员快速开发 JSP 页面。
-
JSP 相对于 Servlet 有哪些优势?
- 我们也可以从 servlet 生成 HTML 响应,但该过程繁琐且容易出错,当需要编写复杂的 HTML 响应时,在 servlet 中编写将是一场噩梦。JSP 可帮助解决这种情况,并为我们提供了编写普通 HTML 页面的灵活性,并且仅在需要时包含我们的 Java 代码。
- JSP 提供了标签库、表达式语言、自定义标签等附加功能,有助于更快地开发用户视图。
- JSP 页面易于部署,我们只需在服务器中替换修改后的页面,容器将负责部署。对于 servlet,我们需要重新编译并再次部署整个项目。实际上 Servlet 和 JSP 是相辅相成的。我们应该使用 Servlet 作为服务器端控制器并与模型类进行通信,而 JSP 应该用于表示层。
-
JSP页面的生命周期
JSP life cycle is also managed by container. Usually every web container that contains servlet container also contains JSP container for managing JSP pages. JSP pages life cycle phases are:
- Translation - JSP pages doesn’t look like normal java classes, actually JSP container parse the JSP pages and translate them to generate corresponding servlet source code. If JSP file name is home.jsp, usually its named as home_jsp.java.
- Compilation - If the translation is successful, then container compiles the generated servlet source file to generate class file.
- Class Loading - Once JSP is compiled as servlet class, its lifecycle is similar to servlet and it gets loaded into memory.
- Instance Creation - After JSP class is loaded into memory, its object is instantiated by the container.
- Initialization - The JSP class is then initialized and it transforms from a normal class to servlet. After initialization, ServletConfig and ServletContext objects become accessible to JSP class.
- Request Processing - For every client request, a new thread is spawned with ServletRequest and ServletResponse to process and generate the HTML response.
- Destroy - Last phase of JSP life cycle where it’s unloaded into memory.
-
Life cycle methods of JSP
JSP lifecycle methods are:
- jspInit() declared in JspPage interface. This method is called only once in JSP lifecycle to initialize config params.
- _jspService(HttpServletRequest request, HttpServletResponse response) declared in HttpJspPage interface and response for handling client requests.
- jspDestroy() declared in JspPage interface to unload the JSP from memory.
-
Simple JSP Example with Eclipse and Tomcat
We can use Eclipse IDE for building dynamic web project with JSPs and use Tomcat to run it. Please read [Java Web Applications](/community/tutorials/java-web-application-tutorial-for-beginners#first-web-app-servlet) tutorial to learn how can we easily create JSPs in Eclipse and run it in tomcat. A simple JSP example page example is: `home.jsp`
```
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>First JSP</title>
</head>
<%@ page import="java.util.Date" %>
<body>
<h3>Hi Pankaj</h3><br>
<strong>Current Time is</strong>: <%=new Date() %>
</body>
</html>
```
If you have a simple JSP that uses only JRE classes, we are not required to put it as WAR file. Just create a directory in the tomcat webapps folder and place your JSP file in the newly created directory. For example, if your JSP is located at apache-`tomcat/webapps/test/home.jsp`, then you can access it in browser with URL `https://localhost:8080/test/home.jsp`. If your host and port is different, then you need to make changes in URL accordingly.
We can place JSP files at any location in the WAR file, however if we put it inside the WEB-INF directory, we wont be able to access it directly from client. We can configure JSP just like servlets in web.xml, for example if I have a JSP example page like below inside WEB-INF directory: `test.jsp`
```
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Test JSP</title>
</head>
<body>
Test JSP Page inside WEB-INF folder.<br>
Init Param "test" value =<%=config.getInitParameter("test") %><br>
HashCode of this object=<%=this.hashCode() %>
</body>
</html>
```
And I configure it in web.xml configuration as:
```
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>FirstJSP</display-name>
<servlet>
<servlet-name>Test</servlet-name>
<jsp-file>/WEB-INF/test.jsp</jsp-file>
<init-param>
<param-name>test</param-name>
<param-value>Test Value</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/Test.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Test1</servlet-name>
<jsp-file>/WEB-INF/test.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>Test1</servlet-name>
<url-pattern>/Test1.do</url-pattern>
</servlet-mapping>
</web-app>
```
Then I can access it with both the URLs https://localhost:8080/FirstJSP/Test.do and https://localhost:8080/FirstJSP/Test1.do Notice that container will create two instances in this case and both will have their own servlet config objects, you can confirm this by visiting these URLs in browser. For Test.do URI, you will get response like below.
```
Test JSP Page inside WEB-INF folder.
Init Param "test" value =Test Value
HashCode of this object=1839060256
```
For Test1.do URI, you will get response like below.
```
Test JSP Page inside WEB-INF folder.
Init Param "test" value =null
HashCode of this object=38139054
```
Notice the init param value in second case is null because it's not defined for the second servlet, also notice the hashcode is different. If you will make further requests, the hashcode value will not change because the requests are processed by spawning a new thread by the container. Did you noticed the use of **config** variable in above JSP example but there is no variable declared, it's because its one of the 9 implicit objects available in JSP page, read more about them at [**JSP Implicit Objects**](/community/tutorials/jsp-implicit-objects "JSP Implicit Objects with Examples").
All the core JSP interfaces and classes are defined in `javax.servlet.jsp` package. Expression Language API interfaces are classes are part of `javax.servlet.jsp.el` package. JSP Tag Libraries interfaces and classes are defined in `javax.servlet.jsp.tagext` package. Here we will look into interfaces and classes of Core JSP API.
-
JspPage Interface
JspPage interface extends Servlet interface and declares jspInit() and jspDestroy() life cycle methods of the JSP pages.
-
HttpJspPage Interface
HttpJspPage interface describes the interaction that a JSP Page Implementation Class must satisfy when using the HTTP protocol. This interface declares the service method of JSP page for HTTP protocol as public void _jspService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException.
-
JspWriter abstract Class
Similar to PrintWriter in servlets with additional facility of buffering support. This is one of the implicit variables in a JSP page with name “out”. This class extends java.io.Writer and container provide their own implementation for this abstract class and use it while translating JSP page to Servlet. We can get it’s object using PageContext.getOut() method. Apache Tomcat concrete class for JspWriter is
org.apache.jasper.runtime.JspWriterImpl
. -
JspContext abstract Class
JspContext 是 PageContext 类的基类,它抽象了所有非 servlet 特有的信息。JspContext 提供了获取 JspWriter 进行输出的机制、使用属性的机制以及管理各种范围命名空间的 API。
-
PageContext 抽象类
PageContext 扩展了 JspContext,以便在将 JSP 用于 Web 应用程序时提供有用的上下文信息。PageContext 实例提供对与 JSP 页面相关联的所有命名空间的访问、对多个页面属性的访问以及实现细节之上的一层。隐式对象会自动添加到 pageContext。
-
JspFactory 抽象类
JspFactory 是一个抽象类,它定义了许多在运行时可供 JSP 页面使用的工厂方法,用于创建用于支持 JSP 实现的各种接口和类的实例。
-
JspEngineInfo 抽象类
JspEngineInfo 是一个抽象类,提供有关当前 JSP 引擎的信息。
-
ErrorData 最终类
包含有关错误的信息,用于错误页面。
-
JspException 类
JSP 容器已知的通用异常,类似于 ServletException。如果 JSP 页面抛出 JspException,则使用错误页面机制向用户显示错误信息。
-
JspTagException 类
标签处理程序使用异常来指示某些不可恢复的错误。
-
SkipPageException 类
表示调用页面必须停止评估的异常。由简单标记处理程序抛出,表示不得评估页面的剩余部分。不应在 JSP 页面中手动抛出此异常。
Since JSP is built on top of HTML, we can write comments in JSP file like html comments as `<-- This is HTML Comment -->` These comments are sent to the client and we can look it with view source option of browsers. We can put comments in JSP files as: `<%-- This is JSP Comment--%>` This comment is suitable for developers to provide code level comments because these are not sent in the client response.
Scriptlet tags are the easiest way to put java code in a JSP page. A scriptlet tag starts with `<%` and ends with `%>`. Any code written inside the scriptlet tags go into the `_jspService()` method. For example:
```
<%
Date d = new Date();
System.out.println("Current Date="+d);
%>
```
Since most of the times we print dynamic data in JSP page using _out.print()_ method, there is a shortcut to do this through JSP Expressions. JSP Expression starts with `<%=` and ends with `%>`. `<% out.print("Pankaj"); %>` can be written using JSP Expression as `<%= "Pankaj" %>` Notice that anything between `<%= %>` is sent as parameter to `out.print()` method. Also notice that scriptlets can contain multiple java statements and always ends with semicolon (;) but expression doesn't end with semicolon.
JSP Directives are used to give special instructions to the container while JSP page is getting translated to servlet source code. JSP directives starts with `<%@` and ends with `%>` For example, in above JSP Example, I am using _page_ directive to to instruct container JSP translator to import the Date class.
JSP Declarations are used to declare member methods and variables of servlet class. JSP Declarations starts with `<%!` and ends with `%>`. For example we can create an int variable in JSP at class level as `<%! public static int count=0; %>`
Once JSP files are translated to Servlet source code, the source code (.java) and compiled classes both are place in **Tomcat/work/Catalina/localhost/FirstJSP/org/apache/jsp** directory. If the JSP files are inside other directories of application, the directory structure is maintained. For JSPs inside WEB-INF directory, its source and class files are inside **Tomcat/work/Catalina/localhost/FirstJSP/org/apache/jsp/WEB\_002dINF** directory. Here is the source code generated for above test.jsp page. `test_jsp.java`
```
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.32
* Generated at: 2013-08-21 03:40:59 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.WEB_002dINF;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
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=US-ASCII");
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("\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"https://www.w3.org/TR/html4/loose.dtd\">\n");
out.write("<html>\n");
out.write("<head>\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">\n");
out.write("<title>Test JSP</title>\n");
out.write("</head>\n");
out.write("<body>\n");
out.write("Test JSP Page inside WEB-INF folder.<br>\n");
out.write("Init Param \"test\" value =");
out.print(config.getInitParameter("test") );
out.write("<br>\n");
out.write("HashCode of this object=");
out.print(this.hashCode() );
out.write("\n");
out.write("</body>\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 { 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);
}
}
}
```
Notice following points in above servlet code;
- The package of class starts with org.apache.jsp and if JSPs are inside other folders, it includes directory hierarchy too. Usually we dont care about it.
- The generates servlet class is final and can't be extended.
- It extends `org.apache.jasper.runtime.HttpJspBase` that is similar to HttpServlet except that it's internal to Tomcat JSP Translator implementation. HttpJspBase extends HttpServlet and implements HttpJspPage interface.
- Notice the local variables at the start of \_jspService() method implementation, they are automatically added by JSP translator and available for use in service methods, i.e in scriptlets.As a java programmer, sometimes it helps to look into the generated source for debugging purposes.
We can define init parameters for the JSP page as shown in above example and we can retrieve them in JSP using **config** implicit object, we will look into implicit objects in JSP in more detail in future posts.
We can override JSP init method for creating resources to be used by JSP service() method using JSP Declaration tags, we can override jspInit() and jspDestroy() or any other methods also. However we should never override \_jspService() method because anything we write in JSP goes into service method.
Apart from standard servlet attributes with request, session and context scope, in JSP we have another scope for attributes, i.e Page Scope that we can get from pageContext object. We will look it's importance in custom tags tutorial. For normal JSP programming, we don't need to worry about page scope.
这就是面向初学者的 JSP 示例教程的全部内容。希望它能帮助您理解 JSP 的基本概念并帮助您入门。我们将在以后的文章中探讨其他 JSP 功能。