JSP
java server page 運行在服務器端的頁面爵川。本質(zhì)就是servlet若贮。運行流程:
jsp(第一次訪問時) => .java ==> .class ==> 運行
jsp第一次訪問時艾帐,會生成一個java文件,然后運行對應的class文件。
在
\work\Catalina\localhost\jsp_simple\org\apache\jsp
下面可以看到一個java文件和對應的class文件塔鳍。
JSP中的腳本
<% java代碼 %> <!--該腳本包裹的代碼會出現(xiàn)在 service方法中,有分號-->
<%=表達式 %> <!--該腳本是呈現(xiàn)某個值呻此,直接使用會在 out.write()中轮纫。而<fmt:formatDate value="<%=new Date()%>" 作為參數(shù)直接傳入而已,并不在out.write()中顯示到頁面焚鲜。無分號-->
<%! 內(nèi)容 %>: <!--該腳本包裹的內(nèi)容會出現(xiàn)在類定義中掌唾,類方法或者類成員。有分號恃泪。>
對比<%-- --%> 注釋郑兴。被注釋的內(nèi)容 不會出現(xiàn)在java文件中。
注解 <%-- -- %> 和 的區(qū)別
當我們在瀏覽器中審查源碼的時候贝乎,<%–- -–%> 標記的內(nèi)容是完全看不到的, 而\ 標記的內(nèi)容不但可以看到叽粹,里面的內(nèi)容還會被解析览效。
EL表達式
代替輸出腳本 <%= %>
格式: ${表達式}
EL表達式可以在4個域中取數(shù)據(jù) => 4個內(nèi)置對象 requestScope / applicationScope / sessionScope / pageScope
舉個例子
<head>
<%
request.setAttribute("name", "requestKey");
application.setAttribute("name", "applicationKey")
%>
</head>
<body>
<!-- 從指定域取值 -->
${requestScope.name}<br />
${applicationScope.name}<br />
<!-- 不從指定域取值,從小域到大域中查找.顯示最先找到的,這里顯示的是requestScope里的 -->
${name}<br />
</body>
JSP指令
page指令
page : <%@ page language="java" import="java.util.*, java.io.*" pageEncoding="UTF-8" autoFlush="true" buffer="8kb" errorPage="/page/error.jsp" %>
- page指令用于指定頁面一些基本屬性虫几。
- language="java" :頁面中使用的語言為java.
- import="java.util.*" :就是導包. 是所有屬性中唯一一個可以在頁面中出現(xiàn)多次的屬性锤灿。比如上面的import="java.util.*, import="java.io.*
- pageEncoding="UTF-8" 頁面保存到硬盤編碼。
- contentType="text/html; charset=UTF-8" :發(fā)送給瀏覽器的編碼.以上兩個碼表最好一致. 但是一般設置一個屬性即可.另外一個屬性自動設置
- autoFlush="true" buffer="8kb" :如果緩沖區(qū)裝滿是否自動刷新到瀏覽器. 如果裝滿并沒有選擇自動刷新,那么會拋出異常.buffer="8kb", 決定jsp輸出緩沖區(qū)大小為8kb(默認)
- 在某個頁面如index.jsp中配置errorPage="/page/error.jsp"配置當前頁面的錯誤頁面辆脸;在error.jsp中配置isErrorPage="true" . 指定當前頁面是否是一個錯誤頁面但校。true表示可以使用<%=exception.getMessage() %>打印出錯誤信息。
- extends="" 決定當前jsp的父類是誰.父類必須是servlet的子類.
- info="" getServletInfo 剛方法的返回值.
- isELIgnored="false" 決定當前頁面能否使用 EL表達式. 默認值就是支持EL
- session="true" 當前jsp頁面是否可以直接使用session對象.默認值就是true
include指令
include 屬于靜態(tài)包含 , 就是將兩個jsp的代碼合并啡氢。然后在共同編譯成一個java文件状囱,再編譯成class再執(zhí)行。
因為會先合并代碼,所以變量可以共享倘是。(意思即是兩個jsp不能定義相同的變量亭枷,比如下面的。)兩個jsp都有這個搀崭,include就會報錯
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
MyJsp1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'MyJsp1.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
This is my JSP page. <br>
// 靜態(tài)包含了MyJsp2.jsp, 下面的str可以直接使用
<%@ include file="/include/MyJsp2.jsp" %>
<%=str %>
</body>
</html>
MyJsp2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<% String str = "MyJsp2"; %>
<html>
<head>
<title>My JSP 'MyJsp2.jsp' starting page</title>
</head>
<body>
This is my 重復 page. <br>
</body>
</html>
訪問MyJsp1.jsp就會打印
This is my JSP page.
This is my 重復 page.
MyJsp2
動態(tài)包含
<jsp:include page="/index.jsp"></jsp:include>
, 和下面的代碼一樣的效果叨粘。
<%
//request.getRequestDispatcher("").include(request, response);
%>
上面的包含就是之前學習的request的請求包含. 這種包含也叫做動態(tài)包含。動態(tài)包含的兩個頁面會分別編譯成兩個java,class.分別執(zhí)行. 只是在最后輸出時將輸出結果合并. 所以頁面中的變量不會共享瘤睹。
九大內(nèi)置對象
指的在jsp中不加以聲明就可以直接使用的9個對象.
原理: 因為我們的代碼是寫在jsp對應java的service方法中的.所以在service方法中聲明的變量,我們可以直接使用.
public void _jspService(final javax.servlet.http.HttpServletRequest ->1 request, final javax.servlet.http.HttpServletResponse response ->2)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}
final javax.servlet.jsp.PageContext pageContext ->3;
javax.servlet.http.HttpSession session ->4 = null;
final javax.servlet.ServletContext application ->5;
final javax.servlet.ServletConfig config ->6;
javax.servlet.jsp.JspWriter out = null_ ->7;
final java.lang.Object page = this ->8;
javax.servlet.jsp.JspWriter _jspx_out -> 9 = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
這是第一次訪問jsp自動生成的java文件_jspService方法源碼 一部分升敲。
對象名稱 對象類型
request HttpServletRequest
response HttpServletResponse
session HttpSession
exception Throwable
application ServletContext
config ServletConfig
以上是以前學過的。
page Object 一般沒用.
out JspWriter 用于向瀏覽器輸出信息
pageContext PageContext 9大內(nèi)置對象的首領.
out
JSPWriter和response.getWriter
<%out.write("a");
response.getWriter().write("b");
out.write("c");
response.getWriter().write("d");
%>
上面回輸出bd ac
在輸出到瀏覽器時,會先把兩個流合并轰传。再輸出.
合并時response的字符流在前驴党。JSPWriter在后。所以不管代碼書寫順序如何, 最終
response流的內(nèi)容總會在JSPwriter流的內(nèi)容之前绸吸。
結論: 在jsp中輸出使用out(JSPWriter)輸出,不要使用response.getWriter輸出鼻弧。
page
page對象般沒有用.
page對象 指向的就是 this(當前jsp生成Servlet對象)设江。使用情況一般是在開發(fā)框架時,框架需要用到JSP對象,進行一些頁面的操作時,將page對象傳過去即可。
pageContext
本身是一個域?qū)ο? 在pageContext對象上有一個map攘轩,這個Map就是Page叉存。
范圍: 就只在當前頁面中有用,范圍甚至比request域范圍還小度帮。
page域 < request域 < session域 < application域
就是在一個頁面內(nèi)的共享數(shù)據(jù)歼捏,本質(zhì)是jsp編譯后的java文件中service中的一個變量而已,只在當前頁面的service方法內(nèi)有效笨篷。
操作其他三個域
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%
// PageContext可以操作4個域
//增
pageContext.setAttribute("name","requestTom", PageContext.REQUEST_SCOPE);
pageContext.setAttribute("name","sessionTom", PageContext.SESSION_SCOPE);
pageContext.setAttribute("name","applicationTom", PageContext.APPLICATION_SCOPE);
pageContext.setAttribute("name","pageTom", PageContext.PAGE_SCOPE);
//根據(jù)鍵獲得值
pageContext.getAttribute("name", PageContext.REQUEST_SCOPE);
//刪
// pageContext.removeAttribute("name",PageContext.REQUEST_SCOPE);
//查所有鍵
pageContext.getAttributeNamesInScope(PageContext.REQUEST_SCOPE);
// find 從4個域中找 ,從小到大.一旦找到立刻停止,并返回找到的.
pageContext.findAttribute("name");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'page.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
request: <%=request.getAttribute("name") %><br>
session: <%=session.getAttribute("name") %><br>
application: <%=application.getAttribute("name") %><br>
findAttribute:<%=pageContext.findAttribute("name") %><br>
</body>
</html>
獲得其他八個內(nèi)置對象
<%
// PageContext可以獲得其他8個內(nèi)置對象
pageContext.getRequest();
pageContext.getResponse();
pageContext.getSession();
pageContext.getServletContext();
pageContext.getServletConfig();
pageContext.getException();
pageContext.getPage();
pageContext.getOut();
%>
page域的作用
用于標簽處理類與JSP之前交互數(shù)據(jù)的"橋梁"瞳秽。在jsp中應避免在頁面上書寫任何java代碼,EL表達式的出現(xiàn)就是避免這種情況率翅,將java代碼寫到EL表達式中而不是jsp练俐。EL表達式中就有標簽處理類。標簽處理器要給jsp看的東西冕臭,只要放在page域中腺晾,jsp從中取出就行;反之辜贵,jsp也可以向page域放東西悯蝉,讓標簽處理器拿到。
JSP中的動作標簽
<%-- Jsp動作標簽托慨,分擔jsp頁面的java代碼 --%>
<jsp:forward page="/index.jsp"></jsp:forward>
<%-- 上面的標簽和下面轉(zhuǎn)發(fā)的代碼是一個效果 --%>
<%-- request.getRequestDispatcher("/index.jsp").forward(request, response); --%>
<jsp:include page="/index.jsp"></jsp:include>
<%-- request.getRequestDispatcher("/index.jsp").include(request, response) --%>
BeanUtils的使用
主要作用:把對象的屬性比如鍵值對數(shù)據(jù)封裝到對象中
這里把表單數(shù)據(jù)封裝到user對象中鼻由。BeanUtils設置屬性的時候依賴于底層的getter和setter方法
package beanUtils;
public class User {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [name=" + name + ", password=" + password + "]";
}
}
表單的參數(shù)的鍵必須與Bean中屬性名稱對應。注意這里的name屬性厚棵,name和password要和上面User的字段一樣
<body>
<form action="/jsp/BeanUtils" method="post">
賬號:<input type="text" name="name" /><br>
密碼:<input type="password" name="password"> <br>
<input type="submit" value="登錄">
</form>
</body>
package beanUtils;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
@WebServlet("/BeanUtils")
public class Bean extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User user = new User();
try {
BeanUtils.populate(user, request.getParameterMap());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(user);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
BeanUtils.populate(bean,Map)
蕉世,其中Map中的key必須與目標對象中的屬性名相同,否則不能實現(xiàn)拷貝窟感。
JavaBean
- 要求為屬性提供get/set方法任意之一
- 需要有空參構造
- 實現(xiàn)串行化接口(可選)
上面的User類
就是一個JavaBean讨彼。實現(xiàn)了getter和setter,默認空參柿祈。所以它是哈误。
getParameter
獲取到的表單數(shù)據(jù)都是String類型。加入輸入年齡躏嚎,想以int存入蜜自,自動完成。BeanUtils 可以自動幫你轉(zhuǎn)換8個基本數(shù)據(jù)類型卢佣。但是輸入日期重荠,以Date存入怎么辦?如果遇到自定義類型需要轉(zhuǎn)換虚茶,我們要自己寫一個轉(zhuǎn)換器并注冊戈鲁。
自定義轉(zhuǎn)換器并注冊
1.自定義轉(zhuǎn)換器
package beanUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.Converter;
public class MyConverter implements Converter {
/* 參數(shù)1: 需要轉(zhuǎn)換成什么類型仇参,register(new MyConverter(), Date.class)傳進來的,這里就是Date.class
參數(shù)2: 待轉(zhuǎn)換表單參數(shù)
返回值: 轉(zhuǎn)換結果
*/
@Override
public Object convert(Class arg0, Object transIn) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String dateString = transIn.toString();
try {
Date date = sdf.parse(dateString);
return date;
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
2. 轉(zhuǎn)換器注冊
// 參數(shù)1婆殿,轉(zhuǎn)換器诈乒;參數(shù)2,支持的轉(zhuǎn)換類型婆芦。即可以轉(zhuǎn)換日期格式
// 注意:注冊類型轉(zhuǎn)換器,必須寫在populate方法之前.
ConvertUtils.register(new MyConverter(), Date.class);
BeanUtils.populate(user, request.getParameterMap());
新增了填寫年齡和入職日期(分別對應int和Date)
<form action="/jsp/BeanUtils" method="post" >
用戶名:<input type="text" name="name" /><br>
密碼:<input type="password" name="password" /><br>
年齡:<input type="text" name="age" /><br>
入職日期:<input type="text" name="date" /><br>
<input type="submit" value="登錄" />
</form>
同時User也新增
private int age;
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
注意怕磨,導包Date都是java.util.Date而不是java.sql.Date,去import下檢查消约!
JavaBean的屬性只看get或者set屬性肠鲫,和成員變量個數(shù)沒有關系。
JSTL
Java standard Tag Library => java標準標簽庫
JSTL標簽用于代替,簡化頁面中的java代碼或粮。是apache組織提供一套已經(jīng)開發(fā)好的標簽庫.
使用最多是core庫导饲,需要用到taglib指令。prefix="c"
表示前綴氯材,自定義帜消。我這里用c表示core的意思。
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
if標簽
<body>
<!-- core庫
c:if 判斷 標簽
-->
<%
request.setAttribute("num1",10);
request.setAttribute("num2", 100);
%>
<c:if test="${num1 > num2}">
num1 比較大!!
</c:if>
<c:if test="${num1 < num2}">
num2 比較大!!
</c:if>
</body>
if-else標簽
<body>
<!-- core庫
ifelse 標簽
choose
when(可以出現(xiàn)多次)
otherwise
根據(jù)num1 num2 的大小,在頁面提示值大的那個
-->
<%
request.setAttribute("num1",1000);
request.setAttribute("num2", 100);
%>
<c:choose>
<c:when test="${num1>num2}">
num1 比較大!
</c:when>
<c:when test="${num1==num2}">
num1 與num2 相等!
</c:when>
<c:otherwise>
num2 比較大!
</c:otherwise>
</c:choose>
</body>
forEach標簽
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix= "c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<%-- 類選擇器 --%>
<style type="text/css">
.one{
background-color: yellow;
}
.two{
background-color: blue;
}
</style>
</head>
<body>
<%-- <c:forEach>(常用) 遍歷標簽
items="${requestScope.list}" 要遍歷的集合設置給該屬性
var="abc" 每次遍歷集合中元素 該屬性值作為鍵放入page域
varStatus="st" 每次遍歷的狀態(tài),會封裝成一個對象 以該屬性值為鍵 放入page域
數(shù)數(shù)的功能
begin="1" 從幾開始數(shù)
end="100" 數(shù)到幾
step="1" 每次數(shù)幾個數(shù)
var="num" 將當前數(shù)的數(shù)以該屬性值作為鍵放入page域
--%>
<%
List<String> list = new ArrayList<>();
list.add("tom");
list.add("jerry");
list.add("jack");
list.add("rose");
request.setAttribute("list", list);
%>
<table border="1">
<tr>
<th>用戶名</th>
<th>當前遍歷索引</th>
<th>當前遍歷計數(shù)</th>
<th>是否是集合第一個元素</th>
<th>是否是集合最后一個元素</th>
</tr>
<%-- 奇數(shù)行選擇one即黃色浓体,偶數(shù)行選擇two即藍色 --%>
<%-- var這里是為傳入的列表里面的子項定義一個鍵,可以看成是for (A a : aList)里面的a -->
<c:forEach items="${list}" var="name" varStatus="st" >
<tr class="${st.index%2==0?"one":"two"}" >
<td>${name}</td>
<td>${st.index}</td>
<td>${st.count}</td>
<td>${st.first}</td>
<td>${st.last}</td>
</tr>
</c:forEach>
</table>
<hr>
<!-- 數(shù)數(shù)的功能-->
<c:forEach begin="1" end="10" step="2" var="num" >
${num}
</c:forEach>
</body>
</html>
長這樣
fmt庫 格式化庫
格式化日期
<body>
<%--
格式化日期
fmt:formatDate
pattern:轉(zhuǎn)換成的格式 var:將已經(jīng)格式化好的以這個鍵(這里是date)放入 scope中(這里是request域
--%>
<fmt:formatDate value="<%=new Date()%>"
pattern="yyyy/MM/dd hh:mm:ss" var="date" scope="request" />
${requestScope.date}
</body>
格式化數(shù)字
<body>
<!--
格式化數(shù)字
fmt:formatNumber
-->
<fmt:formatNumber value="3.1415926" pattern="0000.00000000000" var="num1" scope="request" ></fmt:formatNumber>
<fmt:formatNumber value="3.1415926" pattern="####.###########" var="num2" scope="request" ></fmt:formatNumber>
${requestScope.num1}<br>
${requestScope.num2}<br>
分別輸出辈讶,兩種方式的區(qū)別不言自明命浴。
0003.14159260000
3.1415926
路徑填寫問題
路徑總結
前提: 所有路徑都應以"/"開頭.
項目名: jsp
資源名: AServlet
客戶端路徑 => 給瀏覽器用的路徑 => 填寫項目名稱
<form action="/jsp/AServlet" > 提交到瀏覽器
![](/jsp/AServlet) 瀏覽器從哪個路徑加載資源
<a href="/jsp/AServlet" > 用戶點擊鏈接后,瀏覽器應該加載什么資源
response.sendRedirect("/day10-jsp/AServlet") 重定向贱除,告訴瀏覽器轉(zhuǎn)向哪兒
服務器端路徑 => 給服務器端使用的路徑 => 填寫項目下的路徑
request.getRequestDispatcher("/AServlet") 轉(zhuǎn)發(fā)生闲。服務器內(nèi)部事務
errorPage="/AServlet" 出現(xiàn)錯誤,轉(zhuǎn)發(fā)到該頁面
<location>/AServlet</location> 出現(xiàn)某一個狀態(tài)碼月幌,轉(zhuǎn)發(fā)到這個頁面
<url-pattern>/Aservlet</url-pattern> 告訴服務器碍讯,這個路徑能訪問到這個資源
MVC思想
javaee三層架構
為什么不在Service中處理所有的業(yè)務邏輯而需要DAO呢?
假設一個銀行系統(tǒng)扯躺,有轉(zhuǎn)賬和取錢功能捉兴。無非就是減錢和加錢的調(diào)用。將取錢和加錢的功能封裝录语,Service中的轉(zhuǎn)賬就調(diào)用加錢和減錢的方法倍啥。取錢功能就調(diào)用減錢。做到了進一步的解耦澎埠。
javaee三層架構和MVC思想的區(qū)別
- 概念不同虽缕,一個是架構一個是思想
- MVC范圍更大,三層架構是在javaee
- javaee三層架構符合MVC設計思想蒲稳。如JSP是View部分氮趋,Servlet是control部分伍派。而Service和Dao共同組成Model部分
by @sunhaiyu
2017.4.10