JAVA EE(二) —— Servlet 1(Servlet生命週期、創建Servlet項目、Servlet對象、Servlet配置文件、Servlet上傳下載文件)

一、Servlet 概述

1、Servlet 概述

(1)Servlet 介紹

  • 狹義的 Servlet 是指 Java 語言實現的一個接口,廣義的 Servlet 是指任何實現了這個 Servlet 接口的類,一般情況下,人們將 Servlet 理解爲後者。
  • Servlet 運行於支持 Java 的應用服務器中,從原理上講,Servlet 可以相應任何類型的請求,但絕大多數情況下 Servlet 只用來擴展基於 Http 協議的 Web 服務器。

(2)Servlet 特點

  • Servlet 是運行在支持 Java 的應用服務器上;
  • Servlet 的實現遵循了服務器能夠識別的規則,也就是服務器會自動的根據請求調用對應的 Servlet 進行請求處理;
  • Servlet 簡單方便,可移植性強。

2、Servlet 運行流程

實現Servlet 不管是繼承 HttpServlet 還是繼承 GenericServlet 類,都是爲了實現Servlet 的操作,Servlet 的工作步驟如下

  • 瀏覽器發送請求到服務器:http://localhost:8080/工程名/類名(資源名)
  • 連接服務器:tomcat
  • 發送頭文件 request 請求文件
  • 服務器解析出主機名或者主機地址(Dos解析)
  • 服務器訪問工程(即tomcat下的webapps文件下對應的工程)
  • 服務器訪問第5步中的工程下的資源(.class文件)
  • 利用反射創建 Servlet 對象,一個 Servlet 工程對應只有一個 Servlet 對象
  • 執行 init() 方法初始化 Servlet 對象
  • 執行 service() 方法,同時創建 request 對象和 response 對象
  • 服務器響應(response)

如果需要在發送請求之前就連接上數據庫,即一旦tomcat啓動就執行init()方法,則只需要在<servlet>中配置優先級<load-on-startup>1</load-on-startup>

3、開發Java EE工程步驟

以Eclipse開發工具爲例,示例創建一個Java EE工程項目
(1)創建一個Dybnamic web project 工程

(2)點擊下一步,在最後一步勾上生成web.xml文件

(3)在Eclipse中搭建tomcat服務器環境
window --> Preferences --> Server --> Runtime Environment --> 選擇右邊的add按鈕,然後找到Apache(8.0) --> 選擇文件夾 --> Finish

(4)將服務器的一欄的jar包,添加到工程中
工程上右鍵 --> 選擇builder path --> config… --> Libraries --> 選擇添加add Lib… --> 選擇Server Runtime…

(5)創建一個類,繼承GenericServlet,並重寫servers方法
response.getOutputStream().write("hello world".getBytes());

(6)配置web.xml文件

//添加Server類的映射
<servlet>
   <servlet-name>類名</servlet-name>
   <servlet-class>完整類名</servlet-class>	//完整類名 = 包名 + 類名
</servlet>

//配置Servlet的訪問請求映射
<servlet-mapping>
   <servlet-name>類名</servlet-name>
   <url-pattern>/類名</url-pattern>
</servlet-mapping>
  • 配置servlet,每一個servlet都需要配置一個,其中<servlet-name>類名</servlet-name> //的類名可以自定義,但必須和<servlet-mapping>中的<servlet-name>名字一致,建議使用類名。
  • 配置servlet下面的映射,能夠識別瀏覽器的訪問,其中<url-pattern>/類名</url-pattern>中的類名可以自定義,但是要與在瀏覽- 器訪問時輸入的請求名一致,還可以用通配符*.後綴
  • 在瀏覽器訪問時輸入的url:http://localhost:8080/工程名/請求名
    • http:表示http超文本傳輸協議
    • localhost:表示主機名,本地tomcat
    • 8080:tomcat的訪問端口,可以再servlet.xml文件中配置
    • 工程名:寫工程名是因爲在訪問時需要訪問到tomcat下的webapps文件夾下存放的對應的工程名
    • 請求名:和web.xml文件中的<url-pattern>中配置的參數一致

(7)將工程放置到tomcat服務器中
單擊服務器,選擇 add and remove --> 將自己想要添加到服務器的工程添加進去即可

(8)啓動服務器,沒有錯誤之後,在瀏覽器中打開訪問:http://localhost:8080/工程名/類名

4、Servlet生命週期

(1)Servlet 的生命週期:從 Servlet 被創建到 Servlet 被銷燬的過程,一次創建,到處服務,一個 Servlet 只會有一個對象,服務所有的請求。

  • 創建對象:使用構造方法創建對象(實例化)
  • 初始化:執行 init() 方法
  • 服務:執行 service() 方法
  • 銷燬:執行 destroy() 方法

(2)如果 Servlet 在 web.xml 中配置了 load-on-startup,聲明週期爲從服務器啓動到服務器關閉。

<load-on-startup>1</load-on-startup>

二、創建 Servlet 項目的三種方式

1、方式一:實現 Servlet 接口

(1)因爲是實現 Servlet 接口,所以需要實現接口裏的方法。

(2)示例:

public class ServletDemo1 implements Servlet {
    //public ServletDemo1(){}
    
     //生命週期方法:當Servlet第一次被創建對象時執行該方法,該方法在整個生命週期中只執行一次
    public void init(ServletConfig arg0) throws ServletException {
		System.out.println("執行int方法......");
	}

    //生命週期方法:對客戶端響應的方法,該方法會被執行多次,每次請求該servlet都會執行該方法
    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        System.out.println("hehe");
    }

    //生命週期方法:當Servlet被銷燬時執行該方法
    public void destroy() {
        System.out.println("當Servlet被銷燬時執行該方法");
    }
//當停止tomcat時也就銷燬的servlet。
    public ServletConfig getServletConfig() {
        return null;
    }

    public String getServletInfo() {
        return null;
    }
}

2、繼承 GenericServlet 類

(1)它實現了 Servlet 接口並重寫了 service 的方法,不過這種方法極少用。

(2)示例:

public class ServletDemo2 extends GenericServlet {
    @Override
   public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		response.getOutputStream().write("hello world".getBytes());
	}
}

3、繼承 HttpServlet 方法

(1)這種方式在開發中比較常用。

@WebServlet("/ServletDemo2")		//使用註解,可以不用配置xml文件
public class ServletDemo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().append("Served at: ").append(request.getContextPath());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

繼承 HttpServlet 也可以重寫service方法,但是傳入的參數必須是HttpServletRequest requestHttpServletResponse response。這種重寫的service方法會主動調用doGet()方法和doPost()方法。

4、service()、doGet()、doPost() 區別

  • service():可以處理 doGet() / doPost(),如果 Sertlvet 中有 service() 方法,會優先調用 service() 方法;
  • doGet():在沒有 service() 方法的情況下處理 doGet() 方式的請求;
  • doPost():在沒有 service() 方法的情況下處理 doPost() 方式的請求。
  • 如果要重寫的 service() 方法中調用了父類的 service() 方法(super, service(arg0, arg1)),則 service() 方法處理後,會再次根據請求方式響應 doGet() / doPost() 方法,而此時如果沒有重寫 doGet() / doPost() 方法的話,就會報405的錯誤,所以一般不再重寫的 service() 方法中調用父類的 service() 方法。

5、字節和字符的亂碼問題

(1)字節亂碼問題

@WebServlet("/ServletDemo3")
public class ServletDemo extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String str = "你好";
		//字節處理亂碼
		//修改瀏覽器響應頭中的content-type的值,即修改編碼爲utf-8
		//第一種
//		response.setHeader("content-type", "text/html;charset=utf-8");
		//第二種
		response.setContentType("text/html;charset=utf-8");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(str.getBytes());
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

(2)字符亂碼問題

@WebServlet("/ServletDemo3")
public class ServletDemo extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	String str = "你好";
	//字符亂碼處理
	//修改瀏覽器響應頭中的content-type的值,即修改編碼爲utf-8
	//這種方式可以修改,但存在問題:有可能瀏覽器還是亂碼
	response.setHeader("content-type", "text/html;charset=utf-8");
	//所以還需要修改服務器編碼
	response.setCharacterEncoding("utf-8");
	PrintWriter writer = response.getWriter();
	writer.write(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

三、Servlet 的對象

1、Request 對象

(1)request 對象概述
服務器接收到瀏覽器的請求後,會創建一個 Request 對象,對象中存儲了此次請求相關的請求數據。服務器在調用 Servlet 時會將創建的 Request 對象作爲實參傳遞給 Servlet 的方法,如 service() 方法。

(2)request 對象使用

  • 獲取請求頭數據
request.getMethod();		//獲取請求方式
request.getRequestURL();		//獲取請求URL信息
request.getRequestURI();		//獲取請求URI信息
request.getScheme();		//獲取協議
  • 獲取請求行數據
request.getHeader("鍵名");		//返回指定的請求頭集合
request.getHeaderNames();		//返回請求頭的鍵名的枚舉集合
  • 獲取用戶數據
request.getParameter("鍵名");		//返回指定的用戶數據
request.getParameterValues("鍵名");		//返回同鍵不同值的請求數據(多選),返回數組
request.getParameterNames();		//返回所有用戶請求數據的枚舉集合

設置請求編碼格式

request.setCharacterEncoding("utf-8");

如果要獲取的請求數據不存在,不會報錯,而會返回 null。

(3)request 對象的作用域
使用請求轉發後,不同的 Servlet 之間的數據共享需要使用 request 對象的作用域。

request.setAttribute(Object name, Object value);
request.getAttribute(Object obj);

使用 request 對象進行數據共享的方式,數據只在一次請求內有效,解決了在一次請求內的不同Servlet的數據共享問題。

特點:
作用域由服務器創建,每次請求都會創建,並且生命週期只在這一次請求中。

2、Response 對象

(1)Response 對象概述
服務器在調用指定的 Servlet 進行請求處理的時候,會給 Servlet 的方法傳遞兩個實參 request 和 response。其中 request 中封存了請求相關的請求數據,response 則是用來進行響應,將數據響應到瀏覽器的一個對象,。

(2)Response 對象使用

  • 設置響應頭
response.setHeader(String name, String value);	//在響應頭中添加響應信息,但是同鍵會覆蓋
response.addHeader(String name, String value); //在響應頭中添加響應信息,但是同鍵不會覆蓋
  • 設置響應狀態
response.sendError(int num, String msg); 	//自定義響應狀態碼
  • 設置響應實體
response.getWriter().write(String str);		//響應具體的數據給瀏覽器
response.getWriter().print(String str); 		//響應具體的數據給瀏覽器

設置響應編碼格式

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

3、ServletContext 對象

(1)ServletContext 概述

  • Servlet 創建對象時,在加載 Servlet 容器的時候,會創建一個 ServletContext 容器,因爲是伴隨着 Servlet 對象的創建而創建,所以整個web工程裏面,只有一個 ServletContext。
  • 一個web工程裏面,會有很多的 servlet,但是多個 servlet 之間,共用一個 ServletContext 對象,可以實現 servlet 之間的通信,即 ServletContext 解決了不同用戶之間的數據共享。
  • ServletContext 由服務器創建,用戶共享。其生命週期爲服務器啓動到服務器關閉,作用域爲整個項目。
//發送端
ServletContext context = this.getServletContext();
context.setAttribute("名字",);
接收端
Object obj = context.getAttribute("名字");

(2)獲取 ServletContext 對象

  • 方式一:通過 ServletConfig 對象和 ServletContext() 對象可以獲取
ServletContext sc = this.getServletConfig().getServletContext();
  • 方式二:通過 this.ServletContext() 獲取,response 和 request 都可以獲取
ServletContext sc = this.getServletContext();
  • 方式三:通過 getSession() 獲取
ServletContext sc = request.getSession().getServletContext();`

(3)ServletContext 數據存儲和獲取

sc.setAttribute(String name, Object value);
sc.getAttribute(String name);

(4)ServletContext 全局配置數據

  • ServletContext 對象代表的是全局的參數變量,獲取的初始化參數不能夠配置在單個的 servlet 中。
<context-param>
	<param-name></param-name>
	<param-value></param-value>
</context-param>

注意

  • 如果是編碼集,需要通過配置文件來初始化。
  • 一組<context-param>標籤只能配置一個鍵值對,要配置多個可以聲明多個<context-param>

getInitParameter();方法:根據鍵的名字返回web.xml中配置的全局數據的值,返回String類型數據,如果數據不存在返回null;
getInitParameterNames();方法:返回鍵名的枚舉

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext text = this.getServletContext();	
		String name = text.getInitParameter("name");
		response.getOutputStream().write(name.getBytes());
		Enumeration<String> e = text.getInitParameterNames();
		while(e.hasMoreElements()) {
			System.out.println(e.nextElement());
		}
	}
}

(5)ServletContext 對象的請求轉發
① html文件

<body>
    <form action="Demo2" method="post">
	          用戶名:<input type = "text" name = "userName" />
	          密碼:<input type = "password" name = "passWord" />
	    <input type = "submit" >
    </form>
</body>

② 請求轉發

@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//測試是否執行1
		response.getOutputStream().write("是否執行1".getBytes());
		
		//請求轉發
		RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/Demo3");
		rd.forward(request, response);
		
		//測試是否執行1
		response.getOutputStream().write("是否執行1".getBytes());
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

請求轉發之前和之後的代碼依舊可以執行,執行的結果都不會再頁面顯示,前面的沒有顯示是因爲 response 在執行的時候只是先把結果存放在內存中,等待所有內容執行完畢後纔會顯示在頁面,但是在請求轉發的時候,會清空 response 的所有數據,所以 response 的結果會被清空。

後面的執行是因爲 doGet() 方法並沒有結束,當 doGet() 執行完轉發請求rd.forward(request, response);後會回到方法中執行其後的代碼,但是執行的結果不會再頁面上是因爲在請求轉發之後頁面已經跳轉,並且不會跳轉回來,所以不會顯示。

③ 獲取輸入框信息

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//默認一個賬號和密碼
		String userName = "張三";
		String passWord = "1234";
		//獲取輸入的內容
		String name = request.getParameter("userName");
		String word = request.getParameter("passWord");
		//設置編碼
		response.setContentType("text/html;charset=utf-8");
		//判斷賬號密碼是否正確
		if(name.equals(userName) && word.equals(passWord)) {
			response.getOutputStream().write("成功".getBytes("utf-8"));
		}else {
			response.getOutputStream().write("錯誤".getBytes("utf-8"));
		}
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

4、ServletConfig 對象

(1)ServletConfig 對象是 Servlet 的專屬配置對象,每個 Servlet 都單獨擁有一個 ServletConfig 對象,用來獲取 wen.xml 中配置信息。

(2)配置一個 Servlet 的初始化參數,在<servlet>中配置中添加以下信息

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

(3)主要方法

  • getInitParameter()

① 獲取 ServletConfig 對象,第一種:在init()方法中

ServletConfig config = this.getServletConfig();
獲取參數
config.getInitParameter("userName");

② 獲取 ServletConfig 對象,第二種:在 init(ServletConfig config) 方法中

String userName = config.getInitParameter("userName");
  • getInitParameterNames()
ServletConfig config = this.getServletConfig();
Enumeration e = config.getInitParameterNames();

5、請求轉發和重定向

(1)請求轉發介紹
請求轉發能夠實現多個servlet聯動操作處理請求,這樣可以避免代碼冗餘,然servlet的職責更加明確

RequestDispather rd = request.getRequestDispacher(”要轉發的地址“);
rd.forward(request,response);

請求轉發特點:
請求轉發只發送一次請求,瀏覽器的地址欄信息不會發生改變。

(2)重定向 sendRedirect(“前端的url路徑”)
重定向能夠解決表單重複提交的問題,以及當前servlet無法處理的請求的問題
重定向示例:

response.sendRedirect("路徑");

status:300-399,表示請求成功,等待其他的請求響應,作用是實現頁面的跳轉。

重定向特點:
重定向有兩次請求,創建兩個request對象,並且瀏覽器的地址欄信息會發生改變。

重定向使用時機:

  • 如果請求中有表單數據,而數據又比較重要,不能重複提交,建議使用重定向。
  • 如果請求被Servlet接收後,無法進行處理,建議使用重定向定位到可以處理的資源。

(3) 重定向和請求轉發的區別

  • 重定向不會等攜帶數據,只做頁面跳轉,而請求轉發可以攜帶request對象的數據
  • 重定向頁面的請求地址會發生改變,而請求轉發不會修改地址欄
  • 重定向會發送兩次請求,而請求轉發只會請求一次,所以不建議頻繁使用重定向
  • 重定向不能訪問WEB-INF下面的html文件,請求轉發可以訪問
  • 重定向屬於客戶端的行爲,而請求轉發屬於服務器的行爲
  • 重定向解決了表單數據重複提交,以及當前 Servlet 無法處理的請求的問題,請求轉發會造成表單數據重複提交

四、Servlet 配置文件

1、web.xml 文件

(1)web.xml 文件作用
配置項目相關的配置信息,保護 Servlet ,解耦一些數據對程序的依賴。

(2)web.xml 的位置

  • 每個web項目中會有一個web.xml 文件,在tomcat 服務器中(服務器目錄conf目錄下)也有一個web.xml 文件。
  • 區別:
    • Web 項目下的 web.xml 文件爲局部配置,針對本項目的位置。
    • Tomcat 下的 web.xml 文件爲全局配置,配置公共信息。

(3)web.xml 配置內容

  • 全局上下文配置
  • Servlet 配置
  • 過濾器配置
  • 監聽器配置

加載順序:Web 容器會按照 ServletContext -> context-param -> listener -> filter -> servlet 的順序加載組件,這些元素可以配置在 web.xml 文件中的任意位置。

2、server.xml 文件

1、server.xml 文件的核心組件

<Server>
	<Service>
		<connector />	//配置端口號
		<connector />
		<Engine>		//一個<Service>便籤下只能有一個<Engine>標籤組
			<Host>
				<Context />
			</Host>
		</Engine>
	</Service>
</Server>

五、Servlet 代碼示例

1、WEB 讀取文件

(1)方式一:使用類加載器讀取配置文件

@WebServlet("/Demo1")
public class Demo1 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		InputStream is = Demo1.class.getClassLoader().getResourceAsStream("test1.properties");
		Properties prop = new Properties();
		prop.load(is);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

(2)方式二:通過 ServletContext 對象中的 getResourceAsStream();

@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletConfig config = this.getServletConfig();
		InputStream is = config.getServletContext().getResourceAsStream("/WEB-INF/classes/test1.properties");
		Properties prop = new Properties();
		prop.load(is);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

通過方式這種方式獲取配置文件需要注意的是:文件路徑已經改變,不在存在於src下面,而是存放在工程名下/WEB-INF/classes文件夾下

(3)方式三:getRealPath()

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext context = this.getServletContext();
		String path = context.getRealPath("/WEB-INF/classes/test1.properties");
		FileInputStream fis = new FileInputStream(path);
		Properties prop = new Properties();
		prop.load(fis);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

(4)方式四:將讀取文件放在 WebContent 或者 WebRoot 文件目錄下

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext context = this.getServletContext();
		InputStream is = context.getResourceAsStream("test1.properties");
		Properties prop = new Properties();
		prop.load(is);	
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

2、Servlet 文件下載

(1)Servlet 下載文件概述
包括字節讀取流、字節輸出流兩個步驟,但是有一個關鍵步驟:想要使網頁能夠下載文件,需要設置其中的一個頭文件信息。

response.setHeader("content-disposition", "attchment;filename=文件名");

(2)Servlet 下載文件示例

@WebServlet("/Demo4")
public class Demo4 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String path = this.getServletContext().getRealPath("/WEB-INF/classes/圖片.jpg");
		//截取文件名
		String newPath = path.substring(path.lastIndexOf("\\") + 1);
		//設置頭文件
		response.setHeader("content-disposition", "attchment;filename=" + URLEncoder.encode(newPath,"utf-8"));
		FileInputStream fis = new FileInputStream(path);
		ServletOutputStream out = response.getOutputStream();
		int len = -1;
		byte[] bytes = new byte[1024];
		while((len = fis.read(bytes)) != -1) {
			out.write(bytes, 0 , len);
		}
		out.close();
		fis.close();
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章