JavaWEB - Servlet

MSJ_743579531發表於2020-10-05

Servlet

一、介紹

Servlet(Server Applet),全稱Java Servlet。是用Java編寫的伺服器端程式,其主要功能在於互動式地瀏覽和修改資料,生成動態Web內容。狹義的Servlet是指Java語言實現的一個介面,廣義的Servlet是指任何實現了這個Servlet介面的類,一般情況下,人們將Servlet理解為後者。
Servlet執行於支援Java的應用伺服器中。從實現上講,Servlet可以響應任何型別的請求,但絕大多數情況下Servlet只用來擴充套件基於HTTP協議的Web伺服器。

Servlet工作模式:

① 客戶端傳送請求至伺服器
② 伺服器啟動並呼叫Servlet,Servlet根據客戶端請求生成響應內容並將其傳給伺服器
③ 伺服器將響應返回客戶端

二、API

1.javax.servlet Servlet介面:

Servlet介面是一個核心介面,Servlet的所有功能都是在這個介面的基礎之上來實現的。
它有五個抽象方法:
init()、service()、destory()、getServletConfig()、getServletInfo()

2.javax.servlet GenericServlet抽象類:

GenericServlet抽象類除了實現或繼承Servlet介面中的五個方法外,還提供了額外方法:
getInitParameter():獲得初始化引數
getServletName():獲得Servlet名稱
getServletContext():獲得Servlet上下文物件

3.javax.servlet.http HttpServlet抽象類:

HttpServlet抽象類繼承了GenericServlet抽象類,重寫了service()方法,提供了doGet()、doPost()等方法。

三、使用方法

首先建立一個類實現Servlet介面,重寫方法,或者繼承HttpServlet。

public class LoginServlet implements Servlet {
	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		System.out.println("初始化引數的方法");
	}
	@Override
	public ServletConfig getServletConfig() {
		return null;
	}
	@Override
	public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
		System.out.println("執行業務邏輯的方法");
	}
	@Override
	public String getServletInfo() {
		return null;
	}
	@Override
	public void destroy() {
	System.out.println("銷燬servlet");
	}
}

然後在web.xml文件中配置對映關係。

<servlet>
	<servlet-name>自定義名稱</servlet-name>
	<servlet-class>處理請求的類的完整路徑</servlet-class>
</servlet>
<servlet-mapping><!-- mapping 表示對映 -->
	<servlet-name>自定義名稱</servlet-name>
	<url-pattern>請求名</url-pattern>
	</servlet-mapping>

例如:

<servlet>
	<servlet-name>aa</servlet-name>
	<servlet-class>
		com.yhp.web.DemoServlet
	</servlet-class>
</servlet>
<servlet-mapping><!-- mapping 表示對映 -->
	<servlet-name>aa</servlet-name>
	<url-pattern>/tologin</url-pattern>
</servlet-mapping>

標籤的執行順序:
請求過來以後→web.xml→servlet-mapping標籤中的url-pattern標籤中的內容和請求名進行匹配→匹配成功後找對應的servlet-mapping標籤中的servlet-name→去servlet標籤中找和上一個servlet-name相同的name值→去找servlet標籤中的servlet-class中處理類的完整路徑。

四、工作原理

1.Servlet介面定義了Servlet與servlet容器之間的契約。這個契約是:Servlet容器將Servlet類載入記憶體,併產生Servlet例項和呼叫它具體的方法。但是要注意的是,在一個應用程式中,每種Servlet型別只能有一個例項。
2.使用者請求致使Servlet容器呼叫Servlet的Service()方法,並傳入一個ServletRequest物件和一個ServletResponse物件。ServletRequest物件和ServletResponse物件都是由Servlet容器(例如TomCat)封裝好的,並不需要程式設計師去實現,程式設計師可以直接使用這兩個物件。
3.ServletRequest中封裝了當前的Http請求,因此,開發人員不必解析和操作原始的Http資料。ServletResponse表示當前使用者的Http響應,程式設計師只需直接操作ServletResponse物件就能把響應輕鬆的發回給使用者。
4.對於每一個應用程式,Servlet容器還會建立一個ServletContext物件。這個物件中封裝了上下文(應用程式)的環境詳情。每個應用程式只有一個ServletContext。每個Servlet物件也都有一個封裝Servlet配置的ServletConfig物件。

五、生命週期

當客戶端首次傳送第一次請求後,由容器(web伺服器tomcat)去解析請求,根據請求找到對應的servlet,判斷該類的物件是否存在,不存在則建立servlet例項,調取init()方法進行初始化操作,初始化完成後調取service()方法,由service()判斷客戶端的請求方式,如果是get,則執行doGet(),如果是post則執行doPost()。處理方法完成後,作出相應結果給客戶端,單次請求處理完畢。
當使用者傳送第二次以後的請求時,會判斷物件是否存在,但是不再執行init(),而直接執行service方法,調取doGet()或doPost()方法。
當伺服器關閉時調取destroy()方法進行銷燬。

四個過程:

1.例項化:建立servlet例項
2.初始化:init()
3.處理請求:service()
4.服務終止:destory()
生命週期

六、請求request

HttpServletRequest表示Http環境中的Servlet請求。它擴充套件於javax.servlet.ServletRequest介面

常用方法:

1.String getParameter(String name)
根據表單元件名稱獲取提交資料,返回值是String
注:伺服器在接收資料時使用字串統一接收
2.String[] getParameterValues(String name)
獲取表單元件對應多個值時的請求資料
3.void setCharacterEncoding(String charset)
指定每個請求的編碼(針對post請求才起作用)
4.RequestDispatcher getRequestDispatcher(String path)
跳轉頁面,返回一個RequestDispatcher物件,該物件的forward()方法用於轉發請求
5.存值:request.setAttribute(“key”,value);
6.取值:request.getAttribute(“key”); //取值後需要向下轉型

客戶端傳送資料給伺服器:

方式1:通過表單提交(可選擇get/post提交)
方式2:通過a標籤傳送資料(get提交)

<a href="請求名?key=value&key=value&key=value...">
<a href="/login?a=10&name=abc&pass=123">

這裡的key值=表單元素的控制元件名,value值=表單中控制元件的value屬性值,通過getParameter獲取資料。
方式3:通過位址列直接拼接(get提交)
方式4:js提交資料(get提交)
location.href=“目標請求?key=value&key=value”

處理請求亂碼:

方式1:setCharacterEncoding(“UTF-8”); //post提交時管用
方式2:String s=new String(變數名.getBytes(“ISO-8859-1”),“UTF-8”); //針對於get提交時中文亂碼
示例:String s=new String(request.getParameter(“key”).getBytes(“ISO-8859-1”),“GBK”);
方式3:修改tomcat中配置檔案: //適用於get提交
在Tomcat目錄結構\conf\server.xml中設定字符集

get和post的區別:

1.GET請求請求的資料會附加在URL之後,以?分割URL和傳輸資料,多個引數用&連線。URL的編碼格式採用的是ASCII編碼,而不是uniclde,即所有的非ASCII字元都要編碼之後再傳輸。而POST請求會把請求的資料放置在HTTP請求包的包體中,上面的item=bandsaw就是實際的傳輸資料。
因此,GET請求的資料會暴露在位址列中,而POST請求則不會。
2.傳輸資料的大小
在HTTP規範中,沒有對URL的長度和傳輸的資料大小進行限制。但是在實際開發過程中,對於GET,特定的瀏覽器和伺服器對URL的長度有限制。因此,在使用GET請求時,傳輸資料會受到URL長度的限制。而對於POST,由於不是URL傳值,理論上是不會受限制的,但是實際上各個伺服器會規定對POST提交資料大小進行限制,Apache、IIS都有各自的配置。
3.安全性
POST的安全性比GET的高。這裡的安全是指真正的安全,而不同於上面GET提到的安全方法中的安全,上面提到的安全僅僅是不修改伺服器的資料。比如,在進行登入操作,通過GET請求,使用者名稱和密碼都會暴露再URL上,因為登入頁面有可能被瀏覽器快取以及其他人檢視瀏覽器的歷史記錄的原因,此時的使用者名稱和密碼就很容易被他人拿到了。除此之外,GET請求提交的資料還可能會造成Cross-site request frogery攻擊。

示例:

public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //處理post請求
        req.setCharacterEncoding("utf-8"); //一定要在接收引數的前面
        //1.接收引數
        String uname = req.getParameter("uname");
        String upass = req.getParameter("upass");
        System.out.println("uname="+uname+"upass="+upass);
        String[] loves = req.getParameterValues("love");
        for(String love:loves) {
            System.out.println("love="+love);
        }
        //2.跳轉頁面測試
        if("admin".equals(uname)) {
            req.getRequestDispatcher("/success.html").forward(req,resp);
        } else {
            req.getRequestDispatcher("/error.html").forward(req,resp);
        }
    }
}

七、響應response

在Service API中,定義了一個HttpServletResponse介面,它繼承自ServletResponse介面,專門用來封裝HTTP響應訊息。 在HttpServletResponse介面中定義了向客戶端傳送響應狀態碼,響應訊息頭,響應訊息體的方法。

常用方法:

1.void addCookie(Cookie var1);
給這個響應新增一個cookie
2.void sendRedirect(String var1) ;
傳送一條響應碼,將瀏覽器跳轉到指定的位置
3.PrintWriter getWriter()
獲得字元流,通過字元流的write(String s)方法可以將字串設定到response 緩衝區中,隨後Tomcat會將response緩衝區中的內容組裝成Http響應返回給瀏覽器端。
4.setContentType()
設定響應內容的型別

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	//處理get請求
	resp.setContentType("text/html;charset=utf-8");
	PrintWriter writer = resp.getWriter();
	writer.print("<script>alert('請通過表單進行登入');location.href='/login.html'</script>");
}

轉發和重定向對比:

轉發:request.getRequestDispatcher("/success.html").forward(request,response);
重定向:response.sendRedirect("/success.html");
相同點是都用來跳轉頁面,不同點是重定向時位址列會改變,request中儲存的資料會丟失;轉發時位址列顯示的是請求頁面的地址,request資料可以儲存。轉發屬於一次請求一次響應,重定向屬於兩次請求(位址列修改了兩次)兩次響應。
補充:使用out物件往頁面中輸出js或html或css
out.print("<script type=‘text/javascript’>alert(‘登入失敗’);location=’…/login.jsp’</script>");
注:使用js跳轉頁面,也會丟失request中的資料

八、會話session

request存的值只能在單次請求中儲存,儲存的資料不能跨頁面,當重定向時,request存的值會丟失;session的資料可以在多個頁面中共享,即使重定向頁面,資料也不會丟失,session中可以包含n個request。
從開啟瀏覽器到關閉瀏覽器,期間訪問伺服器就稱為一次會話。
可使用session驗證使用者是否登入。

常用方法:

1.void setAttribute(String key,Object value)
以key/value的形式儲存物件值,將資料儲存在伺服器端
2.Object getAttribute(String key)
通過key獲取物件值
3.void invalidate()
設定session物件失效
4.String getId()
獲取sessionid,當第一次登入成功後,session會產生一個唯一的id,瀏覽器之後訪問時如果發現id值還是之前id,那麼說明當前訪問的屬於同一個會話。
5.void setMaxInactiveInterval(int interval)
設定session的非活動時間
6.int getMaxInactiveInterval()
獲取session的有效非活動時間(以秒為單位),預設的有效時間是30分鐘
7.void removeAttribute(String key)
從session中刪除指定名稱(key)所對應的物件

設定有效時間:

方式1:session.setMaxInactiveInterval(10*60);
方式2:修改web.xml

<session-config>
	<session-timeout>10</session-timeout> //單位:分鐘
</session-config>

讓session失效:

1.invalidate()
2.removeAttribute(“key”)
3.關閉瀏覽器

自動跳轉:

<meta http-equiv=“refresh” content=“3;url=index.jsp”>
在head標籤中新增該標籤即可,單位:秒。

九、獲得初始化引數

例如:request.setCharacterEncoding(“utf-8”);
程式碼的耦合度太高,不便於後期維護修改,可以通過初始化引數實現。

1.實現方式:

首先在web.xml中定義初始化引數

<servlet>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>utf-8</param-value>
	</init-param>
</servlet>

然後在servlet中獲得初始化引數,重寫init()方法

public void init(ServletConfig config) throws ServletException {
	encoding = config.getInitParameter("encoding");
}

這種方式的初始化引數僅限於當前servlet中使用。

2.全域性初始化引數:

首先定義,context-param是和servlet標籤同級別

<context-param>
	<param-name>encoding</param-name>
	<param-value>utf-8</param-value>
</context-param>

然後獲得資料

public void init(ServletConfig config) throws ServletException {
	encoding = config.getServletContext().getInitParameter("encoding");
}

十、註解實現Servlet

@WebServlet註解配置Servlet
從Servlet3.0開始,配置Servlet支援註解方式,但還是保留了配置web.xml方式,所以使用Servlet有兩種方式:
1.Servlet類上使用@WebServlet註解進行配置
2.web.xml檔案中配置

@WebServlet常用屬性:

WebServlet常用屬性
loadOnStartup屬性:標記容器是否在啟動應用時就載入Servlet,預設不配置或數值為負數時表示客戶端第一次請求Servlet時再載入;0或正數表示啟動應用就載入,正數情況下,數值越小,載入該Servlet的優先順序越高。
name屬性:可以指定也可以不指定,通過getServletName()可以獲取到,若不指定,則為Servlet的完整類名。
urlPatterns/value屬性: String[]型別,可以配置多個請求地址。

注意事項:

<web-app></web-app>根元素中不能配置屬性metadata-complete=“true”,否則無法載入Servlet。metadata-complete屬性表示通知Web容器是否尋找註解,預設不寫或者設定false,容器會掃描註解,為Web應用程式構建有效的後設資料;metadata-complete=“true”,會在啟動時不掃描註解(annotation)。如果不掃描註解的話,用註解進行的配置就無法生效。

urlPatterns的常用規則:

/*或者/:攔截所有
*.do:攔截指定字尾
/user/test:攔截路徑

示例:

@WebServlet(urlPatterns = {"/test"},
        initParams = {
            @WebInitParam(name = "code",value = "utf-8")
        },loadOnStartup = 1)
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get請求被執行");
        resp.sendRedirect("success.html");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("初始化引數code="+config.getInitParameter("code"));
    }
}

相關文章