Java之JSP和Servlet基礎知識。
JSP基礎
JSP起源
JSP,Java Server Pager的簡稱。由SUN倡導並聯合其它公司建立。
JSP是一門指令碼語言
JSP可以嵌入到HTML中
JSP擁有Java語言的所有特性
面向物件、健壯、多執行緒、安全、可移植、高效能
JSP運行於JSP容器中
Tomcat、Jetty等。
JSP會被轉換成Servlet
JSP->Servlet->Class檔案。
Mac下常用shell命令:
pwd 列印當前目錄
ls 列出當前目錄下的所有檔案目錄
cd 更改目錄
.代表當前工作目錄
..代表上一級目錄
~代表使用者根目錄
chmod更改許可權
JSP基本語法
<% %>
JSP程式碼段,可以輸入任意的Java語言片段。
<%! %>
JSP宣告,在JSP頁面範圍宣告變數、函式和類。
<%= %>
JSP表示式
JSP中HTML註釋:
<!-- -->
<!-- <% %>-->
<%-- --%>
JSP註釋,不會被客戶端瀏覽器看到。
JSP編譯指令
通知Servlet引擎處理訊息,它只在JSP程式被轉化成Servlet的過程中起作用。
Page指令
在JSP最上方,用於指定語言,MIME,編碼,錯誤跳轉頁面等等。
1. contentType指定MIME和網頁編碼,無859.
2. import匯入多個java類,也可以使用多個page匯入多個java類。
3. errorPage表示JSP頁面發生執行時錯誤跳轉的頁面。
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.ArrayList,java.util.LinkedList"
errorPage="err.jsp"%>
include指令
靜態包含指令,可將外部檔案包含進來。包含JSP檔案時,不能有重複的變數宣告。
被匯入的JSP編譯指令會起作用。
<%@ include file="header.jsp"%>
taglib指令
JSP動作指令
客戶端請求時動態執行的指令
forward指令
重定向指令,它下面的程式碼不會被執行,不會被返回到客戶端。
可以通過jsp:param指令向跳轉的JSP頁面傳遞引數。在目標JSP頁面中通過request.getParameter()方法接收引數。
<jsp:forward page="err.jsp">
<jsp:param name="name" value="bendeng"></jsp:param>
<jsp:param name="pass" value="8765432"></jsp:param>
</jsp:forward>
include指令
動態包含指令,包含靜態HTML和動態JSP檔案。
被匯入的JSP編譯指令不會起作用,並可以額外加引數。
<jsp:include page="body.jsp">
<jsp:param name="bgcolor" value="red"></jsp:param>
</jsp:include>
在被包含的JSP檔案中通過request物件將body背景色修改為傳過去的值。
<body bgcolor="<%=request.getParameter("bgcolor")%></body>
useBean指令
JavaBean:一個公開的建構函式、屬性有get、set方法、可序列化。
例如:我們在接收提交的頁面的,使用User這個bean,然後使用useBean指令配合setProperty、getProperty指令來接收顯示提交內容:
提交頁面如下:
<form action="regist.jsp" method="post">
使用者名稱:<input type="text" name="name"/><br>
密碼:<input type="password" name="pass"/><br>
<input type="submit" value="提交"/>
接受頁面如下:
<jsp:useBean id="user" class="com.ben.bean.User"></jsp:useBean>
<jsp:setProperty property="name" name="user"></jsp:setProperty>
<jsp:setProperty property="pass" name="user"></jsp:setProperty>
<jsp:getProperty property="name" name="user"></jsp:getProperty>
<jsp:getProperty property="pass" name="user"></jsp:getProperty>
JSP內建物件
out物件
向客戶端瀏覽器輸出資訊
<%
out.println("out物件");
out.newLine();
out.println("<br>");
out.flush();
out.clearBuffer();//清空緩衝區的資料
//out.clear();//也是清空緩衝區資料,不同的是如果之前呼叫flush,則會丟擲異常。
out.println("緩衝區大小:" + out.getBufferSize());//預設8K,可通過編譯指令修改,如:<%@ page buffer="16kb" %>
out.println("<br>");
out.println("當前緩衝區剩餘大小:" + out.getRemaining());
%>
request物件
request物件封裝了客戶端提交到伺服器的請求資訊,表單、cookie等資訊。
請求的方法名:<%=request.getMethod() %><br/>//GET
請求的資源:<%=request.getRequestURI() %><br/>
請求使用的協議:<%=request.getProtocol() %><br/>//HTTP/1.1
請求的伺服器IP:<%=request.getServerName() %><br/>
請求的伺服器埠:<%=request.getServerPort() %><br/>
客戶端的IP地址:<%=request.getRemoteAddr() %><br/>
客戶端的主機名:<%=request.getRemoteHost() %><br/>
getRequestURL:<%=request.getRequestURL() %><br/>
getScheme:<%=request.getScheme() %>//Http
//request.getParameter() 用於從客戶端請求獲取表單資訊
request.setAttribute();
request.getAttribute();
這兩個方法用於在web元件間共享資訊,比如JSP之間。
請求頭資訊;
<%
Enumeration<String> e = request.getHeaderNames();
while(e.hasMoreElements()){
String headerName = e.nextElement();
out.println(headerName+":"+request.getHeader(headerName)+"<br/>");
}
%>
response物件
對客戶端請求封裝回覆信息。
response.setHeader("Cache-Control", "no-cache");//瀏覽器讀到這個頭資訊,就不會將網頁資訊存到快取。還有public,private等
response.setIntHeader("Refresh", 2);//每隔2秒重新整理一次
//response.sendRedirect("index.jsp");//重定向到另一個JSP網頁
//設定Cookie
Cookie cookie = new Cookie("ben","deng");
cookie.setMaxAge(3600);//單位為s
response.addCookie(cookie);
session物件
HTTP是無狀態的。伺服器使用session可以儲存客戶端瀏覽器的資訊。
<%! SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); %>
sessionId= <%=session.getId() %></br>
session建立時間= <%=sdf.format(new Date(session.getCreationTime())) %></br>
session最後訪問時間= <%=sdf.format(new Date(session.getLastAccessedTime())) %></br>
session失效時間: <%= session.getMaxInactiveInterval()%>//預設1800s
session失效時間可以通過修改部署描述符來實現。
<session-config>
<session-timeout>10</session-timeout>
</session-config>
如果不生效,需要刪掉work目錄的檔案。
application物件
應用一啟動就會生成application物件。web應用不關閉,application就一直存在。
servlet的環境通過呼叫getServletConfig().getContext()
方法獲得。作用域是application(整個程式執行期)。它提供了關於伺服器版本,應用級初始化引數和應用內資源絕對路徑,註冊資訊的方式。
伺服器資訊:<%=application.getServerInfo() %>
應用資訊:<%=application.getServletContextName() %>
主機資訊:<%=application.getVirtualServerName() %>
由於Application物件是隨應用的生命週期存在,所以通過它可以對一些配置進行全域性載入和儲存。
config物件
代表當前JSP程式的配置資訊。一般JSP在應用中作為View層使用,一般使用較少,在Servlet中用的比較多。讀取的配置資訊來自web.xml。
<%=config.getInitParameter("name") %>
在web.xml中配置一個servlet:
<servlet>
<servlet-name>config</servlet-name>
<jsp-file>/jsp/config.jsp</jsp-file>
<init-param>
<param-name>name</param-name>
<param-value>name</param-value>
</init-param>
<init-param>
<param-name>pass</param-name>
<param-value>pass</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>config</servlet-name>
<url-pattern>/*</url-pattern><!--這個樣式所有請求都會返回config.jsp的內容 -->
</servlet-mapping>
在config.jsp中,使用
String name = config.getInitParameter("name");
String pass = config.getInitParameter("pass");
取出配置在web.xml中的資訊。
exception物件
exception物件只有在page指令中具有屬性isErrorPage="true"
時才有效。它就是Java中普通的Throwable物件。
通過JSP錯誤頁面中一個catch塊已經益出但沒有捕獲的java.lang.Throwable的任意例項,傳向了errorPage的URI。
比如在indx.jsp中執行如下程式碼
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"
errorPage="exception.jsp"%>
try{
int c = 3/0;
}catch(Exception e){
}
在exception.jsp中:
<%
out.print(exception.getLocalizedMessage());
%>
頁面輸出/ by zero
。
page物件
繼承自Object物件,代表當前JSP頁面。
pageContext物件。
pageContext物件提供所有四個作用域層次的屬性查詢和修改能力,它也提供了轉發請求到其它資源和包含其他資源的方法。該物件的方法都是抽象方法。
JSP作用域
作用域規定的是變數的有效期限。JSP有四大作用域:Request、Page、Session、Application。
我們使用public Object getAttribute(String name)
獲得變數值,
使用public void setAttribute(String name, Object value)
將變數值儲存到對應作用域中。
物件 | 說明 | 型別 | 作用域 |
---|---|---|---|
request | 請求物件 | javax.servlet.ServletRequest | Request |
response | 響應物件 | javax.servlet.SrvletResponse | Page |
pageContext | 頁面上下文物件 | javax.servlet.jsp.PageContext | Page |
session | 會話物件 | javax.servlet.http.HttpSession | Session |
application | 應用程式物件 | javax.servlet.ServletContext | Application |
out | 輸出物件 | javax.servlet.jsp.JspWriter | Page |
config | 配置物件 | javax.servlet.ServletConfig | Page |
page | 頁面物件 | javax.lang.Object | Page |
exception | 異常物件 | javax.lang.Throwable | page |
如果把變數放到pageContext裡,就說明它的作用域是page,它的有效範圍只在當前jsp頁面裡。
從把變數放到pageContext開始,到jsp頁面結束,你都可以使用這個變數。
如果把變數放到request裡,就說明它的作用域是request,它的有效範圍是當前請求週期。
所謂請求週期,就是指從http請求發起,到伺服器處理結束,返回響應的整個過程。在這個過程中可能使用forward的方式跳轉了多個jsp頁面,在這些頁面裡你都可以使用這個變數。
如果把變數放到session裡,就說明它的作用域是session,它的有效範圍是當前會話。
所謂當前會話,就是指從使用者開啟瀏覽器開始,到使用者關閉瀏覽器這中間的過程。這個過程可能包含多個請求響應。也就是說,只要使用者不關瀏覽器,伺服器就有辦法知道這些請求是一個人發起的,整個過程被稱為一個會話(session),而放到會話中的變數,就可以在當前會話的所有請求裡使用。
如果把變數放到application裡,就說明它的作用域是application,它的有效範圍是整個應用。
整個應用是指從應用啟動,到應用結束。我們沒有說“從伺服器啟動,到伺服器關閉”,是因為一個伺服器可能部署多個應用,當然你關閉了伺服器,就會把上面所有的應用都關閉了。
application作用域裡的變數,它們的存活時間是最長的,如果不進行手工刪除,它們就一直可以使用。
與上述三個不同的是,application裡的變數可以被所有使用者共用。如果使用者甲的操作修改了application中的變數,使用者乙訪問時得到的是修改後的值。這在其他scope中都是不會發生的,page, request,session都是完全隔離的,無論如何修改都不會影響其他人的資料。
初識Servlet
Servlet = Server + Applet。它是一個特殊的Java類。
下面新建一個Servlet:
public class Hello extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
PrintWriter pw = resp.getWriter();
pw.println("Hello Servlet");
pw.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
doGet(req,resp);
}
@Override
public void init() throws ServletException {
System.out.println("hello init()");
super.init();
}
@Override
public void destroy() {
System.out.println("hello destroy()");
super.destroy();
}
}
在web.xml中進行配置:
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.ben.servlet.Hello</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
使用tomcat,進行執行。localhost:8080/hello,即可顯示Hello Servlet。我是在IDEAIU中進行的,如果在Eclipse中路徑可能有變化,需要加工程名。
如果瀏覽器出現HTTP Status 405 - HTTP method GET is not supported by this URL,註釋掉doGet和doPost的父類呼叫即可。
Servlet處理流程
客戶端瀏覽器發出一個請求,tomcat內建web Server接收到請求,將請求轉達給Servlet容器,Servlet容器會載入Servlet例項。Servlet使用HttpServletRequest接收請求例項,Servlet可能會將請求轉發給其它的Servlet處理。全部處理完,請求結果通過HttpServletResponse物件返回給瀏覽器。
當Servlet裝載和例項化以後,Servlet會呼叫init()
方法,進行初始化。Servlet處於服務狀態。在整個生命週期中,init()執行一次。接收請求時,會呼叫service()
方法,當Servlet不再使用,容器銷燬Servlet之前會呼叫destroy()
方法。destroy()方法中做一些資源釋放和日誌輸出的操作。
init()不需要過載,只在需要初始化時做一些操作時才需要過載,建議過載無參的init()方法,不用呼叫super.init()方法,有參的init()必須呼叫父類的init()方法。
使用Servlet實現簡單的登陸
login.jsp如下:
<form method="post" action="<%=request.getContextPath()%>/login">
<input name="username" type="text" placeholder="使用者名稱"/><br/>
<input name="password" type="password" placeholder="密碼"/><br/>
<input type="submit" value="登陸">
<input type="reset" value="重置">
</form>
新建一個LoginServlet:
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
//System.out.println("使用者名稱=" + username);
//System.out.println("密碼=" + password);
String forward = null;
//這裡簡單判斷使用者名稱來跳轉成功和失敗頁面
if("ben.deng".equals(username)){
forward = "success.jsp";
//這種跳轉不會帶請求的引數資訊,只在響應頭中的location中指定了跳轉頁面。但可以進入其他應用的頁面
//resp.sendRedirect("success.jsp");
}else{
forward = "fail.jsp";
//resp.sendRedirect("fail.jsp");
}
//請求轉發,可以請求引數帶給轉發的頁面。但只限於應用內的頁面。
RequestDispatcher dispatcher = req.getRequestDispatcher(forward);
dispatcher.forward(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
public void init() throws ServletException {
System.out.println("login init()");
}
}
web.xml配置:
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.ben.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
Servlet過濾器
建立一個過濾器:
public class MyFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("doFilter...");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
System.out.println("filter init():" + config.getInitParameter("paramKey"));
}
}
和Servlet一樣,需要在web.xml中配置:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.ben.filter.MyFilter</filter-class>
<init-param>
<param-name>paramKey</param-name>
<param-value>paramValue</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在一個web應用中,可以有多個Filter,多個Filter的執行順序是和web.xml中配置的順序是一致的。
過濾器之編碼轉換
在doGet或doPost方法中返回一串有中文的字串,瀏覽器很多時候會顯示亂碼。這時我們可以在過濾器中進行處理。
String charEncoding = "UTF-8";
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
if(!charEncoding.equals(req.getCharacterEncoding())){
req.setCharacterEncoding(charEncoding);
}
resp.setCharacterEncoding(charEncoding);
chain.doFilter(req, resp);
}
過濾器之許可權校驗
新建一個許可權校驗過濾器:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String loginStatus = (String) request.getSession().getAttribute("login");
if (loginStatus != null && "success".equals(loginStatus)) {
chain.doFilter(req, resp);
} else {
String reqPath = request.getServletPath();
if (!reqPath.contains("login")) {
//System.out.println("您尚未登陸");
RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
rd.forward(req, resp);
}else{
chain.doFilter(req, resp);
}
}
}
在部署描述符中註冊過濾器:
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>com.ben.filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
上述程式碼示例中是將登陸成功後再session中設定一個login屬性為success,在其他非login/login.jsp等頁面中判斷是否有這個session,沒有或值不對(比如登入失敗設定的其他值),則要求跳轉到登入介面。從而實現一個簡單的許可權校驗過濾。