Java Web 基础 - JSP

JSP 即 Java Server Pages,是一种简化的 Servlet 设计,实现了在 Java 中使用 HTML 标签。JSP 作为一种动态网页技术标准 也是 Java EE 的标准,与Servlet 一样,实在服务器端执行的。

常见动态网站开发技术对比

  • JSP:Java 平台,安全性高,适合开发大型、企业级的 Web 应用程序;
  • ASP.net:.Net 平台,简单易学,但安全性和跨平台性差;
  • PHP:简单高效,成本低、开发周期短,适合中小型企业的 Web 应用开发(LAMP:Linux + Apache + MySQL + PHP)。

页面元素

指令

  • page:位于 JSP 页面顶端,一个页面可以有多个;
  • include:将外部文件嵌入当前 JSP 中,同时解析这个页面中的 JSP 语句;
  • taglib:使用标签库定义新的自定义标签,在 JSP 页面中启用定制行为。

page 指令

<%@ page 
    contentType="text/html;charset=UTF-8" 
    language="java" 
    import="java.util.*"
%>

其中 pageEncoding 是 JSP 文件本身的编码,contentType 的 charset 是指服务端发给客户端时的内容编码,contentType 更常用。

注释

JSP 中的注释有三种

  • HTML 注释(客户端/浏览器可见)
  • JSP 注释(客户端不可见)
  • JSP 脚本注释
<!-- HTML 注释 -->

<%-- JSP 注释 --%>

<%
// 单行注释
/*
    多行注释
*/
%>

脚本

在 JSP 页面中执行 Java 代码。

实例:使用脚本输出九九乘法表

<%!
    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();
    }
%>

表达式

在 JSP 页面中执行表达式,注意不能有分号。

实例:使用表达式打印九九乘法表

<%!
    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;
    }
%>

<!-- 下面这个是表达式 -->
<%=printMultiTable()%>

静态内容

...

生命周期

JSP 实际上是一种 Servlet 对象;通过 tomcat/ work/ Catalina/ localhost 目录可以看到生成的 java 源代码(包含 jsp 初始化、解析执行的方法),其生命周期:

  1. 用户发出请求(如访问 index.jsp)
  2. 服务端判断用户是否首次发起请求:
    2.1 是,JSP 引擎把该 JSP 文件转换成(先执行构造方法)一个 Servlet 对象,生成字节码文件,并执行 jspInit(),访问字节码文件;
    2.2 否,直接访问字节码文件;
  3. 解析执行字节码文件,调用 jspService() 方法。

jspService():用于处理客户端请求,对于每个请求都创建一个新的线程来处理,如果多个客户端同时请求,则 JSP 引擎会创建多个线程(每个客户端对应一个);使用多线程可以大大降低对系统的资源需求,提高系统的并发量和减少响应时间。但也可能存在线程同步问题。由于该 Servlet 常驻于内存,响应速度很快。

<%@ 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>

内置对象

JSP 内置对象是 Web 容器创建的一组对象,不使用 new 关键字就可以使用的内置对象。

  • out
  • request
  • response
  • session
  • application
  • Page, PageContext, exception, config

out 对象

out 对象是缓冲区(内存的一块保存临时数据的区域)相关的 JspWriter 类的实例,向客户端输出内容的对象。

<body>
    <%
        out.println("<h2>静夜思</h2>");
        out.println("床前明月光<br>");
        out.println("疑是地上霜<br>");
        out.flush();
        //out.clear();           //这里会抛出异常。
        out.clearBuffer();       //这里不会抛出异常。
        out.println("举头望明月<br>");
        out.println("低头思故乡<br>");
    %>
    缓冲区大小:<%=out.getBufferSize() %>byte<br>
    缓冲区剩余大小:<%=out.getRemaining() %>byte<br>
    是否自动清空缓冲区:<%=out.isAutoFlush() %><BR>
</body>

request 对象

客户端请求信息被封装在 request 对象中,是 HttpServletRequest 类的实例。request 对象具有请求域,即完成客户端的请求之前该对象会一直有效。

<body>
    <h1>request内置对象</h1>
    <%
        request.setCharacterEncoding("utf-8");   // 无法解决URL传递中文出现的乱码问题,需要修改 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>
    协议类型及版本号:  <%=request.getProtocol() %><br>
    服务器主机名 :<%=request.getServerName() %><br>
    服务器端口号:<%=request.getServerPort() %><BR>
    请求文件的长度 :<%=request.getContentLength() %><BR>
    请求客户端的IP地址:<%=request.getRemoteAddr() %><BR>
    请求的真实路径:<%=request.getRealPath("request.jsp") %><br>
    请求的上下文路径:<%=request.getContextPath() %><BR>
</body>

解决 URL 传递中文乱码问题,修改 Tomcat 配置文件:

<Connector port="8888"
    ...
    URIEncoding="utf-8"
/>

response 对象

包含了响应客户请求的有关信息,是 HttpServletResponse 类的实例,但在 JSP 中很少使用。reponse 对象具有页面作用域(只对某次访问有效,其他页面的 response 对当前页面无效)。

<%@ page language="java" import="java.util.*,java.io.*" contentType="text/html; charset=utf-8" %>
<%
    response.setContentType("text/html;charset=utf-8"); //设置响应的MIMI类型

    out.println("<h1>response内置对象</h1>");
    out.println("<hr>");
    // out.flush();

    PrintWriter outer = response.getWriter();    //获得输出流对象
    outer.println("大家好,我是response对象生成的输出流outer对象");
    // response.sendRedirect("reg.jsp");    //请求重定向
    
    // 请求重定向
    // response.sendRedirect("request.jsp");
    
    // 请求转发
    request.getRequestDispatcher("request.jsp").forward(request, response);
%>
  • 请求重定向:客户端行为,response.sendRedirect(),本质上是两次请求,前一次请求对象不会保存,地址栏 URL 会改变;
  • 请求转发:服务端行为,response.getRequestDispatcher().forward(req, resp); 是一次请求,转发后请求对象会保存,地址栏的 URL 不会改变。

session 对象

在服务端存储的、不同客户端与服务端一次会话的信息(时间概念),第一次 JSP 页面被装载时自动创建 session 对象,是 HttpSession 类的实例,完成会话期管理(从打开浏览器访问服务端到关闭浏览器)。

生命周期:在 Tomcat 后台管理 http://localhost:8080/manager 中可以查看 Session 存储情况。

  • 创建:第一次访问某个 JSP 或者 Servlet 时候,服务器会为当前会话创建一个 SessionId,每次客户端向服务端发送请求时,都会将此 SessionId 携带过去,服务端会对此 SessionId 进行校验;
  • 活动:某次会话中通过超链接打开新页面属于同一次会话,只要当前会话页面没有关闭,重新打开新的窗口访问同一项目资源也是同一次会话(除非所有页面关闭再重新创建);
  • 销毁:调用session.invalidate()方法、Session 过期(超时)、服务器重启都会销毁对象。

关于 session 对象:

  • Tomcat 默认 session 超时时间为 30min;
  • 设置超时时间:在代码中设置 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);
    
        //设置当前session最大生成期限单位是秒
        //session.setMaxInactiveInterval(10);//10秒钟
    %>
    Session创建时间:<%=sdf.format(d)%><br>
    Session的ID编号:<%=session.getId()%><BR>
    从Session中获取用户名:<%=session.getAttribute("username") %><br>
    <a href="session_page2.jsp" target="_blank">跳转到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 类的实例,实现了用户间数据共享,可存放全局变量;
  • 创建于服务器启动,终止于服务器关闭(服务器启动关闭决定 application 对象);
  • 在用户前后连接、或不同用户之间的连接中,可以对 application 对象的同一属性进行操作;
  • 任何地方对 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 对象

指向当前 JSP 页面本身,类似类中的 this 指针,是 java.lang.Object 类的实例。

pageContext 对象

  • 提供了对 JSP 页面内所有对象及命名空间的访问(汇集了页面中的所有功能);
  • 可以访问到本页所在的 Session,可以取本页所在的 application 的某一属性值;
  • 本类名也为pageContext
<body>
    <h1>pageContext内置对象</h1>
    <hr>
    用户名是:<%=pageContext.getSession().getAttribute("username") %><br>
    <%
        // 跳转到注册页面
        // pageContext.forward("reg.jsp");
        pageContext.include("include.jsp");
    %>
</body>

config 对象

在 Servlet 初始化时,JSP 引擎向它传递信息使用到 config 对象,此信息包括 Servlet 初始化时用到的参数(通过属性名称和属性值构成)以及服务器的基本信息(通过传递一个 ServletContext 对象),常用方法:

  • getServletContext:返回含有服务器相关信息的 ServletContext 对象;
  • getInitParameter:返回初始化参数的值;
  • getInitParameterNames:返回 Servlet 初始化所需所有参数值的枚举;
  • ...

exception 对象

页面运行异常时产生,当一个 JSP 页面要应用此对象,必须把 isErrorPage 设置为 true,否则无法通过编译,实际上是 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); //抛出运行时异常,算数异常
    %>
</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 实现用户登录

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 则登录成功,服务器转发;失败则重定向到失败页面
        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:登录成功转发页面

<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">
        登录失败!请检查用户或者密码!<br>
        <a href="login.jsp">返回登录</a>
    </div>
</div>

JSP 状态管理

HTTP 协议的无状态性:浏览器发送请求给服务器,服务器响应客户端请求,但当同一浏览器再次发送请求给服务器时,服务器并不知道它就是之前那个浏览器。

保存用户状态的两大机制:

  • Cookie
  • Session

实例:JSP 中使用 Cookie 实现用户登录

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) {      // 首先判断用户是否选择了记住登录状态
    
            String username = URLEncoder.encode(request.getParameter("username"), "utf-8");     // 把用户名和密码保存在Cookie对象里面
            String password = URLEncoder.encode(request.getParameter("password"), "utf-8");     // 使用URLEncoder解决无法在Cookie当中保存中文字符串问题
    
            Cookie usernameCookie = new Cookie("username", username);
            Cookie passwordCookie = new Cookie("password", password);
            usernameCookie.setMaxAge(864000);
            passwordCookie.setMaxAge(864000);       // 设置最大生存期限为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);             // 设置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"/>十天内记住我的登录状态</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 中加入传递参数

<body>
    <jsp:forward page="user.jsp">
        <jsp:param value="admin@123.net" name="email"/>
        <jsp:param value="888888" name="password"/>
    </jsp:forward>
</body>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容