今天上班居然遲到了,昨天失眠了,看完吐槽大會實在不知道做些什么,剛好朋友給我發了兩個JavaWeb的練習項目,自己就又研究了下,三四點才睡,可能周日白天睡的太多了,早上醒來已經九點多了,立馬刷牙洗臉頭都沒洗打車到公司,到公司都十點半了,還好領導不錯沒有追究,謝謝老板謝謝陳工和同事們。下面開始今天正題。
上一篇博客介紹了Tomcat的工作流程以及Servlet的生命周期,偏重理論,今天這一篇博客介紹下Servlet的應用,偏重實驗,由於內容比較多,打算每天在家寫一點,爭取這周把這一篇完成。
一、編寫第一個Servlet
學習任何一門語言都是從HelloWorld開始,今天學習Servlet也是從HelloWorld開始。打開Eclipse,File->New->Dyanmic Web Project新建一個名為HelloWorld的項目。在src下新建一個Package,這里我命名為test。在包下新建一個命名為HelloWorld的Servlet.如下圖,左圖是新建右圖是新建完成的目錄結構。
在Servlet中寫入幾行簡單的程序,來運行一下整體感知一下。

package com.test.cyw; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; @WebServlet("/HelloWorld") public class HelloWorld extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>HelloWorld</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>HelloWorld</h2>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
在瀏覽器中輸入:http://localhost:8088/HelloWorld/HelloWorld,會顯示下面的頁面。
二、@WebServlet注解
假如輸入小寫的http://localhost:8088/HelloWorld/helloWorld,會出現錯誤的頁面,如下圖,這里可能會有疑問:為什么輸入大寫的就顯示正常而輸入小寫的就報錯呢?而且也沒有和上一篇博客寫的在web.xml中配置ServletName等這些參數也能運行。這是什么原因呢?
其實這主要是@WebServlet注解在起作用。Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中進行Servlet的部署描述,簡化開發流程。開發Servlet3的程序需要一定的環境支持。Servlet3是Java EE6規范的一部分,MyEclipse10和Tomcat7都提供了對Java EE6規范的支持。Tomcat需要Tomcat7才支持Java EE6,Tomcat7需要使用JDK6。關於注解以后有機會會專門寫一博客來研究它,這里主要是@WebServlet注解的使用。
使用@WebServlet將一個繼承於javax.servlet.http.HttpServlet的類定義為Servlet組件。
@WebServlet有很多的屬性:
asyncSupported:聲明Servlet是否支持異步操作模式。
description: Servlet的描述。
displayName: Servlet的顯示名稱。
initParams: Servlet的init參數。
name: Servlet的名稱。
urlPatterns: Servlet的訪問URL。
value: Servlet的訪問URL。
Servlet的訪問URL是Servlet的必選屬性,可以選擇使用urlPatterns或者value定義。
這里我們對上面的代碼稍作修改就可以實現大小寫都支持。

package com.test.cyw; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; /*@WebServlet("/HelloWorld")*/ @WebServlet(name = "HelloWorld", urlPatterns = {"/helloWorld","/HelloWorld"}) public class HelloWorld extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>HelloWorld</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>HelloWorld</h2>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
三、參數配置
1.Servlet的參數配置
上面使用@WebServlet注解來實現映射,不過要Servlet3.0以后才支持,在web.xml中配置是一比較常見的方式。還是以HelloWrold項目為demo,在web.xml中進行如下配置,也能達到@WebServlet注解的效果。配置在xml中的參數修改只需要重啟下服務器就好,不用再修改Servlet類.

<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>com.test.cyw.HelloWorld</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/helloWorld</url-pattern>
<url-pattern>/helloWorld.aspx</url-pattern>
</servlet-mapping>
<servlet></servlet>是Servlet配置的開始和結束標記。
<servlet-name>指定一個變量,主要是為了配置<servlet-mapping>時使用。
<servlet-class>指定Servlet對應的類。
<init-param></init-param>配置初始化參數開始結束標記。
<param-name>參數名,主要是為以后獲取提供name
<param-value>參數值,設置初始化參數的值
<servlet-mapping></servlet-mapping>Servlet映射關系配置開始結束標記,用戶輸入的url怎么找到對應的Servlet-Class就要靠它。
<servlet-name>HelloWorld</servlet-name>與上面<Servlet>配置的<servlet-name>一致。
<url-pattern>用戶輸入的url格式,可以使用通配符*或?。從javaee5開始可以配置多個,上面的代碼中添加了一個后綴名為aspx的,這樣也可以映射。
2.上下文的參數配置
由於<init-param>是用配置在Servlet的參數,只能供對應的一個Servlet使用。要想配置的參數是全局的可以供所有的Servlet使用,可以使用上下文參數。例如像控制用戶上傳圖片的類型,只允許用戶上傳jpg或png類型的圖片。可以在web.xml中做下面的配置。

<context-param>
<param-name>ImageType</param-name>
<param-value>.jpg,.png</param-value>
</context-param>
四、請求與響應
在編程中請求與響應很常見,不僅僅是JavaWeb,其他語言也都有。先不說JavaWeb的請求響應,先從字面理解下。
請求:誰請求誰?客戶端發起請求,客戶端請求服務端。
響應:誰響應誰? 服務端做出響應,服務端響應客戶端的請求。服務端是屬於被動的。
一般是用戶發起請求,服務端根據請求做出響應,發起是用戶的操作,編程一般不關心,根據請求做出對應的響應是編程需要做的。在Toamcat接到請求后,servlet 容器創建 HttpServletRequest、HttpServletResponse對象,並將該對象作為參數傳遞給該 servlet 的 service 方法.具體使用由於內容比較多這里就不一一列舉,可以下載api查看,在本博客中我也會附上鏈接,供大家下載。
五、web.xml參數讀取
上面配置了一些初始化的參數,配置參數就是為了讀取使用,如果不提供讀取的方法,配置參數基本沒啥用。
1.Servlet參數讀取
獲取Servlet初始化參數有兩種方法。一是直接調用Servlet的getInitParameter("name")二是通過getServletConfig()獲取ServletConfig再使用getInitParameter("name")方法獲取
2.上下文參數讀取
獲取全局上下文參數可以先通過getServletContext()獲取ServletContext對象,再使用getInitParameter("name")方法獲取。
3.資源注射
上面的獲取參數都是需要在Servlet中調用方法獲取參數,有沒有不編寫程序獲取的呢?答案是有。不然我也不會問這個。JavaEE5提供了一個解決方案叫做資源注射@Resource.不過只能配置java.lang包下的標准類型的數據。
下面的web.xml中分別配置Servlet初始化參數、全局上下文參數和注射參數。在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" id="WebApp_ID" version="3.1">
<display-name>HelloWorld</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>com.test.cyw.HelloWorld</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/helloWorld</url-pattern>
<url-pattern>/helloWorld.aspx</url-pattern>
</servlet-mapping>
<env-entry>
<env-entry-name>resourceStringA</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>hello,resourceStringA</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>resourceStringB</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>hello,resourceStringB</env-entry-value>
</env-entry>
<context-param>
<param-name>ImageType</param-name>
<param-value>.jpg,.png</param-value>
</context-param>
</web-app>

package com.test.cyw; import java.io.IOException; import java.io.PrintWriter; import javax.annotation.*; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; /*@WebServlet("/HelloWorld")*/
/*@WebServlet(name = "HelloWorld", urlPatterns = {"/helloWorld","/HelloWorld"})*/
public class HelloWorld extends HttpServlet { private @Resource(name="resourceStringA") String resourceString1; private @Resource(name="resourceStringB") String resourceString2; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { //直接獲取
String servletParam=this.getInitParameter("encoding"); //通過ServletConfig對象獲取 //servletParam=this.getServletConfig().getInitParameter("encoding"); //通過獲取ServletContext對象獲取
String contextParam=this.getServletContext().getInitParameter("ImageType"); out.println("<html>"); out.println("<head>"); out.println("<title>HelloWorld</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>HelloWorld</h2>"); out.println("<h2>ServletParam:"+servletParam+"</h2>"); out.println("<h2>ContextParam:"+contextParam+"</h2>"); out.println("<h2>ResourceString1:"+resourceString1+"</h2>"); out.println("<h2>ResourceString2:"+resourceString2+"</h2>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
六、Servlet生命周期
關於生命周期上一博客也寫的比較清楚,這里主要介紹下@PreDestroy和@PostConstruct。
JavaEE5增加了兩個影響生命周期的注解:@PreDestroy和@PostConstruct。被用來修飾非靜態void()類型的不能拋出異常聲明的方法。

public @PostConstruct void PostConstruct() { System.out.println("構造函數后ini前"); } @PreDestroy public void PreDestory() { System.out.println("destory后Servlet卸載前"); }
七、Servlet間跳轉
1.轉向
在WebContent目錄下新建一個test.jsp文件,在HelloWorld的Servlet中放入下面的代碼,會跳轉到test.jsp頁面。

RequestDispatcher dispatcher= request.getRequestDispatcher("/test.jsp"); dispatcher.forward(request, response);
2.重定向
轉向是通過request來實現的,那重定向是通過response來實現。

response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); response.setHeader("Location", "http://www.cnblogs.com/5ishare/");
3.自動刷新

response.setHeader("Refresh", "2000;URL=http://www.cnblogs.com/5ishare/");
八、Servlet線程安全
關於線程安全想必大家都練習過這樣一個多線程取錢的練習。多線程操作一個變量會導致數據異常,最好不要在Servlet中聲明全局變量。
九、遇到的問題
1.端口號問題
在創建完Servelt運行時不管是大寫的HelloWorld還是小寫的helloWorld,始終報錯,這個仔細檢查才發現是端口錯誤,由於自己在公司配置的環境Tomcat端口是8080,自己也習慣了直接輸的也是8080,導致出錯,自己查看了下本機Tomcat的端口號發現是8088,改過來之后能運行成功。