【Java Web初級系列教程】Servlet教程

前言:剛上手Java Web的時候,想尋找好一點的網上教程,發現網上資源魚龍混雜,框架滿天飛,對於新手剛開始就希望從基礎的原理性的開始一步步瞭解,索性自己蒐集材料開始寫。

      本系列教程專門爲Java Web初級開發者設計,階梯式的循序漸進,沒有用到任何框架,都是基礎篇,一步步帶你揭開Java Web應用的面紗,這是第二篇。

       背景說明

            任何事物的產生都是有原因的,爲什麼會有Servlet呢?請看上一篇文章,大笑

上一篇文章:【Java Web初級系列教程】Web基礎知識介紹了 Java Web應用基礎知識;Web服務器、Web客戶端、HTTP、HTML、Web容器、用Servlet和JSP創建簡單應用,並查看了效果,這篇文章給你介紹關於Java Servlet更多細節、Java Servlet API核心接口、Servlet 3.0 annotation、Servlet生命週期、最後同樣會有機哥的Demo:登錄應用。

     目錄:

          1.Servlet簡介

          2.Common Gateway Interface(CGI)

          3.CGI vs Servlet

          4.Servlet API  層級

              4.1 Servlet Interface

              4.2 ServletConfig Interface

              4.3 ServletContext Interface

              4.4 ServletRequest Interface

              4.5 ServletResponse Interface

              4.6 RequestDispatcher Interface

              4.7 GenericServlet Interface

              4.8 HttpServer class 

          5.Servlet 屬性(Attributes)

          6.Servlet3 Annotations

          7.Java Servlet登錄Demo

 1.Servlet簡介

     上一篇說過,如果你要處理客戶端請求,應該創建Servlet,javax.servlet和javax.servlet.http包給我們提供了實現Servlet的接口和類。當初設計這二個包的時候,設計的人覺得Servlet(Server Applet)服務小程序應該是一種抽象的服務,不依賴具體的協議,但是後來我們發現這麼多年基本servlet都是基於HTTP協議的,所以我們基本上看到實現的Servlet都是繼承HttpServlet的。HttpServlet提供了Servlet基本的方法,例如doGet()和doPost().

2.Common Gateway Interface(CGI)

       在Java Servlet API之前,CGI技術被用來創建動態Web應用。CGI技術有很多缺點:爲每一個請求都創建進程,平臺獨立代碼(C、C++),高內存消耗以及低性能。

3.CGI vs Servlet

     Servlet呢就克服了CGI技術的短板:
          1.Servlet處理時間、內存利用率都比CGI優秀。Servlet使用多線程並且爲每一個請求創建一個新線程,CGI則是爲每一個請求創建一個新對象;
          2.Servlet可移植性更好。用Servlet做的Web應用能夠運行在Web容器中:Tomcat、JBoss、Glassfish等,操作系統Windows、Linux、Unix、Solaris.Mac都可以;
          3.Servlet很強大。因爲Web容器實現了servlet的生命週期管理,我們不需要擔心內存泄漏、安全和垃圾回收等;
          4.Servlet很容易維護,學習曲線很平緩,因爲所有我們需要去做的就是關係業務邏輯。

4.Servlet API  層級

     這部分內容會比較枯燥,但是很多時候學習一門技術或者一項技能,你剛開始可能要先死記住一些規範,就像武功祕籍中的心法口訣,然後再在實踐中慢慢領悟使用,乃至提升,最後成爲武林高手,這一部分內容就是需要先記住,然後後面慢慢用的過程中體會、領悟。
    javax.servlet.Servlet是Servlet API的最基礎的接口。接下來我會介紹一些其他的接口和類都是我們需要記住的,未來開發的時候會用到的。下面是Servlet API的層級圖:

   4.1 Servlet Interface
        說過,javax.servlet.Servlet是最基礎的接口,這裏聲明瞭Servlet的生命週期管理,你應該理解生命週期管理吧。好吧,我還是解釋一下,生命週期指的是創建、初始化、執行、銷燬等不同階段,體現在Servlet中就是一個個函數,構造函數就是創建、init()函數初始化、service()執行、destory()銷燬。那在javax.servlet.Servlet接口中的方法有:
     (1).public abstract void init(ServletConfig paramServletConfig) throws ServletException –這個方法非常非常重要,這個方法是被Web容器來調用的,用來初始化servlet和ServletConfig參數。這個方法執行結束之後Servlet才準備好去處理客戶的請求。這個方法在Servlet生命週期中只調用一次,並且讓這個和別的Java類不一樣。我們可以繼承這個方法,在這個方法內初始化Servlet需要用到的資源,數據準備工作,例如數據庫連接、Socket連接等。
     (2).public abstract ServletConfig getServletConfig() –這個方法返回一個Servlet 配置對象,這個對象包含了這個Servlet的初始化參數和啓動配置,具體一點講,我們可以拿到在發佈描述(web.xml)或者Servlet 3 annotation定義的初始化參數。後面我們會講到ServletConfig接口。
      (3).public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException -這個方法就是實際負責處理客戶端請求的方法,無論Servlet容器接受到任何請求,它會創建新的線程來執行service()方法,把request和response作爲參數傳入。Servlet通常運行在多線程環境中,所以要注意的是讓共享的資源通過同步達到線程安全。
    (4).public abstract String getServletInfo() - 這個方法返回的字符串包括Servlet的信息,如:作者、版本、版權。
   4.2 ServletConfig Interface
       javax.servlet.ServletConfigServlet的配置信息用這個對象存儲。每一個Servlet都有自己的ServletConfig對象,ServletConfig對象是Web容器負責創建和初始化的。我們可以通過在web.xml或者WebInitParam annotation設置Servlet的初始化參數。ServletConfig接口重要的方法是:
      (1).public abstract ServletContext getServletContext() –這個方法返回ServletContext對象,這裏重點說下和ServletConfig區別,ServletConfig是每個Servlet都帶有一個,是專屬的Servlet配置,ServletContext是所有Servlet共享一個Context,所以如果想在多個Servlet中共享數據,可以把數據放在ServletContext中。
      (2).public abstract Enumeration<String> getInitParameterNames() –這個方法返回Servlet的初始化參數的枚舉,沒有沒有初始化,這個是個空的枚舉。
       (3).public abstract String getInitParameter(String paramString) –這個方法獲取指定名稱的值,paramString是參數名稱,如果沒有這個參數,這個函數返回null。
   4.3 ServletContext Interface
       javax.servlet.ServletContext接口提供了當問Web應用程序變量的方法。一個Java Web應用只有一個ServletContext對象,這個對象對所有Servlet都是可見的,所以如果我們想在多個Servlet中共享初始的參數,那我們可以在web.xml中使用<context-param>元素初始化參數。我們·可以通過ServletConfig的getServletContext() 方法獲取到ServletContext。ServletContext接口也有些重要的函數:
     (1).public abstract ServletContext getContext(String uripath) –這個方法獲取指定URL的Context,我們前面講過,一個Web應用只有一個Context,應用之間的交互可以通過另一個應用的URL路徑來找到。
     (2).public abstract URL getResource(String path) throws MalformedURLException –這個函數根據給定的資源路徑返回一個URL對象,你可能會奇怪,URL不就是資源路徑嘛,怎麼還會根據路徑找路徑,這裏的路徑不一樣,URL是HTTP協議的絕對路徑,這裏的path可以是文件相對路徑,可以是數據庫,可以是網絡路徑等等。
       (3).public abstract InputStream getResourceAsStream(String path) –這個方法根據給定path返回InputStream對象,這個方法主要是讓path路徑下的資源對外部Servlet可訪問。
       (4) .public abstract RequestDispatcher getRequestDispatcher(String urlpath) –這個方法大部分時候是用來獲取另一個Servlet的引用。在獲取RequestDispatcher對象之後, Servlet可以將請求轉發到RequestDispatcher引用的資源對象上,或者將請求數據的內容取出來發給RequestDispatcher引用的資源對象。這裏可能有點費解,後面在Demo中詳細解釋。
       (5).public abstract Object getAttribute(String name) - 這個函數返回對於屬性名稱的值對象
       (6).public abstract void setAttribute(String paramString, Object paramObject) –這個函數用來設置應用作用域的屬性,這個屬性對於所有Servlet都是開放的,是應用級的。
       (7).String getInitParameter(String name) –這個方法返回在web.xml中定義的初始參數的值
說明:說實話,這個接口的名稱應該稱爲ApplicationContext,不是任何指定的Servlet的。不要和ServletContext getContext(String uripath) 混淆,getContext(String uripath) 是用來獲取別的應用的Context的,這個用在應用間的通信。
      4.4 ServletRequest Interface
        ServletRequest 是Web容器創建的,封裝了客戶端的請求信息,Web容器會將它作爲參數傳給service()方法調用。ServletRequest 有幾個比較終於的方法:
      (1).Object getAttribute(String name) –這個方法獲取請求信息的值(對象)
        (2).String getParameter(String name) –這個方法返回請求參數值(字符串)
      (3).String getServerName() –返回請求中的主機名
      (4).int getServerPort() – 獲取服務端口
 注意:一般我們用的比較多的是ServletRequest 子接口HttpServletRequest,上一篇說過,請求中包含很多信息,HTTP協議形式封裝好了這些信息。 HttpServletRequest還包含一些其他方法用來:Session管理、cookie 和請求授權。
    4.5 ServletResponse interface
         ServletResponse interface也是Web容器創建,用來發送應答信息給客戶端。Web服務器將ServletResponse作爲參數傳遞給service().HttpServletResponse中一些重要的方法:
     (1).void addCookie(Cookie cookie) –將cookie信息添加到Response
       (2).void addHeader(String name, String value) –用給定的name和value添加到response header中
       (3).String encodeURL(java.lang.String url) - 將Session Id編碼到指定的URL,如果URL不需要編碼,返回null。
       (4).void sendRedirect(String location) –使用location指定的URL發送一個臨時的重定向response給客戶端。
       (5).void setStatus(int sc) –被用來設置response的狀態碼。
    4.6 RequestDispatcher Interface
         RequestDispatcher interface 有二種使用方法,一種是將請求發給指定的資源,資源可以是HTML、JSP或同一個Context下其他Servlet,另一種用法是將其他資源發給Response。這個接口一般用來在同一個Context下的Servlet通信。
       (1).void forward(ServletRequest request, ServletResponse response) –將來自一個Servlet的請求發給另一個資源;
       (2).void include(ServletRequest request, ServletResponse response) –在Response中包含資源的內容
    4.7 GenericServlet Interface
         GenericServlet是個實現ServletConfig、Serializable 、Servlet的抽象類,這個類實現了所有的Servlet的生命週期管理、ServletConfig函數、並且使我們繼承這個類更容易。最重要的方法是init()方法。
    4.8 HttpServer class 
          HttpServer類是我們最常用的,基本上我們都是繼承這個類來寫我們自己的Servlet,這個類繼承 GenericServlet,提供了基本的HTTP 操作,我們繼承這個類以後的子類需要重寫以下幾個方法中的一個或幾個:
  1. doGet(), for HTTP GET requests
  2. doPost(), for HTTP POST requests
  3. doPut(), for HTTP PUT requests
  4. doDelete(), for HTTP DELETE requests

 5.Servlet 屬性(Attributes)

     Servlet屬性是用來內部Servlet通信的,有三種作用域的屬性:request作用域、session作用域、應用作用域。
ServletRequest、HttpSession和ServletContext提供了get/set/remove方法可以對三種作用域的屬性進行操作。
另外特別注意的是:Attribute和Parameter不同,Parameter是在web.xml聲明或annotation聲明的。

 6.Servlet3 Annotations

     在Servlet 3之前,所有的servlet 映射和parameter初始化都在web.xml中完成。現在可以更簡單啦。。有些重要的Servlet annotation:
     1.WebServlet -  我們用這個annotation聲明初始化Servlet和參數
     2.WebInitParam – 可以用在Servlet或者Filter,定義初始化參數,包含name和value
      3.WebFilter和WebListener  後面會詳細講解。

7.Java Servlet登錄Demo

      現在我們上硬菜,這個Demo主要驗證用戶登錄。
    首先是登錄頁面  login.jsp ,我們在web.xml歡迎頁設置爲login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <title>login.html</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
  	<form action="LoginServlet" method="post">
  		用戶名:<input type="text" name="user">
  		<br>
  		密碼:<input type="password" name="pwd">
  		<br>
  		<input type="submit" value="Login">
  	</form>
  </body>
</html>


如果用戶登錄成功,跳轉到LoginSuccess.jsp。Jsp文件如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>  
    <title>Login Success page</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
   	<h3>歡迎機哥,登錄成功</h3>
	<a href="login.html">登錄頁面</a>
  </body>
</html>
那負責處理請求的LoginServlet如下:
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(
	description = "Login Servlet",
	urlPatterns = {"/LoginServlet"},
	initParams = { 
			@WebInitParam(name = "user", value = "機哥"),
			@WebInitParam(name = "password", value = "123456")
	}
)
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private ServletContext servletContext;
	
	public LoginServlet() {
		super();
	}

	public void init() throws ServletException {
		servletContext = getServletContext();
		//一般我們會在做一些初始化操作,耗時的
		if(servletContext.getInitParameter("dbURL").equals("jdbc:mysql://localhost/mysql_db")&&
				servletContext.getInitParameter("dbUser").equals("mysql_user")&&
				servletContext.getInitParameter("dbUserPwd").equals("mysql_pwd")){
			servletContext.setAttribute("DB_success", "true");
		}else throw new ServletException("DB Connection error");
	}
	
	public void destroy() {
		super.destroy(); // Just puts "destroy" string in log
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		String user = request.getParameter("user");
		String pwd = request.getParameter("pwd");
		
		String userID = getServletConfig().getInitParameter("user");
		String password = getServletConfig().getInitParameter("password");
		
		System.out.println("user:"+user +"pwd:"+pwd);
		System.out.println("userID:"+userID +"password:"+password);
		response.setHeader("Content-type", "text/html;charset=UTF-8");  
		response.setCharacterEncoding("UTF-8"); 
		response.setContentType("text/html;charset=UTF-8");
		
		if(userID.equals(user) && password.equals(pwd)){
			
			response.sendRedirect("LoginSuccess.jsp");
		}else{
			PrintWriter out = response.getWriter();
			out.println("<font color=red>用戶名或密碼不對</font>");		
			RequestDispatcher rq = servletContext.getRequestDispatcher("/login.jsp");		
			rq.include(request, response);
		}
	}
}
注意@WebServlet和@WebInitParm,這是我們說過的Java annotation。

login.jsp

loginSuccess.jsp

嗯嗯嗯,最後的話,機哥留個QQ:493967121  歡迎Java Web開發工程師互相學習交流。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章