JSP 即 Java Server Pages,是一種簡化的 Servlet 設(shè)計,實(shí)現(xiàn)了在 Java 中使用 HTML 標(biāo)簽。JSP 作為一種動態(tài)網(wǎng)頁技術(shù)標(biāo)準(zhǔn) 也是 Java EE 的標(biāo)準(zhǔn),與Servlet 一樣,實(shí)在服務(wù)器端執(zhí)行的。
常見動態(tài)網(wǎng)站開發(fā)技術(shù)對比
- JSP:Java 平臺,安全性高,適合開發(fā)大型、企業(yè)級的 Web 應(yīng)用程序;
- ASP.net:.Net 平臺,簡單易學(xué),但安全性和跨平臺性差;
- PHP:簡單高效,成本低、開發(fā)周期短,適合中小型企業(yè)的 Web 應(yīng)用開發(fā)(LAMP:Linux + Apache + MySQL + PHP)。
頁面元素
指令
- page:位于 JSP 頁面頂端,一個頁面可以有多個;
- include:將外部文件嵌入當(dāng)前 JSP 中,同時解析這個頁面中的 JSP 語句;
- taglib:使用標(biāo)簽庫定義新的自定義標(biāo)簽,在 JSP 頁面中啟用定制行為。
page 指令
<%@ page
contentType="text/html;charset=UTF-8"
language="java"
import="java.util.*"
%>
其中 pageEncoding 是 JSP 文件本身的編碼,contentType 的 charset 是指服務(wù)端發(fā)給客戶端時的內(nèi)容編碼,contentType 更常用。
注釋
JSP 中的注釋有三種
- HTML 注釋(客戶端/瀏覽器可見)
- JSP 注釋(客戶端不可見)
- JSP 腳本注釋
<!-- HTML 注釋 -->
<%-- JSP 注釋 --%>
<%
// 單行注釋
/*
多行注釋
*/
%>
腳本
在 JSP 頁面中執(zhí)行 Java 代碼。
實(shí)例:使用腳本輸出九九乘法表
<%!
void printMultiTable(JspWriter out) throws Exception {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++)
out.println(i + "*" + "=" + i * j + " ");
out.println("<br>");
}
}
%>
<% printMultiTable(out); %>
聲明
在 JSP 頁面中定義變量或方法
<%!
String name = "ywh";
Integer age = 16;
String getDesc(String name, Integer age) {
return "name: " + name + ", age: " + age.toString();
}
%>
表達(dá)式
在 JSP 頁面中執(zhí)行表達(dá)式,注意不能有分號。
實(shí)例:使用表達(dá)式打印九九乘法表
<%!
String printMultiTable() {
String s = "";
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++)
s += i + "*" + "=" + i * j + " ";
s += "<br>";
}
return s;
}
%>
<!-- 下面這個是表達(dá)式 -->
<%=printMultiTable()%>
靜態(tài)內(nèi)容
...
生命周期
JSP 實(shí)際上是一種 Servlet 對象;通過 tomcat/ work/ Catalina/ localhost 目錄可以看到生成的 java 源代碼(包含 jsp 初始化、解析執(zhí)行的方法),其生命周期:
- 用戶發(fā)出請求(如訪問 index.jsp)
- 服務(wù)端判斷用戶是否首次發(fā)起請求:
2.1 是,JSP 引擎把該 JSP 文件轉(zhuǎn)換成(先執(zhí)行構(gòu)造方法)一個 Servlet 對象,生成字節(jié)碼文件,并執(zhí)行jspInit(),訪問字節(jié)碼文件;
2.2 否,直接訪問字節(jié)碼文件; - 解析執(zhí)行字節(jié)碼文件,調(diào)用
jspService()方法。
jspService():用于處理客戶端請求,對于每個請求都創(chuàng)建一個新的線程來處理,如果多個客戶端同時請求,則 JSP 引擎會創(chuàng)建多個線程(每個客戶端對應(yīng)一個);使用多線程可以大大降低對系統(tǒng)的資源需求,提高系統(tǒng)的并發(fā)量和減少響應(yīng)時間。但也可能存在線程同步問題。由于該 Servlet 常駐于內(nèi)存,響應(yīng)速度很快。
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*"%>
<%@ page import="java.text.*" %>
<html>
<head>
<title>JSP</title>
</head>
<body>
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String s = sdf.format(new Date());
%>
today: <%=s%>
</body>
</html>
內(nèi)置對象
JSP 內(nèi)置對象是 Web 容器創(chuàng)建的一組對象,不使用 new 關(guān)鍵字就可以使用的內(nèi)置對象。
- out
- request
- response
- session
- application
- Page, PageContext, exception, config
out 對象
out 對象是緩沖區(qū)(內(nèi)存的一塊保存臨時數(shù)據(jù)的區(qū)域)相關(guān)的 JspWriter 類的實(shí)例,向客戶端輸出內(nèi)容的對象。
<body>
<%
out.println("<h2>靜夜思</h2>");
out.println("床前明月光<br>");
out.println("疑是地上霜<br>");
out.flush();
//out.clear(); //這里會拋出異常。
out.clearBuffer(); //這里不會拋出異常。
out.println("舉頭望明月<br>");
out.println("低頭思故鄉(xiāng)<br>");
%>
緩沖區(qū)大小:<%=out.getBufferSize() %>byte<br>
緩沖區(qū)剩余大?。?lt;%=out.getRemaining() %>byte<br>
是否自動清空緩沖區(qū):<%=out.isAutoFlush() %><BR>
</body>
request 對象
客戶端請求信息被封裝在 request 對象中,是 HttpServletRequest 類的實(shí)例。request 對象具有請求域,即完成客戶端的請求之前該對象會一直有效。
<body>
<h1>request內(nèi)置對象</h1>
<%
request.setCharacterEncoding("utf-8"); // 無法解決URL傳遞中文出現(xiàn)的亂碼問題,需要修改 Tomcat 配置文件
request.setAttribute("password", "123456");
%>
用戶名:<%=request.getParameter("username") %><br>
愛好 :<%
if (request.getParameterValues("favorite") != null) {
String[] favorites = request.getParameterValues("favorite");
for (int i = 0; i < favorites.length; i++) {
out.println(favorites[i] + " ");
}
}
%> <br>
密碼:<%=request.getAttribute("password") %><br>
請求體的MIME類型:<%=request.getContentType() %><br>
協(xié)議類型及版本號: <%=request.getProtocol() %><br>
服務(wù)器主機(jī)名 :<%=request.getServerName() %><br>
服務(wù)器端口號:<%=request.getServerPort() %><BR>
請求文件的長度 :<%=request.getContentLength() %><BR>
請求客戶端的IP地址:<%=request.getRemoteAddr() %><BR>
請求的真實(shí)路徑:<%=request.getRealPath("request.jsp") %><br>
請求的上下文路徑:<%=request.getContextPath() %><BR>
</body>
解決 URL 傳遞中文亂碼問題,修改 Tomcat 配置文件:
<Connector port="8888"
...
URIEncoding="utf-8"
/>
response 對象
包含了響應(yīng)客戶請求的有關(guān)信息,是 HttpServletResponse 類的實(shí)例,但在 JSP 中很少使用。reponse 對象具有頁面作用域(只對某次訪問有效,其他頁面的 response 對當(dāng)前頁面無效)。
<%@ page language="java" import="java.util.*,java.io.*" contentType="text/html; charset=utf-8" %>
<%
response.setContentType("text/html;charset=utf-8"); //設(shè)置響應(yīng)的MIMI類型
out.println("<h1>response內(nèi)置對象</h1>");
out.println("<hr>");
// out.flush();
PrintWriter outer = response.getWriter(); //獲得輸出流對象
outer.println("大家好,我是response對象生成的輸出流outer對象");
// response.sendRedirect("reg.jsp"); //請求重定向
// 請求重定向
// response.sendRedirect("request.jsp");
// 請求轉(zhuǎn)發(fā)
request.getRequestDispatcher("request.jsp").forward(request, response);
%>
- 請求重定向:客戶端行為,
response.sendRedirect(),本質(zhì)上是兩次請求,前一次請求對象不會保存,地址欄 URL 會改變; - 請求轉(zhuǎn)發(fā):服務(wù)端行為,
response.getRequestDispatcher().forward(req, resp);是一次請求,轉(zhuǎn)發(fā)后請求對象會保存,地址欄的 URL 不會改變。
session 對象
在服務(wù)端存儲的、不同客戶端與服務(wù)端一次會話的信息(時間概念),第一次 JSP 頁面被裝載時自動創(chuàng)建 session 對象,是 HttpSession 類的實(shí)例,完成會話期管理(從打開瀏覽器訪問服務(wù)端到關(guān)閉瀏覽器)。
生命周期:在 Tomcat 后臺管理 http://localhost:8080/manager 中可以查看 Session 存儲情況。
- 創(chuàng)建:第一次訪問某個 JSP 或者 Servlet 時候,服務(wù)器會為當(dāng)前會話創(chuàng)建一個 SessionId,每次客戶端向服務(wù)端發(fā)送請求時,都會將此 SessionId 攜帶過去,服務(wù)端會對此 SessionId 進(jìn)行校驗(yàn);
- 活動:某次會話中通過超鏈接打開新頁面屬于同一次會話,只要當(dāng)前會話頁面沒有關(guān)閉,重新打開新的窗口訪問同一項(xiàng)目資源也是同一次會話(除非所有頁面關(guān)閉再重新創(chuàng)建);
- 銷毀:調(diào)用
session.invalidate()方法、Session 過期(超時)、服務(wù)器重啟都會銷毀對象。
關(guān)于 session 對象:
- Tomcat 默認(rèn)
session超時時間為 30min; - 設(shè)置超時時間:在代碼中設(shè)置
session.setMaxInactiveInterval(xxx); // 秒,或修改 Tomcat 配置文件 web.xml
<session-config>
<session-timeout>10</session-timeout>
</session-config> <!-- 單位:分 -->
page1
<body>
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d = new Date(session.getCreationTime());
session.setAttribute("username", "admin");
session.setAttribute("password", "123456");
session.setAttribute("age", 20);
//設(shè)置當(dāng)前session最大生成期限單位是秒
//session.setMaxInactiveInterval(10);//10秒鐘
%>
Session創(chuàng)建時間:<%=sdf.format(d)%><br>
Session的ID編號:<%=session.getId()%><BR>
從Session中獲取用戶名:<%=session.getAttribute("username") %><br>
<a href="session_page2.jsp" target="_blank">跳轉(zhuǎn)到Session_page2.jsp</a>
</body>
page2
<body>
<hr>
<%
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//Date d = new Date(session.getCreationTime());
//session.setAttribute("username", "admin");
%>
Session的ID編號:<%=session.getId()%><BR>
從Session中獲取用戶名:<%=session.getAttribute("username") %><br>
Session中保存的屬性有:
<%
String[] names = session.getValueNames();
for (int i = 0; i < names.length; i++) {
out.println(names[i] + " ");
}
%>
</body>
application 對象
- 是
ServletContext類的實(shí)例,實(shí)現(xiàn)了用戶間數(shù)據(jù)共享,可存放全局變量; - 創(chuàng)建于服務(wù)器啟動,終止于服務(wù)器關(guān)閉(服務(wù)器啟動關(guān)閉決定
application對象); - 在用戶前后連接、或不同用戶之間的連接中,可以對
application對象的同一屬性進(jìn)行操作; - 任何地方對
application對象屬性的操作,都影響其他用戶對此的訪問。
<body>
<%
application.setAttribute("city", "北京");
application.setAttribute("postcode", "10000");
application.setAttribute("email", "lisi@126.com");
%>
所在城市是:<%=application.getAttribute("city") %><br>
application中的屬性有:<%
Enumeration attributes = application.getAttributeNames();
while (attributes.hasMoreElements()) {
out.println(attributes.nextElement() + " ");
}
%><br>
JSP(SERVLET)引擎名及版本號:<%=application.getServerInfo() %><br>
</body>
page 對象
指向當(dāng)前 JSP 頁面本身,類似類中的 this 指針,是 java.lang.Object 類的實(shí)例。
pageContext 對象
- 提供了對 JSP 頁面內(nèi)所有對象及命名空間的訪問(匯集了頁面中的所有功能);
- 可以訪問到本頁所在的 Session,可以取本頁所在的
application的某一屬性值; - 本類名也為
pageContext。
<body>
<h1>pageContext內(nèi)置對象</h1>
<hr>
用戶名是:<%=pageContext.getSession().getAttribute("username") %><br>
<%
// 跳轉(zhuǎn)到注冊頁面
// pageContext.forward("reg.jsp");
pageContext.include("include.jsp");
%>
</body>
config 對象
在 Servlet 初始化時,JSP 引擎向它傳遞信息使用到 config 對象,此信息包括 Servlet 初始化時用到的參數(shù)(通過屬性名稱和屬性值構(gòu)成)以及服務(wù)器的基本信息(通過傳遞一個 ServletContext 對象),常用方法:
-
getServletContext:返回含有服務(wù)器相關(guān)信息的 ServletContext 對象; -
getInitParameter:返回初始化參數(shù)的值; -
getInitParameterNames:返回 Servlet 初始化所需所有參數(shù)值的枚舉; - ...
exception 對象
頁面運(yùn)行異常時產(chǎn)生,當(dāng)一個 JSP 頁面要應(yīng)用此對象,必須把 isErrorPage 設(shè)置為 true,否則無法通過編譯,實(shí)際上是 java.lang.Throwable 的對象。
test_page
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" errorPage="exception.jsp" %>
<body>
<%
System.out.println(100 / 0); //拋出運(yùn)行時異常,算數(shù)異常
%>
</body>
error_page
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" isErrorPage="true" %>
...
<body>
異常的消息是:<%=exception.getMessage()%><BR>
異常的字符串描述:<%=exception.toString()%><br>
</body>
JSP 實(shí)現(xiàn)用戶登錄
dologin.jsp:登錄邏輯處理
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
String username = "";
String password = "";
request.setCharacterEncoding("utf-8"); // 防止中文亂碼
username = request.getParameter("username");
password = request.getParameter("password");
if ("admin".equals(username) && "admin".equals(password)) { // 如果用戶和密碼都等于 admin 則登錄成功,服務(wù)器轉(zhuǎn)發(fā);失敗則重定向到失敗頁面
session.setAttribute("loginUser", username);
request.getRequestDispatcher("login_success.jsp").forward(request, response);
} else {
response.sendRedirect("login_failure.jsp");
}
%>
login.jsp:登錄請求頁面
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<body>
<div id="container">
<div class="logo">
<a href="#"><img src="assets/logo.png" alt=""/></a>
</div>
<div id="box">
<form action="dologin.jsp" method="post">
<p class="main">
<label>用戶名: </label><input name="username" value=""/>
<label>密碼: </label><input type="password" name="password" value="">
</p>
<p class="space">
<input type="submit" value="登錄" class="login" style="cursor: pointer;"/>
</p>
</form>
</div>
</div>
</body>
login_success.jsp:登錄成功轉(zhuǎn)發(fā)頁面
<body>
<div id="container">
<div class="logo">
<a href="#"><img src="assets/logo.png" alt=""/></a>
</div>
<div id="box">
<%
String loginUser = "";
if (session.getAttribute("loginUser") != null)
loginUser = session.getAttribute("loginUser").toString();
%>
歡迎您<font color="red"><%=loginUser%>
</font>,登錄成功!
</div>
</div>
</body>
login_failure.jsp:登錄失敗重定向頁面
<div id="container">
<div class="logo">
<a href="#"><img src="assets/logo.png" alt=""/></a>
</div>
<div id="box">
登錄失?。≌垯z查用戶或者密碼!<br>
<a href="login.jsp">返回登錄</a>
</div>
</div>
JSP 狀態(tài)管理
HTTP 協(xié)議的無狀態(tài)性:瀏覽器發(fā)送請求給服務(wù)器,服務(wù)器響應(yīng)客戶端請求,但當(dāng)同一瀏覽器再次發(fā)送請求給服務(wù)器時,服務(wù)器并不知道它就是之前那個瀏覽器。
保存用戶狀態(tài)的兩大機(jī)制:
- Cookie
- Session
實(shí)例:JSP 中使用 Cookie 實(shí)現(xiàn)用戶登錄
dologin.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<body>
<%
request.setCharacterEncoding("utf-8");
String[] isUseCookies = request.getParameterValues("isUseCookie");
if (isUseCookies != null && isUseCookies.length > 0) { // 首先判斷用戶是否選擇了記住登錄狀態(tài)
String username = URLEncoder.encode(request.getParameter("username"), "utf-8"); // 把用戶名和密碼保存在Cookie對象里面
String password = URLEncoder.encode(request.getParameter("password"), "utf-8"); // 使用URLEncoder解決無法在Cookie當(dāng)中保存中文字符串問題
Cookie usernameCookie = new Cookie("username", username);
Cookie passwordCookie = new Cookie("password", password);
usernameCookie.setMaxAge(864000);
passwordCookie.setMaxAge(864000); // 設(shè)置最大生存期限為10天
response.addCookie(usernameCookie);
response.addCookie(passwordCookie);
}
else {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username") || c.getName().equals("password")) {
c.setMaxAge(0); // 設(shè)置Cookie失效
response.addCookie(c); // 重新保存。
}
}
}
}
%>
<a href="users.jsp" target="_blank">查看用戶信息</a>
</body>
login.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<body>
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username"))
username = URLDecoder.decode(c.getValue(), "utf-8");
if (c.getName().equals("password"))
password = URLDecoder.decode(c.getValue(), "utf-8");
}
}
%>
<form name="loginForm" action="dologin.jsp" method="post">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username" value="<%=username %>"/></td>
</tr>
<tr>
<td>密碼:</td>
td><input type="password" name="password" value="<%=password %>"/></td>
</tr>
<tr>
<td colspan="2"><input type="checkbox" name="isUseCookie" checked="checked"/>十天內(nèi)記住我的登錄狀態(tài)</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="登錄"/><input type="reset" value="取消"/></td>
</tr>
</table>
</form>
</body>
users.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<body>
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username"))
username = URLDecoder.decode(c.getValue(), "utf-8");
if (c.getName().equals("password"))
password = URLDecoder.decode(c.getValue(), "utf-8");
}
}
%><br>
用戶名:<%=username %><br>
密碼:<%=password %><br>
</body>
指令和動作
include 指令與動作

date.jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%@ page import="java.text.*" %>
<%
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String s = sdf.format(d);
out.println(s);
%>
include_command.jsp
<%@ include file="date.jsp" %>
include_action.jsp
<jsp:include page="date.jsp" flush="false"/>
forward 動作
user.jsp
<body>
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
String email = "";
if (request.getParameter("username") != null)
username = request.getParameter("username");
if (request.getParameter("password") != null)
password = request.getParameter("password");
if (request.getParameter("email") != null)
email = request.getParameter("email");
%>
用戶名:<%=username %><br>
密碼:<%=password %><br>
電子郵箱:<%=email %><br>
</body>
login.jsp
<body>
<form name="loginForm" action="forward.jsp" method="post">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登錄"/></td>
</tr>
</table>
</form>
</body>
forward.jsp
<body>
<jsp:forward page="user.jsp"/>
<!--<% request.getRequestDispatcher("user.jsp").forward(request, response); %>-->
</body>
param 動作
在 forward 中加入傳遞參數(shù)
<body>
<jsp:forward page="user.jsp">
<jsp:param value="admin@123.net" name="email"/>
<jsp:param value="888888" name="password"/>
</jsp:forward>
</body>