文章目錄
一、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 request
和HttpServletResponse 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);
}
}