Servlet相關概念及其功能使用

Servlet概述

Servlet 是Java Server Applet的簡稱,稱爲小服務器程序,用Java編寫的服務器端程序,主要功能交互式地瀏覽和修改數據,生成動態Web內容。

Servlet運行於支持Java的應用服務器中。從實現上講,Servlet可以響應任何類型的請求,但絕大多數情況下Servlet只用來擴展基於HTTP協議的Web服務器。

Servlet編程需要使用到javax.servlet 和 javax.servlet.http兩個包下面的類和接口,在所有的類和接口中,javax.servlet.Servlet 接口最爲重要。所有的servlet程序都必須實現該接口或者繼承實現了該接口的類。javax.servlet.ServletConfig;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
javax.servlet.http.HttpSession;
javax.servlet.http.Cookie;

HTTP協議
什麼是HTTP協議

超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最爲廣泛的一種網絡協議,是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的連接方式。

HTTP協議的主要特點如下:
1.支持客戶端/服務器模式。
2.簡單快速: 客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、POST。每種方法規定了客戶與服務器聯繫的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
3.靈活: HTTP允許傳輸任意類型的數據對象。傳輸的類型由Content-Type加以標記。
4.無連接: 無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。採用這種方式可以節省傳輸時間。
5.無狀態: HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味着如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。

Http協議的通信

HTTP通信機制是在一次完整的HTTP通信過程中,Web瀏覽器與Web服務器之間將完成下列7個步驟:
1、 建立TCP連接
在HTTP工作開始之前,Web瀏覽器首先要通過網絡與Web服務器建立連接,該連接是通過TCP來完成的,該協議與IP協議共同構建Internet,即著名的TCP/IP協議族,因此Internet又被稱作是TCP/IP網絡。HTTP是比TCP更高層次的應用層協議,根據規則,只有低層協議建立之後才能進行更高層協議的連接。因此,首先要建立TCP連接,一般TCP連接的端口號是80

2、 瀏覽器向Web服務器發送請求命令
一旦建立了TCP連接,Web瀏覽器就會向Web服務器發送請求命令
例如:GET /sample/hello.html HTTP/1.1

3、 瀏覽器發送請求頭信息
瀏覽器發送其請求命令之後,還要以頭信息的形式向Web服務器發送一些別的信息,之後瀏覽器發送了一空白行來通知服務器,它已經結束了該頭信息的發送。

4、 Web服務器應答
客戶機向服務器發出請求後,服務器會客戶機回送應答,
HTTP/1.1 200 OK
應答的第一部分是協議的版本號和應答狀態碼

5、 Web服務器發送應答頭信息
正如客戶端會隨同請求發送關於自身的信息一樣,服務器也會隨同應答向用戶發送關於它自己的數據及被請求的文檔。

6、 Web服務器向瀏覽器發送數據
Web服務器向瀏覽器發送頭信息後,它會發送一個空白行來表示頭信息的發送到此爲結束,接着,它就以Content-Type應答頭信息所描述的格式發送用戶所請求的實際數據

7、 Web服務器關閉TCP連接
一般情況下,一旦Web服務器向瀏覽器發送了請求數據,它就要關閉TCP連接,然後如果瀏覽器或者服務器在其頭信息加入了這行代碼
Connection:keep-alive
TCP連接在發送後將仍然保持打開狀態,於是,瀏覽器可以繼續通過相同的連接發送請求。保持連接節省了爲每個請求建立新連接所需的時間,還節約了網絡帶寬

請求和響應數據格式

HTTP請求報文

當瀏覽器向Web服務器發出請求時,它向服務器傳遞了一個數據塊,也就是請求信息(請求報文),HTTP請求信息由4部分組成:
1 請求行 請求方法/地址 URI協議/版本
2 請求頭(Request Header)
3 空行
4 請求正文

HTTP請求的例子:

POST /hello HTTP/1.1
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Language:zh-CN,zh;q=0.8,en-GB;q=0.6,en;q=0.4
Connection:Keep-Alive
Host:localhost:8080
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Accept-Encoding:gzip, deflate, br

username=zhangsan&age=20&add=beijing

HTTP響應報文
HTTP應答與HTTP請求相似,HTTP響應也由4個部分構成,分別是:

1、狀態行
2、響應頭(Response Header)
3、空行
4、響應正文

HTTP/1.1 200 OK   //狀態行
Server: nginx
Date: Tue, 31 May 2016 02:09:24 GMT
Content-Type: text/html;charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With,access_token,access-token,content-type,multipart/form-data,application/x-www-form-urlencoded
Access-Control-Allow-Methods: GET,POST,OPTIONS
Content-Length: 49

<!DOCTYPE html>   //正文
<html>  
	<head>
		<title>網頁標題</title>
		<meta charset="utf-8">
	</head>
	<body>
		網頁內容
	</body>
</html>
Servlet使用

Servlet接口

在ServletAPI中最重要的是Servlet接口,所有Servlet都會直接或間接的與該接口發生聯繫,或是直接實現該接口,或間接繼承自實現了該接口的類。

該接口包括以下五個方法:

//初始化servlet方法
init(ServletConfig config)
//獲取Servlet配置
ServletConfig getServletConfig()
//服務方法:執行處理瀏覽器請求的方法
service(ServletRequest req,ServletResponse res)
//獲取servlet信息:作者版權
String getServletInfo()
//銷燬方法
destroy( )

處理方式:
(1)第一次訪問Servlet時,服務器會創建Servlet對象,並調用init方法,再調用service方法
(2)第二次再訪問時,Servlet對象已經存在,不再創建,執行service方法
(3)當服務器停止,會釋放Servlet,調用destroy方法。

HttpServlet類
是繼承GenericServlet的基礎上進一步的擴展。
提供將要被子類化以創建適用於 Web 站點的 HTTP servlet 的抽象類。HttpServlet 的子類至少必須重寫一個方法,該方法通常是以下這些方法之一:
doGet,如果 servlet 支持 HTTP GET 請求
doPost,用於 HTTP POST 請求
doPut,用於 HTTP PUT 請求
doDelete,用於 HTTP DELETE 請求
initdestroy,用於管理 servlet 的生命週期內保存的資源
getServletInfoservlet 使用它提供有關其自身的信息

Servlet的兩種創建方式

Servlet的第一種創建方式:繼承HttpServlet
Servlet創建的第二種方式:實現接口Servlet

Servlet的兩種配置方式

第一種註解式配置 Servlet3.0及以後 :

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

使用註解方式:

註解類 WebServlet
name:serlvet名字 (可選)
value: 配置url路徑
urlPatterns:配置url路徑 ,和value作用一樣,不能同時使用
loadOnStartup:配置Servlet的創建的時機, 如果是0或者正數 啓動程序時創建,如果是負數,則訪問時創建。數字越小優先級越高。
initParams:配置Servlet的初始化參數

第二種web.xml配置 Servlet所有版本都支持:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
  <display-name>Web_Day11</display-name>
  <!--Servlet的第二種配置  -->
  <!--Servlet配置  -->
  <servlet>
  <!--名稱  -->
    <servlet-name>hello2</servlet-name>
    <!--Servlet的全稱類名  -->
    <servlet-class>com.qf.web.servlet.HelloServlet</servlet-class>
    <!--啓動的優先級,數字越小越先起作用  -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--映射配置  -->
  <servlet-mapping>
  <!--名稱  -->
    <servlet-name>hello2</servlet-name>
    <!--資源的匹配規則:精確匹配  -->
    <url-pattern>/hello2</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
  </welcome-file-list>
</web-app>

容器在進行url-pattern配置的時候是遵循一定的匹配原則的
url-pattern定義匹配規則,取值說明:
精確匹配 /具體的名稱 只有url路徑是具體的名稱的時候纔會觸發Servlet
後綴匹配 .xxx 只要是以xxx結尾的就匹配觸發Servlet
通配符匹配 /
匹配所有請求,包含服務器的所有資源
通配符匹配 / 匹配所有請求,包含服務器的所有資源,不包括.jsp

load-on-startup
1元素標記容器是否應該在web應用程序啓動的時候就加載這個servlet。
2它的值必須是一個整數,表示servlet被加載的先後順序。
3如果該元素的值爲負數或者沒有設置,則容器會當Servlet被請求時再加載。
4如果值爲正整數或者0時,表示容器在應用啓動時就加載並初始化這個servlet,值越小,servlet的優先級越高,就越先被加載。值相同時,容器就會自己選擇順序來加載。

<init-param>
      <param-name>name</param-name>
      <param-value>張三</param-value>
</init-param>

1 init-param元素用來定義Servlet啓動的參數,可以定義多個
2 param-name表示參數名稱
3 param-value表示參數值

Servlet生命週期
階段一、實例化(調用構造方法)
階段二、初始化(init方法)
階段三、就緒/服務
階段四、銷燬

獲取請求參數

GET請求

GET提交的數據會放在URL之後,以?分割URL和傳輸數據,參數之間以&相連
GET提交的數據大小有限制(因爲瀏覽器對URL的長度有限制)
GET方式提交數據,會帶來安全問題
效率高

對應的Servlet的方法是doGet

POST請求

POST方法是把提交的數據放在HTTP包的Body中
POST方法提交的數據沒有限制
POST提交的數據相對安全
效率相對沒有GET高

對應的Servlet的方法是doPost

中文亂碼處理

產生亂碼,就是因爲服務器和客戶端溝通的編碼不一致造成的,因此解決的辦法是:在客戶端和服務器之間設置一個統一的編碼,之後就按照此編碼進行數據的傳輸和接收

GET中文亂碼
在Tomcat7及以下
客戶端以UTF-8的編碼傳輸數據到服務器端,而服務器端的request對象使用的是ISO8859-1這個字符編碼來接收數據,服務器和客戶端溝通的編碼不一致因此纔會產生中文亂碼的。解決辦法:在接收到數據後,先獲取request對象以ISO8859-1字符編碼接收到的原始數據的字節數組,然後通過字節數組以指定的編碼構建字符串,解決亂碼問題。

Tomcat8的版本中GET基本就不會亂碼了,因爲服務器對url的編碼格式可以進行自動轉換

POST亂碼
由於客戶端是以UTF-8字符編碼將表單數據傳輸到服務器端的,因此服務器也需要設置以UTF-8字符編碼進行接收,要想完成此操作,服務器可以直接使用從ServletRequest接口繼承而來的"setCharacterEncoding(charset)"方法進行統一的編碼設置。

Servlet輸出中文內容
瀏覽器識別不到返回的中文是什麼編碼格式,就會默認使用GB2312,如果返回的是UTF-8格式的那麼在瀏覽器上就會顯示亂碼的問題

解決內容中的亂碼

response.setContentType("text/html;charset=UTF-8");//方式一
response.setCharacterEncoding("UTF-8");//方式二:輸出一個完整的網頁
線程安全問題

因爲每次請求都會創建一個線程,如果多人同時請求,那麼就會存在多個線程操作同一個Servlet對象,那麼如果在對應的方法中操作了成員變量,就有可能產生線程安全的問題。

如何保證線程安全

1、synchronized
將存在線程安全問題的代碼放到同步代碼塊中

2、實現SingleThreadModel接口
servlet實現SingleThreadModel接口後,每個線程都會創建servlet實例,這樣每個客戶端請求就不存在共享資源的問題,但是servlet響應客戶端請求的效率太低,所以已經淘汰。

3、儘可能只使用局部變量

表單數據請求響應示例:

package com.tomcat;

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 java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ServletDemo")
public class ServletDemo extends HttpServlet {
    //request請求,response響應
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //設置接收到的請求體信息爲utf-8編碼
        request.setCharacterEncoding("utf-8");
        //沒有在響應頭聲明編碼,使用時還需要在輸出時輸出整個html聲明
        //response.setCharacterEncoding("utf-8");
        //設置響應體文本格式和編碼
        response.setContentType("text/html;charset=utf-8");
        //獲取請求體的內容
        String username = request.getParameter("username");
        String phone = request.getParameter("phone");
        System.out.println(username+"->"+phone);
        //給請求體響應
        PrintWriter writer = response.getWriter();
        writer.print("<h2>提交成功!!!</h2>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
<meta charset="UTF-8">
<form action="ServletDemo" method="post">
    用戶名:<input type="text" name="username">
    <br />
    聯繫方式:<input type="text" name="phone">
    <br />
    <input type="submit" value="提交">
</form>

頁面跳轉

Java Web服務端控制頁面跳轉主要有兩種:重定向和轉發

重定向

重定向就是通過各種方法將網絡請求重新定個方向轉到其它位置。

實現原理:
客戶瀏覽器發送http請求----》web服務器接受後發送302狀態碼響應及對應新的location給客戶瀏覽器–》客戶瀏覽器發現是302響應,則自動再發送一個新的http請求,請求url是新的location地址----》服務器根據此請求尋找資源併發送給客戶。

特點:
1、重定向是客戶端行爲。
2、重定向是瀏覽器做了至少兩次的訪問請求。
3、重定向瀏覽器地址改變。
4、重定向兩次跳轉之間傳輸的信息會丟失(request範圍)。
5、重定向可以指向任何的資源,包括當前應用程序中的其他資源,同一個站點上的其他應用程序中的資源,其他站點的資源。注意:傳遞給HttpServletResponse.sendRedirect 方法的相對URL以“/”開頭,它是相對於整個WEB站點的根目錄
示例:
301跳轉:

@WebServlet("/ServletDemo4")
public class ServletDemo4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	//設置響應狀態碼
        response.setStatus(301);
        //設置響應頭信息設置響應信息頭
        response.setHeader("Location","ServletDemo");
        System.out.println("不想處理");
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

302跳轉:

@WebServlet("/ServletDemo")
public class ServletDemo extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("http://www.baidu.com");
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
請求轉發

原理:
客戶瀏覽器發送http請求----》web服務器接受此請求–》調用內部的一個方法在容器內部完成請求處理和轉發動作----》將目標資源發送給客戶。在這裏,轉發的路徑必須是同一個web容器下的url,其不能轉向到其他的web路徑上去,中間傳遞的是自己的容器內的request。在客戶瀏覽器路徑欄顯示的仍然是其第一次訪問的路徑,也就是說客戶是感覺不到服務器做了轉發的。

特點:
1,轉發是服務器行爲
2,轉發是瀏覽器只做了一次訪問請求
3,轉發瀏覽器地址不變
4,轉發兩次跳轉之間傳輸的信息不會丟失,所以可以通過request進行數據的傳遞
5,轉發只能將請求轉發給同一個WEB應用中的組件
注意:如果創建RequestDispatcher 對象時指定的相對URL以“/”開頭,它是相對於當前WEB應用程序的根目錄。

網絡路徑

絕對路徑: 用在不同網站之間的跳轉,比如:http://www.baidu.com/aaa/1.jpg

相對路徑: 用在同一個網站中, aaa/1.jpg,僅限靜態資源,如果頁面比較多,並且使用框架,會出現混亂。

根路徑:根指定就是主機名(服務器) /day12web1/loginservlet (/ 表示 http://localhost:8080/)

/day12web1/loginservlet 如果在瀏覽器中使用 / 表示 http://localhost:8080/

/listservlet 如果是在服務器中使用 / 表示 /day12web1

response對象

在Servlet中可以使用的內置對象主要有:request、response、application、session、out(PrintWriter)。

ServletResponse簡介

​ 定義輔助 servlet 將響應發送到客戶端的對象。servlet 容器創建 ServletResponse 對象,並將它作爲參數傳遞給 servlet 的 service 方法。 要發送 MIME 正文響應中的二進制數據,請使用 getOutputStream 返回的 ServletOutputStream。要發送字符數據,請使用 getWriter 返回的 PrintWriter 對象。

2.2 HttpServletResponse介紹

擴展 ServletResponse 接口以提供特定於 HTTP 的發送響應功能。例如,該接口擁有訪問 HTTP 頭和 cookie 的方法。 客戶端向服務器發起的都是HTTP協議操作,所以我們大部分使用HttpServletResponse對象作爲直接操作對象!

HttpServletResponse 常用API介紹
方法名稱 作用
setStatus(int code) 設置響應狀態碼:200 成功 302 臨時重定向 304 處理緩存 404 Not Found沒有找到資源,500 服務器錯誤
setHeader(name,value) 設置響應信息頭
setCharacterEncoding(String); 設置編碼格式
setContentType(String) 設置返回數據mimetype
getWriter() 獲取字符輸出流
getOutputStream() 獲取字節輸出流
設置返回字符編碼格式

方法一:
可以解決返回字符串亂碼問題,但是需要將返回的字符串封裝到html代碼中.操作繁瑣!

response.setCharacterEncoding("utf-8");

方法二
方案按相對簡單,通過設置響應頭告知瀏覽器解析字符串的編碼格式!

 response.setHeader("Content-type","text/html;charset=UTF-8")

方法三(推薦)
利用setContentType這種綜合性的寫法解決問題!此方法也是開發中常用的方法!方便!

 response.setContentType("text/html;charset=UTF-8")

request對象

ServletRequest介紹

定義將客戶端請求信息提供給某個 servlet 的對象。servlet 容器創建ServletRequest 對象,並將該對象作爲參數傳遞給該 servlet 的service方法。

3.2 HttpServletRequest介紹

HttpServletRequest對象代表客戶端的請求,當客戶端通過HTTP協議訪問服務器時,HTTP請求頭中的所有信息都封裝在這個對象中,開發人員通過這個對象的方法,可以獲得客戶這些信息。

小結: 同響應相同,客戶端請求協議都是基於HTTP所以我們選用HttpServletRequest來操作用戶發送過來的請求的數據!

3.3 HttpServletRequest常用API

URL :Uniform Resource Location (統一資源定位符) 網址

URI :Uniform Resource Identifier (統一資源標識符) URI包含URL

美國總統:—》特朗普 URI
他二大爺:—》特朗普 URI
美國白宮10號樓99號房間–> 特朗普的地址 URL

//獲取請求路徑相關參數
**
getRequestURL方法返回客戶端發出請求時的完整URL。
getRequestURI方法返回請求行中的資源名部分。
getQueryString 方法返回請求行中的參數部分。
getRemoteAddr方法返回發出請求的客戶機的IP地址
getRemoteHost方法返回發出請求的客戶機的完整主機名
getRemotePort方法返回客戶機所使用的網絡端口號
getLocalAddr方法返回WEB服務器的IP地址。
getLocalName方法返回WEB服務器的主機名
getMethod得到客戶機請求方式
//獲取請求頭信息
getHead(name)方法 
getHeaders(String name)方法 
getHeaderNames方法 
//獲取請求正文參數
**getParameter(name)方法
**getParameterValues(String name)方法
getParameterNames方法 
getParameterMap方法  //做框架用,非常實用
getInputStream方法 獲取輸入流
封裝請求參數

將數據封裝到實體類上

創建一個對應的實體類!

實體類要變量命名和變量類型都有相應的要求,要求變量名跟提交參數的key相同,變量跟參數類型相同!

public class User {
 
	private String username;
	private String password;
	private String sex;
	private String [] hobby;
 	//getter/setter 
} 

第一種方式:使用getParameter獲取

String username = req.getParameter("username");
String password=req.getParameter("password");
String gender=req.getParameter("gender");
String[] hobby = req.getParameterValues("hobby");

User user=new User(username,password,gender,hobby);
System.out.println(user.toString());

第二種方式:使用反射進行解析

            //表單的name值和value值
		   //key  name值  value  values值
		   Map<String, String[]> parameterMap = request.getParameterMap();
		  
		   User bean = new User();
		   
		   Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
		   
		   for (Entry<String, String[]> entry : entrySet) {
			//entry map中的一條
			   //username password sex
			   String key = entry.getKey();
			   String[] value = entry.getValue();
			   
			   try {
				PropertyDescriptor descriptor = 
						   new PropertyDescriptor(key, User.class);
				
				Method set = descriptor.getWriteMethod();
				
				/**
				 * 參數:哪個對象的set方法
				 * password username sex
				 */
				if (value.length == 1) {
					set.invoke(bean, value[0]);
				}else{
					set.invoke(bean, (Object)value);
				}
			
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
   
		  }

第三種方式:使用Apache BeanUtils進行快速映射

1.導入beanutils對應jar包、logging日誌、commons-collections-3.2.1.jar
2.映射

BeanUtils.populate(bean2, request.getParameterMap());	
System.out.println(bean2);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章