JSP 第一篇:
概述、原理、周期、指令、行為、內置對象、JavaBean
(一) JSP概述以及簡單使用
什么是JSP?
JSP全名為Java Server Pages,java服務器頁面。JSP是一種基於文本的程序,其特點就是HTML
和Java代碼共同存在!
為什么需要JSP?
JSP是為了簡化Servlet的工作出現的替代品,Servlet輸出HTML非常困難,JSP就是替代Servlet輸出HTML的
JSP還有必要學嗎?
在MVC中,JSP屬於展示層,但是JSP卻又可以寫一定的業務,甚至跑去做數據層的事情,這樣開發中就會變得無比混亂,也增加了開發的困難程度,所以將展示層與業務層分開就成為了主流,也就是我們說的前后端分離,但是事無絕對,確實一些比較老的項目仍然在跑jsp,不管你會不會寫,你總得碰到能看懂吧,如果已經接近找工作,確實還是以比較流行的技術學習比較好,但是若作為學生,時間還是比較富裕的,很多本科也必然都會講,學習一下也是挺好的,況且JSP與Servlet也是息息相關的,我認為,學它就是為了知道為什么以后會用別的技術代替它(狗頭保命),廢話有點多了,還是有一點需要的朋友可以簡單看一看,希望給你能有一點幫助
(二) JSP的工作原理
Tomcat訪問任何的資源都是在訪問Servlet!,當然了,JSP也不例外!JSP本身就是一種Servlet。為什么說JSP本身就是一種Servlet呢?
其實JSP在第一次被訪問的時候會被編譯為HttpJspPage類(該類是HttpServlet的一個子類)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>簡單使用JSP</title>
</head>
<body>
<%
String s = "HelloWorld";
out.println(s);
%>
編譯過程是這樣子的:
瀏覽器第一次請求1.jsp時,Tomcat會將1.jsp轉化成1_jsp.java這么一個類,並將該文件編譯成class文件。編譯完畢后再運行class文件來響應瀏覽器的請求。
以后訪問1.jsp就不再重新編譯jsp文件了,直接調用class文件來響應瀏覽器。當然了,如果Tomcat檢測到JSP頁面改動了的話,會重新編譯的。
既然JSP是一個Servlet,那JSP頁面中的HTML排版標簽是怎么樣被發送到瀏覽器的?我們來看下上面1_jsp.java的源碼就知道了。原來就是用write()出去的罷了。說到底,JSP就是封裝了Servlet的java程序罷了
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title>簡單使用JSP</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n")
有人可能也會問:JSP頁面的代碼服務器是怎么執行的?再看回1_jsp.java文件,java代碼就直接在類中的service()中
String s = "HelloWorld";
out.println(s);
(三) 聲明周期
JSP也是Servlet,運行時只有一個實例,JSP初始化和銷毀時也會調用Servlet的init()和destroy()方法。另外,JSP還有自己初始化和銷毀的方法
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {
}
(四) 指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
作用:用於配置JSP頁面,導入資源文件
格式: <%@ 指令名稱 屬性名1=屬性值1 屬性名2=屬性值2 ... %>
-
contentType:相當於response.setContentType()
-
設置響應體的mime類型以及字符集
-
設置當前jsp頁面的編碼(只能是高級的IDE才能生效,如果使用低級工具,則需要設置pageEncoding屬性設置當前頁面的字符集)
pageEncoding="characterSet | ISO-8859-1"
-
-
import:導包
import="{package.class | package.*}, ..." -
errorPage:當前頁面發生異常后,會自動跳轉到指定的錯誤頁面
//主頁面 <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %> //錯誤后轉到的頁面 <%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> 我們發現地址欄是沒有變化的,所以屬於是服務器跳轉。以上的做法是單個頁面設置的,如果我會有很多錯誤(JSP多的情況下,錯誤就會多),單個設置太麻煩了! 我們可以在web.xml文件中全局設置錯誤頁,只要發生了404錯誤或者空指針異常的錯誤都會跳轉到error.jsp頁面上 <error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.NullPointerException</exception-type> <location>/error.jsp</location> </error-page> -
isErrorPage:標識當前也是是否是錯誤頁面
- true:是,可以使用內置對象exception
- false:否。默認值。不可以使用內置對象exception
(五) 行為
JSP行為(JSP Actions)是一組JSP內置的標簽,只書寫少量的標記代碼就能夠使用JSP提供豐富的功能,JSP行為是對常用的JSP功能的抽象和封裝。
JSP內置的標簽稱之為JSP行為,是為了能夠和JSTL標簽區分開來。(叫做JSP標簽也行)
(1) include 行為
上面已經提及到了,include指令是靜態包含,include行為是動態包含。其實include行為就是封裝了request.getRequestDispatcher(String url).include(request,response)
include行為語法是這樣的:
<jsp:include page=""/>
靜態包含:<%@ include file="被包含頁面"%>
動態包含:<jsp:include page="被包含頁面" flush="true">
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>包含頁頭和頁尾進來</title>
</head>
<body>
<jsp:include page="head.jsp"/>
<jsp:include page="foot.jsp"/>
</body>
jsp行為包含文件就是先編譯被包含的頁面,再將頁面的結果寫入到包含的頁面中(1.jsp)
當然了,現在有靜態包含和動態包含,使用哪一個更好呢?答案是:動態包含。
動態包含可以向被包含的頁面傳遞參數(用處不大),並且是分別處理包含頁面的(將被包含頁面編譯后得出的結果再寫進包含頁面)
【如果有相同名稱的參數,使用靜態包含就會報錯!】!
(2) Param 行為
當使用<jsp:include>和<jsp:forward>行為引入或將請求轉發給其它資源時,可以使用<jsp:param>行為向這個資源傳遞參數
(3) forward 行為
在Servlet中我們使用request.getRequestDispatcher(String url).forward(request,response)進行跳轉。其實forward行為就是對其封裝!
我們來看一下forward的語法
<jsp:forward page=""/>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>訪問1.jsp就跳轉到head.jsp</title>
</head>
<body>
<jsp:forward page="head.jsp"/>
</body>
</html>
如果我要傳遞參數,就要在forward行為嵌套param行為
在跳轉到head.jsp時傳入參數username值為aaa
<jsp:forward page="head.jsp">
<jsp:param name="username" value="aaa"/>
</jsp:forward>
<%
String ss = request.getParameter("username");
%>
獲取到的參數是:
<%=ss%>
(4) directive 行為
directive的中文意思就是指令。該行為就是替代指令<%@%>的語法的
· <jsp:directive.include file=""/> 相當於<%@include file="" %>
· jsp:directive.page/ 相當於<%@page %>
· jsp:directive.taglib/ 相當於<%@taglib %>
使用該指令可以讓JSP頁面更加美觀!
使用scriptlet行為<jsp:scriptlet>替代<%%>是同樣一個道理
(5) javaBean 行為
JSP還提供了操作javaBean對象的行為,暫時記住JSP提供了javaBean行為來操作簡單類即可!后面詳細解釋:
<jsp:useBean id=""/>
<jsp:setProperty name="" property=""/>
<jsp:getProperty name="" property=""/>
(六) JSP內置對象(直接使用)
JSP引擎在調用JSP對應的jspServlet時,會傳遞或創建9個與web開發相關的對象供jspServlet使用。JSP技術的設計者為便於開發人員在編寫JSP頁面時獲得這些web對象的引用,特意定義了9個相應的變量,開發人員在JSP頁面中通過這些變量就可以快速獲得這9大對象的引用
| 變量名 | 真實類型 | 作用 |
|---|---|---|
| pageContext | PageContext | 當前頁面共享數據,還可以獲取其他八個內置對象 |
| request | HttpServletRequest | 一次請求訪問的多個資源(轉發) |
| session | HttpSession | 一次會話的多個請求間 |
| application | ServletContext | 所有用戶間共享數據 |
| response | HttpServletResponse | 響應對象 |
| page | Object | 當前頁面(Servlet)的對象 this |
| out | JspWriter | 輸出對象,數據輸出到頁面上 |
| config | ServletConfig | Servlet的配置對象 |
| exception | Throwable | 內置對象exception是java.lang.Exception類的對象 |
(七) 四種屬性范圍
到目前為止,我們已經學了4種屬性范圍了。
page【只在一個頁面中保存屬性,跳轉頁面無效】
requet【只在一次請求中保存屬性,服務器跳轉有效,瀏覽器跳轉無效】
session【在一個會話范圍中保存屬性,無論何種跳轉均有效,關閉瀏覽器后無效】
application【在整個服務器中保存,所有用戶都可以使用】
4個內置對象都支持以下的方法:
- setAttribute(String name, Object o )
- getAttribute(String name)
- removeAttribute(String name)
※ 應用場景
- request:如果客戶向服務器發請求,產生的數據,用戶看完就沒用了,像這樣的數據就存在request域,像新聞數據,屬於用戶看完就沒用的
- session:如果客戶向服務器發請求,產生的數據,用戶用完了等一會兒還有用,像這樣的數據就存在session域中,像購物數據,用戶需要看到自己購物信息,並且等一會兒,還要用這個購物數據結帳
- servletContext:如果客戶向服務器發請求,產生的數據,用戶用完了,還要給其它用戶用,像這樣的數據就存在servletContext域中,像聊天數據
(八) JavaBean
avaBean就是一個普通的java類,也稱之為簡單java對象--POJO(Plain Ordinary Java Object),是Java程序設計中一種設計模式,是一種基於 Java 平台的軟件組件思想
JavaBean遵循着特定的寫法,通常有以下的規則:
有無參的構造函數
成員屬性私有化
封裝的屬性如果需要被外所操作,必須編寫public類型的setter、getter方法
上面的文字看起來好像很高大上,javaBean其實非常簡單,常見的學生類,書籍類就是按照特定寫法、規則編寫的一個JavaBean對象
為什么需要使用Javabean
使用javaBean的好處:封裝,重用,可讀!
JaveBean你可以理解為一輛貨車,在你的java端和web頁面進行數據傳遞的載體,你當然可以每個變量單獨傳遞,或者使用集合傳遞,但是javabean可以使你的數據更有可讀性,方便開發時明確變量的意義,也使其他閱讀你代碼的人能直接你的意圖
如果bean類與數據庫聯合使用,一張表使用bean類,可以使你的代碼更加簡潔高效,易於理解,現在大多數框架都會使用這種機制。
JSP行為--JavaBean
JSP技術提供了三個關於JavaBean組件的動作元素,即JSP行為(標簽),它們分別為:
jsp:useBean【在JSP頁面中查找javaBean對象或者實例化javaBean對象】
jsp:setProperty【設置javaBean的屬性】
jsp:getProperty【獲取javaBean的屬性】
※ JSP:useBean
<jsp:useBean>
標簽用於在指定的域范圍內查找指定名稱的JavaBean對象:
存在則直接返回該JavaBean對象的引用。
不存在則實例化一個新的JavaBean對象並將它以指定的名稱存儲到指定的域范圍中。
語法:
jsp:useBean id="實例化對象的名稱" class="類的全名" scope="保存范圍"/>
果JSP不支持<jsp:useBean>這個行為,我們要使用Person類是這樣使用的
<%--這里需要導入Person類--%>
<%@ page import="domain.Person" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<%
//new出對象
Person person = new Person();
person.setName("admin");
System.out.println(person.getName());
%>
</body>
但是我們使用<jsp:useBean>就非常整潔,不用導包,不用new對象
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%
person.setName("zhongfucheng");
System.out.println(person.getName());
%>
</body>
</html>
JavaBean中無參的構造函數改成有參的,會出現異常,這是因為<jsp:useBean>
的內部原理是 new了一個無參的構造函數
※ JSP:setProperty
<jsp:setProerty name="對象名稱" property="屬性名" param="參數名" value="值">
四種模式
<jsp:setProperty name="對象名稱" property="*"/>自動匹配
<jsp:setProperty name="對象名稱" property="屬性名稱"/>指定屬性
<jsp:setProperty name="對象名稱" property="屬性名稱" param="參數名稱"/>指定參 數【很少用】
<jsp:setProperty name="對象名稱" property="屬性名稱" value="內容"/>指定內容【很 少用】
當我們沒有學習到<jsp:setProperty>時,我們獲取表單的信息,然后導入到javaBean對象中是這樣的一種情況:
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%
int age = Integer.parseInt(request.getParameter("age"));
person.setAge(age);
System.out.println(person.getAge());
%>
而我們使用<jsp:setProperty>后,代碼更少,功能更強大
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%--指定屬性名稱為age--%>
<jsp:setProperty name="person" property="age"/>
<%
System.out.println(person.getAge());
%>
代碼少很直觀的可以看出來,但是強大在什么地方呢?
表單提交過來的數據都是字符串,在我們沒有用<jsp:setProperty>前,我們存儲設置int類型或其他非字符串類型的數據是需要強轉的!但是<jsp:setProperty>不需要我們強轉,它內部自動幫我們轉換了!
下面再通過自動匹配來感受它的強大
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%--property的值設置為*就代表自動匹配--%>
<jsp:setProperty name="person" property="*"/>
<%
System.out.println(person.getAge());
System.out.println(person.getName());
%>
為什么Property的值可以將表單傳遞過來的數據封裝到JavaBean對象中?
JavaBean屬性名要和表單的name的名稱一致
是通過反射來做的,調用了內省的方法!
※ JSP:getProperty
<jsp:getProperty name="對象名" property="屬性名"/>
<%--使用<jsp:getProperty>輸出--%>
<jsp:getProperty name="person" property="username"/>
<jsp:getProperty name="person" property="age"/>
JSP 第二篇:
EL運算符:概述、內置對象、數據回顯、自定義函數、EL函數庫
(一) 概述
EL:Expression Language 表達式語言
它的作用就是替換和簡化jsp頁面中java代碼的編寫
EL表達式支持簡單的運算符:加減乘除取摸,邏輯運算符。empty運算符(判斷是否為null),三目運算符


empty運算符可以判斷對象是否為null,用作於流程控制!
三目運算符簡化了if和else語句,簡化代碼書寫
<%
List<Person> list = null;
%>
${list==null?"list集合為空":"list集合不為空"}
(二) 內置對象
EL表達式主要是來對內容的顯示,為了顯示的方便,EL表達式提供了11個內置對象
pageContext 對應於JSP頁面中的pageContext對象(注意:取的是pageContext對象)
pageScope 代表page域中用於保存屬性的Map對象
requestScope 代表request域中用於保存屬性的Map對象
sessionScope 代表session域中用於保存屬性的Map對象
applicationScope 代表application域中用於保存屬性的Map對象
param 表示一個保存了所有請求參數的Map對象
paramValues 表示一個保存了所有請求參數的Map對象,它對於某個請求參數,返回的是一個string[]
header 表示一個保存了所有http請求頭字段的Map對象
headerValues 同上,返回string[]數組。
cookie 表示一個保存了所有cookie的Map對象
initParam 表示一個保存了所有web應用初始化參數的map對象
(三) 數據回顯
<%--模擬數據回顯場景--%>
<%
User user = new User();
user.setGender("male");
//數據回顯
request.setAttribute("user",user);
%>
<input type="radio" name="gender" value="male" ${user.gender=='male'?'checked':'' }>男
<input type="radio" name="gender" value="female" ${user.gender=='female'?'checked':'' }>女
(四) 自定義函數
EL自定義函數用於擴展EL表達式的功能,可以讓EL表達式完成普通Java程序代碼所能完成的功能
開發HTML轉義的EL函數
我們有時候想在JSP頁面中輸出JSP代碼,但是JSP引擎會自動把HTML代碼解析, 輸出給瀏覽器。此時我們就要對HTML代碼轉義。
步驟:
編寫一個包含靜態方法的類(EL表達式只能調用靜態方法),該方法很常用,Tomcat都有此方法,可在webappsexamplesWEB-INFclassesutil中找到
public static String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
在WEB/INF下創建tld(taglib description)文件,在tld文件中描述自定義函數
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>myshortname</short-name>
<uri>/zhongfucheng</uri>
<!--函數的描述-->
<function>
<!--函數的名字-->
<name>filter</name>
<!--函數位置-->
<function-class>utils.HTMLFilter</function-class>
<!--函數的方法聲明-->
<function-signature>java.lang.String filter(java.lang.String)</function-signature>
</function>
</taglib>
在JSP頁面中導入和使用自定義函數,EL自定義的函數一般前綴為"fn",uri是"/WEB-INF/tld文件名稱"
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="fn" uri="/WEB-INF/ideal.tld" %>
<html>
<head>
<title></title>
</head>
<body>
//完成了HTML轉義的功能
${fn:filter("<a href='#'>點這里</a>")}
</body>
</html>
(五) EL函數庫(fn方法庫)
- 由於在JSP頁面中顯示數據時,經常需要對顯示的字符串進行處理,SUN公司針對於一些常見處理定義了一套EL函數庫供開發者使用。
- 其實EL函數庫就是fn方法庫,是JSTL標簽庫中的一個庫,也有人稱之為fn標簽庫,但是該庫長得不像是標簽,所以稱之為fn方法庫
- 既然作為JSTL標簽庫中的一個庫,要使用fn方法庫就需要導入JSTL標簽!要想使用JSTL標簽庫就要導入jstl.jar和standard.jar包!
- 所以,要對fn方法庫做測試,首先導入開發包(jstl.jar、standard.jar)
在JSP頁面中指明使用標簽庫
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
JSP 第三篇:
JSTLd的簡單認識、三個常用對象
JSTL全稱為 JSP Standard Tag Library 即JSP標准標簽庫
JSTL作為最基本的標簽庫,提供了一系列的JSP標簽,實現了基本的功能:集合的遍歷、數據的輸出、字符串的處理、數據的格式化等等!
為什么使用
EL表達式可以很方便地引用一些JavaBean以及其屬性但是仍然不夠完美,它不能遍歷集合,做邏輯的控制。
Scriptlet的可讀性,維護性,重用性都十分差!JSTL與HTML代碼十分類似,遵循着XML標簽語法,使用JSTL讓JSP頁面顯得整潔,可讀性非常好,重用性非常高,可以完成復雜的功能!
在JSP中不推薦使用scriptlet輸出,推薦使用JSP標簽
使用JSTL標簽庫步驟
- 導入jstl相關jar包
- 引入標簽庫:taglib指令:
<%@ taglib %> - 使用標簽
Core標簽庫
core標簽庫是JSTL的核心標簽庫,實現了最基本的功能:流程控制、迭代輸出等操作!
core標簽庫的前綴一般是c
常用的三個JSTL標簽
(一) c:if
屬性:
test 必須屬性,接受boolean表達式
如果表達式為true,則顯示if標簽體內容,如果為false,則不顯示標簽體內容
- 注意:c:if 標簽沒有else情況,想要else情況,則可以在定義一個c:if標簽
<%--如果帶過來的名字是admin,那么可以登陸--%>
<c:if test="${param.name=='admin'}">
用戶名:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="登陸">
</c:if>
<%--如果帶過來的名字是admin888,那么就是注冊--%>
<c:if test="${param.name=='admin888'}">
用戶名:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="注冊">
</c:if>
(二) c:choose
它相當於java代碼的switch語句
使用choose標簽聲明,相當於switch聲明
使用when標簽做判斷,相當於case
使用otherwise標簽做其他情況的聲明,相當於default
<c:choose>
<c:when test="${param.name=='admin'}">
歡迎管理員
</c:when>
<c:when test="${param.name=='user'}">
歡迎用戶
</c:when>
<c:otherwise>
識別不出你是誰
</c:otherwise>
</c:choose>
(三) c:forEach
forEach為循環標簽,相當於Java中的while和for
之前我們在使用EL表達式獲取到集合的數據,遍歷集合都是用scriptlet代碼循環,現在我們學了forEach標簽就可以舍棄scriptlet代碼
向Session中設置屬性,屬性的類型是List集合
向Session中設置屬性,屬性的類型是List集合
遍歷session屬性中的List集合,items:即將要迭代的集合。var:當前迭代到的元素
<%
List list = new ArrayList<>();
list.add("admin");
list.add("zhangsan");
list.add("lisi");
session.setAttribute("list", list);
%>
=====================================================
<c:forEach var="list" items="${list}" >
${list}<br>
</c:forEach>
Map對象有稍微地不一樣保存的不是每個迭代的對象,而是Map.Entry
<%
Map map = new HashMap();
map.put("1", "tom");
map.put("2", "jack");
map.put("3", "jack”);
session.setAttribute("map",map);
%>
<c:forEach var="me" items="${map}" >
${me.key} ${me.value}<br>
</c:forEach>
特別說明:本篇中 第二 第三篇部分內容轉載來自 java3y 所寫jsp第四篇內容,在作者基礎上摘出片段,附上鏈接:
https://juejin.im/post/5a7919045188257a76630a23
結尾:
如果內容中有什么不足,或者錯誤的地方,歡迎大家給我留言提出意見, 蟹蟹大家 !_
如果能幫到你的話,那就來關注我吧!(系列文章均會在公眾號第一時間更新)
在這里的我們素不相識,卻都在為了自己的夢而努力 ❤
一個堅持推送原創Java技術的公眾號:理想二旬不止

