Java Web 基礎(chǔ) - JSP

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 + "&nbsp;&nbsp;&nbsp;&nbsp;");
            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 + "&nbsp;&nbsp;&nbsp;&nbsp;";
            s += "<br>";
        }
        return s;
    }
%>

<!-- 下面這個是表達(dá)式 -->
<%=printMultiTable()%>

靜態(tài)內(nèi)容

...

生命周期

JSP 實(shí)際上是一種 Servlet 對象;通過 tomcat/ work/ Catalina/ localhost 目錄可以看到生成的 java 源代碼(包含 jsp 初始化、解析執(zhí)行的方法),其生命周期:

  1. 用戶發(fā)出請求(如訪問 index.jsp)
  2. 服務(wù)端判斷用戶是否首次發(fā)起請求:
    2.1 是,JSP 引擎把該 JSP 文件轉(zhuǎn)換成(先執(zhí)行構(gòu)造方法)一個 Servlet 對象,生成字節(jié)碼文件,并執(zhí)行 jspInit(),訪問字節(jié)碼文件;
    2.2 否,直接訪問字節(jié)碼文件;
  3. 解析執(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] + "&nbsp;&nbsp;");
            }
        }
    %> <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] + "&nbsp;&nbsp;");
        }     
    %> 
</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() + "&nbsp;&nbsp;");
        }
    %><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 指令與動作

指令與動作的區(qū)別

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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容